So I'm doing a project where I am reading in a config file. The config file is just a list of string like "D 1 1", "C 2 2", etc. Now I haven't ever done a read/write in C# so I looked it up online expecting to find some sort of rendition of C/C++ .eof(). I couldn't find one.
So what I have is...
TextReader tr = new StreamReader("/mypath");
Of all the examples online of how I found to read to the end of a file the two examples that kept occurring were
while ((line = tr.ReadLine() != null)
or
while (tr.Peek() >= 0)
I noticed that StreamReader has a bool EndOfStream but no one was suggesting it which led me to believe something was wrong with that solution. I ended up trying it like this...
while (!(tr as StreamReader).EndOfStream)
and it seems to work just fine.
So I guess my question is would I experience issues with casting a TextReader as a StreamReader and checking EndOfStream?
One obvious downside is that it makes your code StreamReader specific. Given that you can easily write the code using just TextReader, why not do so? That way if you need to use a StringReader (or something similar) for unit tests etc, there won't be any difficulties.
Personally I always use the "read a line until it's null" approach - sometimes via an extension method so that I can use
foreach (string line in reader.EnumerateLines())
{
}
EnumerateLines would then be an extension method on TextReader using an iterator block. (This means you can also use it for LINQ etc easily.)
Or you could use ReadAllLines, to simplify your code:
http://msdn.microsoft.com/en-us/library/s2tte0y1.aspx
This way, you let .NET take care of all the EOF/EOL management, and you focus on your content.
No you wont experience any issue's. If you look at the implementation if EndToStream, you'll find that it just checks if there is still data in the buffer and if not, if it can read more data from the underlying stream:
public bool EndOfStream
{
get
{
if (this.stream == null)
{
__Error.ReaderClosed();
}
if (this.charPos < this.charLen)
{
return false;
}
int num = this.ReadBuffer();
return num == 0;
}
}
Ofcourse casting in your code like that makes it dependend on StreamReader being the actual type of your reader which isn't pretty to begin with.
Maybe read it all into a string and then parse it: StreamReader.ReadToEnd()
using (StreamReader sr = new StreamReader(path))
{
//This allows you to do one Read operation.
string contents = sr.ReadToEnd());
}
Well, StreamReader is a specialisation of TextReader, in the sense that StreamReader inherits from TextReader. So there shouldn't be a problem. :)
var arpStream = ExecuteCommandLine(cmd, arg);
arpStream.ReadLine(); // Read entries
while (!arpStream.EndOfStream)
{
var line1 = arpStream.ReadLine().Trim();
// TeststandInt.SendLogPrint(line, true);
}
Related
I am writing a program about job interview. Everything is working properly, except one thing. When I use an outside method TotalLines (where I have seperate StreamReader), it is working properly, but when I am calculating a number of totalLines in the program, I am receiving one question mark on the beginning of the first question. So it is like that:
?What is your name?
but in the text file from which I am reading, I have just - What is your name?
I have no idea why is that. Maybe it is problem with that I am returning StreamReader to beginning? I checked my encoding, everything, but nothing worked. Thanks for your help :)
PotentialEmployee potentialEmployee = new PotentialEmployee();
using (StreamReader InterviewQuestions = new StreamReader(text, Encoding.Unicode))
{
int totalLines = 0;
while (InterviewQuestions.ReadLine() != null)
{
totalLines++;
}
InterviewQuestions.DiscardBufferedData();
InterviewQuestions.BaseStream.Seek(0, SeekOrigin.Begin);
for (int numberOfQuestions = 0; numberOfQuestions < totalLines; numberOfQuestions++)
{
string question = InterviewQuestions.ReadLine();
Console.WriteLine(question);
string response = Console.ReadLine();
potentialEmployee.Responses.Add(question, response);
}
}
But when I have a TotalLines calculation in the outside method, the question mark does not show. Any ideas plase?
It's very likely that the file starts with a byte order mark (BOM) which is being ignored by the reader initially, but then not when you "rewind" the stream.
While you could create a new reader, or even just replace it after reading it, I think it would be better to just avoid reading the file twice to start with:
foreach (var question in File.ReadLines(text, Encoding.Unicode))
{
Console.WriteLine(question);
string response = Console.ReadLine();
potentialEmployee.Responses.Add(question, response);
}
That's shorter, simpler, more efficient code that also won't display the problem you asked about.
If you want to make sure you can read the whole file before asking any questions, that's easy too:
string[] questions = File.ReadAllLines(text, Encoding.Unicode);
foreach (var question in questions)
{
Console.WriteLine(question);
string response = Console.ReadLine();
potentialEmployee.Responses.Add(question, response);
}
Whenever you seek your stream to the beginning, the Byte Order Mark (BOM) is not read again, it's only done the first time after you create a stream reader with Encoding specified.
In order for the BOM to be read correctly again, you need to create a new stream reader. However, you can reuse the stream if you instruct the stream reader to keep the stream open after the reader is disposed, but be sure to seek before you create a new reader.
String s="aasddd??dsfas?df";
s.replace('?','\0');
Is there a way to take an IEnumerable<T> and emit it as a readable stream kinda like this?
private void DoTheThing(IEnumerable<Foo> foos)
{
using (var myStream = foos.EmitAsStream(f =>
{
var line = JsonConvert.SerializeObject(f);
return line;
}))
using(var streamReader = new StreamReader(myStream))
{
while (!streamReader.EndOfStream)
{
var line = streamReader.ReadLine();
Console.WriteLine(line);
}
}
}
Obviously, there are some problems with this, for example it seems to imply a StreamWriter with none specified, but the idea would be that the stream reader would just yield enumerate through the IEnumerable under the hood, apply the transform delegate, and pull the results without making any new, potentially large objects in memory.
I have some very large enumerable objects in memory, and I need to push them to external places (like Amazon S3) which accepts a stream. I really can't afford to build a MemoryStream with the collection and send that; I can spool to disk and read from disk, but I'd prefer not to if I have the option, since it seems like an extra step.
Is this possible? If it's possible, is it practical?
You can, achieve that by using TakeWhile, if I right understood your problem.
Something like :
while(..end ..)
{
var enumeration = foos.TakeWhile(..line bounds..);
var stream = StreamFromEnum(enumeration ); //custom implementation
// stream --> S3
}
I presume you have some custom definition of "line", which might be some sort of stride/slice of data from the stream.
A lazily-evaluated stream wrapper for IEnumerable is a one possible implementation of IEnumerable<T> -> Stream conversion.
The EnumerableToStream package will do exactly what you ask:
using EnumerableToStream;
using (var myStream = foos.Select(f =>
{
var line = JsonConvert.SerializeObject(f);
return line;
}).ToStream())
That ToStream() method is the one that does the magic. If you are interested in the implementation details, have a look at the source code.
I'm trying to get my program to read code from a .txt and then read it back to me, but for some reason, it crashes the program when I compile. Could someone let me know what I'm doing wrong? Thanks! :)
using System;
using System.IO;
public class Hello1
{
public static void Main()
{
string winDir=System.Environment.GetEnvironmentVariable("windir");
StreamReader reader=new StreamReader(winDir + "\\Name.txt");
try {
do {
Console.WriteLine(reader.ReadLine());
}
while(reader.Peek() != -1);
}
catch
{
Console.WriteLine("File is empty");
}
finally
{
reader.Close();
}
Console.ReadLine();
}
}
I don't like your solution for two simple reasons:
1)I don't like gotta Cath 'em all(try catch). For avoing check if the file exist using System.IO.File.Exist("YourPath")
2)Using this code you haven't dispose the streamreader. For avoing this is better use the using constructor like this: using(StreamReader sr=new StreamReader(path)){ //Your code}
Usage example:
string path="filePath";
if (System.IO.File.Exists(path))
using (System.IO.StreamReader sr = new System.IO.StreamReader(path))
{
while (sr.Peek() > -1)
Console.WriteLine(sr.ReadLine());
}
else
Console.WriteLine("The file not exist!");
If your file is located in the same folder as the .exe, all you need to do is StreamReader reader = new StreamReader("File.txt");
Otherwise, where File.txt is, put the full path to the file. Personally, I think it's easier if they are in the same location.
From there, it's as simple as Console.WriteLine(reader.ReadLine());
If you want to read all lines and display all at once, you could do a for loop:
for (int i = 0; i < lineAmount; i++)
{
Console.WriteLine(reader.ReadLine());
}
Use the code below if you want the result as a string instead of an array.
File.ReadAllText(Path.Combine(winDir, "Name.txt"));
Why not use System.IO.File.ReadAllLines(winDir + "\Name.txt")
If all you're trying to do is display this as output in the console, you could do that pretty compactly:
private static string winDir = Environment.GetEnvironmentVariable("windir");
static void Main(string[] args)
{
Console.Write(File.ReadAllText(Path.Combine(winDir, "Name.txt")));
Console.Read();
}
using(var fs = new FileStream(winDir + "\\Name.txt", FileMode.Open, FileAccess.Read))
{
using(var reader = new StreamReader(fs))
{
// your code
}
}
The .NET framework has a variety of ways to read a text file. Each have pros and cons... lets go through two.
The first, is one that many of the other answers are recommending:
String allTxt = File.ReadAllText(Path.Combine(winDir, "Name.txt"));
This will read the entire file into a single String. It will be quick and painless. It comes with a risk though... If the file is large enough, you may run out of memory. Even if you can store the entire thing into memory, it may be large enough that you will have paging, and will make your software run quite slowly. The next option addresses this.
The second solution allows you to work with one line at a time and not load the entire file into memory:
foreach(String line in File.ReadLines(Path.Combine(winDir, "Name.txt")))
// Do Work with the single line.
Console.WriteLine(line);
This solution may take a little longer for files because it's going to do work MORE OFTEN with the contents of the file... however, it will prevent awkward memory errors.
I tend to go with the second solution, but only because I'm paranoid about loading huge Strings into memory.
I'm having an issue where I'm writing the contents of several xml files to one file. When I run the program, the output is in the proper format, but the words are out of order. An example of this:
My string is "<s:AttributeType name=\"Shift\" number=\"34\" nullable=\"true\" writeunknown=\"true\">"
So it should print <s:AttributeType name="Shift" number="34" nullable="true" writeunknown="true">
But instead <s:AttributeType name="Shift" writeunknown="true" number="34" nullable="true">
is returned.
Some of the file is written in using File.WriteAllText(#"C:\Users\status.xml", xsh);
Where 'xsh' is a variable containing a string.
The rest is written in using this loop:
foreach (var i in Numbers.GetWSnumber())
{
string contents = "";
string curFile = #"\\production\public\Staus\TStatus\WS" + i.SetId + ".xml";
if (File.Exists(curFile))
{
System.IO.StreamReader file = new System.IO.StreamReader(curFile);
while ((contents = file.ReadLine()) != null)
{
using (StreamWriter sw = File.AppendText(#"C:\Users\status.xml"))
{
sw.WriteLine(contents);
}
}
file.Close();
}
}
Any help is appreciated
The order of XML attributes is not important so I wouldn't worry about it. However, if it's really bugging you I would suggest moving your using statement.
using (StreamWriter sw = File.AppendText(#"C:\Users\status.xml"))
{
sw.WriteLine(contents);
}
Although this is probably optimized so that it works the same as if it were better written, the way you currently have this a new StreamWriter is being allocated and then disposed of with every iteration.
Your using statement should wrap the while loop and not the other way around. This could possibly solve the problem (though I think it's unlikely) as I don't know how the compiler is handling this line. Either way, it's worth changing.
I am sending mails (in asp.net ,c#), having a template in text file (.txt) like below
User Name :<User Name>
Address : <Address>.
I used to replace the words within the angle brackets in the text file using the below code
StreamReader sr;
sr = File.OpenText(HttpContext.Current.Server.MapPath(txt));
copy = sr.ReadToEnd();
sr.Close(); //close the reader
copy = copy.Replace(word.ToUpper(),"#" + word.ToUpper()); //remove the word specified UC
//save new copy into existing text file
FileInfo newText = new FileInfo(HttpContext.Current.Server.MapPath(txt));
StreamWriter newCopy = newText.CreateText();
newCopy.WriteLine(copy);
newCopy.Write(newCopy.NewLine);
newCopy.Close();
Now I have a new problem,
the user will be adding new words within an angle, say for eg, they will be adding <Salary>.
In that case i have to read out and find the word <Salary>.
In other words, I have to find all the words, that are located with the angle brackets (<>).
How do I do that?
Having a stream for your file, you can build something similar to a typical tokenizer.
In general terms, this works as a finite state machine: you need an enumeration for the states (in this case could be simplified down to a boolean, but I'll give you the general approach so you can reuse it on similar tasks); and a function implementing the logic. C#'s iterators are quite a fit for this problem, so I'll be using them on the snippet below. Your function will take the stream as an argument, will use an enumerated value and a char buffer internally, and will yield the strings one by one. You'll need this near the start of your code file:
using System.Collections.Generic;
using System.IO;
using System.Text;
And then, inside your class, something like this:
enum States {
OUT,
IN,
}
IEnumerable<string> GetStrings(TextReader reader) {
States state=States.OUT;
StringBuilder buffer;
int ch;
while((ch=reader.Read())>=0) {
switch(state) {
case States.OUT:
if(ch=='<') {
state=States.IN;
buffer=new StringBuilder();
}
break;
case States.IN:
if(ch=='>') {
state=States.OUT;
yield return buffer.ToString();
} else {
buffer.Append(Char.ConvertFromUtf32(ch));
}
break;
}
}
}
The finite-state machine model always has the same layout: while(READ_INPUT) { switch(STATE) {...}}: inside each case of the switch, you may be producing output and/or altering the state. Beyond that, the algorithm is defined in terms of states and state changes: for any given state and input combination, there is an exact new state and output combination (the output can be "nothing" on those states that trigger no output; and the state may be the same old state if no state change is triggered).
Hope this helps.
EDIT: forgot to mention a couple of things:
1) You get a TextReader to pass to the function by creating a StreamReader for a file, or a StringReader if you already have the file on a string.
2) The memory and time costs of this approach are O(n), with n being the length of the file. They seem quite reasonable for this kind of task.
Using regex.
var matches = Regex.Matches(text, "<(.*?)>");
List<string> words = new List<string>();
for (int i = 0; i < matches.Count; i++)
{
words.Add(matches[i].Groups[1].Value);
}
Of course, this assumes you already have the file's text in a variable. Since you have to read the entire file to achieve that, you could look for the words as you are reading the stream, but I don't know what the performance trade off would be.
This is not an answer, but comments can't do this:
You should place some of your objects into using blocks. Something like this:
using(StreamReader sr = File.OpenText(HttpContext.Current.Server.MapPath(txt)))
{
copy = sr.ReadToEnd();
} // reader is closed by the end of the using block
//remove the word specified UC
copy = copy.Replace(word.ToUpper(), "#" + word.ToUpper());
//save new copy into existing text file
FileInfo newText = new FileInfo(HttpContext.Current.Server.MapPath(txt));
using(var newCopy = newText.CreateText())
{
newCopy.WriteLine(copy);
newCopy.Write(newCopy.NewLine);
}
The using block ensures that resources are cleaned up even if an exception is thrown.