text file: Reading line by line C# - c#

So, let's say i have a text file with 20 lines, with on each line different text.
i want to be able to have a string that has the first line in it, but when i do NextLine(); i want it to be the next line. I tried this but it doesn't seem to work:
string CurrentLine;
int LastLineNumber;
Void NextLine()
{
System.IO.StreamReader file = new System.IO.StreamReader("c:\\test.txt");
CurrentLine = file.ReadLine(LastLineNumber + 1);
LastLineNumber++;
}
How would i be able to do this?
Thanks in advance.

In general, it would be better if you could design this in a way to leave your file open, and not try to reopen the file each time.
If that is not practical, you'll need to call ReadLine multiple times:
string CurrentLine;
int LastLineNumber;
void NextLine()
{
// using will make sure the file is closed
using(System.IO.StreamReader file = new System.IO.StreamReader("c:\\test.txt"))
{
// Skip lines
for (int i=0;i<LastLineNumber;++i)
file.ReadLine();
// Store your line
CurrentLine = file.ReadLine();
LastLineNumber++;
}
}
Note that this can be simplified via File.ReadLines:
void NextLine()
{
var lines = File.ReadLines("C:\\test.txt");
CurrentLine = lines.Skip(LastLineNumber).First();
LastLineNumber++;
}

One simple call should do it:
var fileLines = System.IO.File.ReadAllLines(fileName);
You will want to validate the file exists and of course you still need to watch for blank lines or invalid values but that should give you the basics. To loop over the file you can use the following:
foreach (var singleLine in fileLines) {
// process "singleLine" here
}
One more note - you won't want to do this with large files since it processes everything in memory.

Well, if you really don't mind re-opening the file each time, you can use:
CurrentLine = File.ReadLines("c:\\test.txt").Skip(LastLineNumber).First();
LastLineNumber++;
However, I'd advise you to just read the whole thing in one go using File.ReadAllLines, or perhaps File.ReadLines(...).ToList().

The ReadLine method already reads the next line in the StreamReader, you don't need the counter, or your custom function for that matter. Just keep reading until you reach your 20 lines or until the file ends.

You can't pass a line number to ReadLine and expect it to find that particular line. If you look at the ReadLine documentation, you'll see it doesn't accept any parameters.
public override string ReadLine()
When working with files, you must treat them as streams of data. Every time you open the file, you start at the very first byte/character of the file.
var reader = new StreamReader("c:\\test.txt"); // Starts at byte/character 0
You have to keep the stream open if you want to read more lines.
using (var reader = new StreamReader("c:\\test.txt"))
{
string line1 = reader.ReadLine();
string line2 = reader.ReadLine();
string line3 = reader.ReadLine();
// etc..
}
If you really want to write a method NextLine, then you need to store the created StreamReader object somewhere and use that every time. Somewhat like this:
public class MyClass : IDisposable
{
StreamReader reader;
public MyClass(string path)
{
this.reader = new StreamReader(path);
}
public string NextLine()
{
return this.reader.ReadLine();
}
public void Dispose()
{
reader.Dispose();
}
}
But I suggest you either loop through the stream:
using (var reader = new StreamReader("c:\\test.txt"))
{
while (some_condition)
{
string line = reader.ReadLine();
// Do something
}
}
Or get all the lines at once using the File class ReadAllLines method:
string[] lines = System.IO.File.ReadAllLines("c:\\test.txt");
for (int i = 0; i < lines.Length; i++)
{
string line = lines[i];
// Do something
}

Related

StreamReader.ReadLine will hang in an infinite loop

I have a simple program to read a file using the StreamReader and process it line by line. But the file I am reading may sometimes locate in a network folder. I came across while doing some testing with such a file, that if the network connection lost at some point while I am reading, it'll stay in the same line again and again looping in an infinite loop by resulting the same line as the result from stream.ReadLine().
Is there a way I can find when the fileHandle is not available from the stream itself? I was expecting a FileNotAvailableException kind of an exception would fire when the filehandle is lost from the StreamReader.
Here's my code snippet...
string file = #"Z://1601120903.csv"; //Network file
string line;
StringBuilder stb = new StringBuilder();
StreamReader stream = new StreamReader(file, Encoding.UTF8, true, 1048576);
do
{
line = stream.ReadLine();
// Do some work here
} while (line != "");
Compare with null not with empty string:
https://msdn.microsoft.com/en-us/library/system.io.streamreader.readline(v=vs.110).aspx
Return Value Type: System.String The next line from the input stream,
or null if the end of the input stream is reached.
do
{
line = stream.ReadLine();
// Do some work here
} while (line != null);
A better approach, however, is to let .Net do the work (line by line file reading) for you and drop all readers:
foreach (String line in File.ReadLines(file)) {
// Do some work here
}
Correct approach 1 (EndOfStream) :
using(StreamReader sr = new StreamReader(...)) {
while(!sr.EndOfStream) {
string line = sr.ReadLine();
Console.WriteLine(line);
}
}
Correct approach 2 (Peek)
using(StreamReader sr = new StreamReader(...)) {
while(sr.Peek() >= 0) {
string line = sr.ReadLine();
}
}
Note: that it is incorrect to threat an empty string as end of file.
if the network connection lost at some point while I am reading,
it'll stay in the same line again and again looping in an infinite
loop by resulting the same line as the result from stream.ReadLine()
I've checked this scenario right now - the System.IO.IOException ("The network path was not found."} should be thrown in this case.
Wrapping this with a try catch block will not fix my problem, will it?
In this case you can break the reading as follows:
string line;
do {
try {
line = sr.ReadLine();
// Do some work here
}
catch(System.IO.IOException) {
break;
}
} while(line != null);
If you write it with a while-loop:
while ((line = sr.ReadLine()) != null)
{
Console.WriteLine(line);
}
Source
One more way would be to use File.ReadAllLines() and it will take care of opening file and reading all lines and closig the file and may also handle scenario when network connection is lost.
var lines = File.ReadAllLines("Z://1601120903.csv");
foreach(line in lines)
{
// Do some work
}
Assuming the file shouldn't change while you reading it and it's not huge, you might want to consider to copy it to a temp file (locally) and then work on it without interference.
If you want to get index of the place you reached this might help:
How to know position(linenumber) of a streamreader in a textfile?
If your stream is a NetworkStream, the ReadLine method will expect more content from the stream, if it reached at end, indefinitely. I think, and according to the StreamReader documentation, it is designed to work only with local file streams. In this case, you can read bytes directly from the NetworkStream.
https://learn.microsoft.com/pt-br/dotnet/api/system.net.sockets.networkstream.read?view=netcore-3.1#System_Net_Sockets_NetworkStream_Read_System_Span_System_Byte__

Attempting to create string from a file

Currently trying to create a string from a text file, however their seems to be an error preventing the stream reader from reading the text file correctly.
private string testString = "Cheese";
private void openToolStripMenuItem_Click(object sender, EventArgs e)
{
if (openFileDialog.ShowDialog() != DialogResult.Cancel)
{
fileName = openFileDialog.FileName;
LoadFile();
}
}
private void LoadFile()
{
String lineFromFile = "Chicken";
*StringBuilder RawFileInput = new StringBuilder();
using (StreamReader reader = new StreamReader(fileName))
{
while ((lineFromFile = reader.ReadLine()) != null)
{
RawFileInput.AppendLine(lineFromFile);
}
}*
testString = lineFromFile;
testTB.Text = testString;
}
The output should the code execute has the output textbox be empty, however should the block of code between the asterisks be commented out, the output textbox obviously displays the test phrase of Chicken. As such I'm pretty sure there is a problem with this particular block, however I can't seem to figure out what.
Thanks in advance.
If I understood well your code, you are trying to set the testTB.Text with the text in your file. Taking that in account, shouldn't your last lines be:
testString = RawFileInput.ToString();
testTB.Text = testString;
You can achieve the same result with no need of a StringBuilder, replacing your whole LoadFile method with this line:
testTB.Text = File.ReadAllText(fileName);
You should be able to read a document in entirety, like the following:
var builder = new StringBuilder();
using(var reader = new StreamReader(path))
builder.Append(reader.ReadToEnd());
That would be the ideal, as it is more performant than ReadAllText.
ReadToEnd works best when you need to read all the input from the
current position to the end of the stream. If more control is needed
over how many characters are read from the stream, use the
Read(Char[], Int32, Int32) method overload, which generally results in
better performance. ReadToEnd assumes that the stream knows when it
has reached an end. For interactive protocols in which the server
sends data only when you ask for it and does not close the connection,
ReadToEnd might block indefinitely because it does not reach an end,
and should be avoided.
If you're wanting the contents of a file to populate a textbox, just set the Multiline property to true, and use File.ReadAllLines()
testTb.Lines = File.ReadAllLines(fileName);

Removing the first line of a text file in C#

I can currently remove the last line of a text file using:
var lines = System.IO.File.ReadAllLines("test.txt");
System.IO.File.WriteAllLines("test.txt", lines.Take(lines.Length - 1).ToArray());
Although, how is it possible to instead remove the beginning of the text file?
Instead of lines.Take, you can use lines.Skip, like:
var lines = File.ReadAllLines("test.txt");
File.WriteAllLines("test.txt", lines.Skip(1).ToArray());
to truncate at the beginning despite the fact that the technique used (read all text and write everything back) is very inefficient.
About the efficient way: The inefficiency comes from the necessity to read the whole file into memory. The other way around could easily be to seek in a stream and copy the stream to another output file, delete the original, and rename the old. That one would be equally fast and yet consume much less memory.
Truncating a file at the end is much easier. You can just find the trunaction position and call FileStream.SetLength().
Here is an alternative:
using (var stream = File.OpenRead("C:\\yourfile"))
{
var items = new LinkedList<string>();
using (var reader = new StreamReader(stream))
{
reader.ReadLine(); // skip one line
string line;
while ((line = reader.ReadLine()) != null)
{
//it's far better to do the actual processing here
items.AddLast(line);
}
}
}
Update
If you need an IEnumerable<string> and don't want to waste memory you could do something like this:
public static IEnumerable<string> GetFileLines(string filename)
{
using (var stream = File.OpenRead(filename))
{
using (var reader = new StreamReader(stream))
{
reader.ReadLine(); // skip one line
string line;
while ((line = reader.ReadLine()) != null)
{
yield return line;
}
}
}
}
static void Main(string[] args)
{
foreach (var line in GetFileLines("C:\\yourfile.txt"))
{
// do something with the line here.
}
}
var lines = System.IO.File.ReadAllLines("test.txt");
System.IO.File.WriteAllLines("test.txt", lines.Skip(1).ToArray());
Skip eliminates the given number of elements from the beginning of the sequence. Take eliminates all but the given number of elements from the end of the sequence.
To remove fist line from a text file
System.IO.StreamReader file = new System.IO.StreamReader(filePath);
string data = file.ReadToEnd();
file.Close();
data = Regex.Replace(data, "<.*\n", "");
System.IO.StreamWriter file = new System.IO.StreamWriter(filePath, false);
file.Write(data);
file.Close();
can do in one line also
File.WriteAllLines(origialFilePath,File.ReadAllLines(originalFilePath).Skip(1));
Assuming you are passing your filePath as parameter to the function.

Best way to write huge string into a file

In C#, I'm reading a moderate size of file (100 KB ~ 1 MB), modifying some parts of the content, and finally writing to a different file. All contents are text. Modification is done as string objects and string operations. My current approach is:
Read each line from the original file by using StreamReader.
Open a StringBuilder for the contents of the new file.
Modify the string object and call AppendLine of the StringBuilder (until the end of the file)
Open a new StreamWriter, and write the StringBuilder to the write stream.
However, I've found that StremWriter.Write truncates 32768 bytes (2^16), but the length of StringBuilder is greater than that. I could write a simple loop to guarantee entire string to a file. But, I'm wondering what would be the most efficient way in C# for doing this task?
To summarize, I'd like to modify only some parts of a text file and write to a different file. But, the text file size could be larger than 32768 bytes.
== Answer == I'm sorry to make confusin to you! It was just I didn't call flush. StremWriter.Write does not have a short (e.g., 2^16) limitation.
StreamWriter.Write
does not
truncate the string and has no limitation.
Internally it uses String.CopyTo which on the other hand uses unsafe code (using fixed) to copy chars so it is the most efficient.
The problem is most likely related to not closing the writer. See http://msdn.microsoft.com/en-us/library/system.io.streamwriter.flush.aspx.
But I would suggest not loading the whole file in memory if that can be avoided.
can you try this :
void Test()
{
using (var inputFile = File.OpenText(#"c:\in.txt"))
{
using (var outputFile = File.CreateText(#"c:\out.txt"))
{
string current;
while ((current = inputFile.ReadLine()) != null)
{
outputFile.WriteLine(Process(current));
}
}
}
}
string Process(string current)
{
return current.ToLower();
}
It avoid to have to full file loaded in memory, by processing line by line and writing it directly
Well, that entirely depends on what you want to modify. If your modifications of one part of the text file are dependent on another part of the text file, you obviously need to have both of those parts in memory. If however, you only need to modify the text file on a line-by-line basis then use something like this :
using (StreamReader sr = new StreamReader(#"test.txt"))
{
using (StreamWriter sw = new StreamWriter(#"modifiedtest.txt"))
{
while (!sr.EndOfStream)
{
string line = sr.ReadLine();
//do some modifications
sw.WriteLine(line);
sw.Flush(); //force line to be written to disk
}
}
}
Instead of of running though the hole dokument i would use a regex to find what you are looking for Sample:
public List<string> GetAllProfiles()
{
List<string> profileNames = new List<string>();
using (StreamReader reader = new StreamReader(_folderLocation + "profiles.pg"))
{
string profiles = reader.ReadToEnd();
var regex = new Regex("\nname=([^\r]{0,})", RegexOptions.IgnoreCase);
var regexMatchs = regex.Matches(profiles);
profileNames.AddRange(from Match regexMatch in regexMatchs select regexMatch.Groups[1].Value);
}
return profileNames;
}

Parse text file with LINQ

I know normally you would use the File.ReadAllLines, but I'm trying to do it with an uploaded file.
Can I somehow put it into a temporary location?, or read it from memory?
I was able to get this working
Is this a string, a Stream, or what? either way, you want a TextReader - the question is simply StringReader vs StreamReader. Once you have that, I would do something like:
public static IEnumerable<string> ReadLines(TextReader reader) {
string line;
while((line = reader.ReadLine()) != null) yield return line;
}
then with whichever reader, I can either user:
foreach(var line in ReadLines(reader)) {
// note: non-buffered - i.e. more memory-efficient
}
or:
string[] lines = ReadLines(reader).ToArray();
// note: buffered - all read into memory at once (less memory efficient)
i.e. if it is a Stream you are reading from:
using(var reader = new StreamReader(inputStream)) {
foreach(var line in ReadLines(reader)) {
// do something fun and interesting
}
}

Categories

Resources