I'm making a console application and I need to be able to write, search and delete entries from within text files, I can write files into a notepad file but that's basically it, this is what I have to read the file:
public static void SearchDetails()
{
Console.WriteLine("Enter ID Number");
string myfile = System.IO.File.ReadAllText(#"C:\\file.txt");
System.Console.WriteLine(myfile);
}
This brings up all the text in the file but I need to be able to search for a specific number within the file and then brings up the next three lines below it. How to I get it to read the input so that it matches with a number in the text file and then bring up the next 3 lines?
So you can do whatever you want with the results but something like this is what you want
// Read the file and display next three lines
System.IO.StreamReader file = new System.IO.StreamReader("c:\\file.txt");
string line;
int lineCnt = 3;
while((line = file.ReadLine()) != null)
{
if (line.Contains(myID) & !bGet)
{
bool bGet = true;
}
if (bGet && lineCnt > 0)
{
bool bGet = true;
Console.WriteLine (line);
lineCnt--;
}
if(lineCnt == 0) {break;}
}
file.Close();
// Suspend the screen.
Console.ReadLine();
Considering you question and comments to answer, the final answer would be:
public static void SearchDetails()
{
Console.WriteLine("Enter ID Number");
int Id = 0;
int.TryParse(Console.ReadLine(), out Id);
string[] lines = System.IO.File.ReadAllLines(#"C:\\file.txt");
List<string> IdLines = lines.Where((x, i) => i % 4 == 0).ToList();
int IdLine = IdLines.IndexOf(Id);
if (IdLine != -1)
{ //then result found
//Id is what user searched for or
// string Id = lines[IdLine*4];
//string[] results = lines.Where((x, i) => i > IdLine * 4 && i < IdLine * 4 + 4).ToArray();
for(int i=IdLine*4;i<IdLine*4+4;i++)
System.Console.WriteLine(lines[i]);
}
else
{
Console.WriteLine("no results!");
}
}
I get this error. Later I searched and found out the reason of illegal characters in my XML and its solution. But I don't have the access to edit any of these files. My job is to read and fetch the tag value, attribute value and similar stuff. SO I can't replace the binary characters with escapes like '\x01' with . Also I tried to include CheckCharacters =false in XMLreader settings. It doesn't take this. Still it is throwing the same error.
Is it not possible to fix in XMLreader? I read about XMLtextReader. It can skip the exception. But already I have coded for all my features using XMLreader. It would be good if I can find a solution for this. Otherwise I would have to change all my code.
My code:
private void button1_Click(object sender, EventArgs e)
{
int i = 0;
var filenames = System.IO.Directory
.EnumerateFiles(textBox1.Text, "*.xml", System.IO.SearchOption.AllDirectories)
.Select(System.IO.Path.GetFullPath);
foreach (var f in filenames)
{
var resolver = new XmlUrlOverrideResolver();
resolver.DtdFileMap[#"X1.DTD"] = #"\\location\X1.DTD";
resolver.DtdFileMap[#"R2.DTD"] = #"\\location\X2.DTD";
resolver.DtdFileMap[#"R5.DTD"] = #"\\location\R5.DTD";
XmlReaderSettings settings = new XmlReaderSettings();
settings.DtdProcessing = DtdProcessing.Parse;
settings.XmlResolver = resolver;
XmlReader doc = XmlReader.Create(f, settings);
while (doc.Read())
{
if ((doc.NodeType == XmlNodeType.Element) && (doc.Name == "ap"))
{
if (doc.HasAttributes)
{
String fin = doc.GetAttribute("ap");
if (fin == "no")
{
String[] array = new String[10000];
array[i] = (f);
File.AppendAllText(#"\\location\NAPP.txt", array[i] + Environment.NewLine);
i++;
}
else
{
String[] abs = new String[10000];
abs[i] = (f);
File.AppendAllText(#"\\location\APP.txt", abs[i] + Environment.NewLine);
i++;
}
}
}
}
}
MessageBox.Show("Done");
}
This is a very simple example of character "filter" that will replae the 0x06 character with a space:
public class MyStreamReader : StreamReader {
public MyStreamReader(string path)
: base(path) {
}
public override int Read(char[] buffer, int index, int count) {
int res = base.Read(buffer, index, count);
for (int i = 0; i < res; i++) {
if (buffer[i] == 0x06) {
buffer[i] = ' ';
}
}
return res;
}
}
You use it this way:
using (var sr = new MyStreamReader(f)) {
var doc = XmlReader.Create(sr, settings);
Note that it's very simple because it's replacing a character (the 0x06) with another character of the same "length" (the space). If you wanted to replace a character with a "sequence" of characters (to escape it), it would get more complex (not impossible, 30 minutes of work difficult)
(I have checked and it seems the XmlTextReader only uses that method and not the Read() method)
As always, when a programmer tells you 30 minutes, it means 0 minutes or 2 hours :-)
This is the "more complex" ReplacingStreamReader:
/// <summary>
/// Only the Read methods are supported!
/// </summary>
public class ReplacingStreamReader : StreamReader
{
public ReplacingStreamReader(string path)
: base(path)
{
}
public Func<char, string> ReplaceWith { get; set; }
protected char[] RemainingChars { get; set; }
protected int RemainingCharsIndex { get; set; }
public override int Read()
{
int ch;
if (RemainingChars != null)
{
ch = RemainingChars[RemainingCharsIndex];
RemainingCharsIndex++;
if (RemainingCharsIndex == RemainingChars.Length)
{
RemainingCharsIndex = 0;
RemainingChars = null;
}
}
else
{
ch = base.Read();
if (ch != -1)
{
string replace = ReplaceWith((char)ch);
if (replace == null)
{
// Do nothing
}
else if (replace.Length == 1)
{
ch = replace[0];
}
else
{
ch = replace[0];
RemainingChars = replace.ToCharArray(1, replace.Length - 1);
RemainingCharsIndex = 0;
}
}
}
return ch;
}
public override int Read(char[] buffer, int index, int count)
{
int res = 0;
// We leave error handling to the StreamReader :-)
// We handle only "working" parameters
if (RemainingChars != null && buffer != null && index >= 0 && count > 0 && index + count <= buffer.Length)
{
int remainingCharsCount = RemainingChars.Length - RemainingCharsIndex;
res = Math.Min(remainingCharsCount, count);
Array.Copy(RemainingChars, RemainingCharsIndex, buffer, index, res);
RemainingCharsIndex += res;
if (RemainingCharsIndex == RemainingChars.Length)
{
RemainingCharsIndex = 0;
RemainingChars = null;
}
if (res == count)
{
return res;
}
index += res;
count -= res;
}
while (true)
{
List<char> sb = null;
int res2 = base.Read(buffer, index, count);
if (res2 == 0 || ReplaceWith == null)
{
return res;
}
int j = 0;
for (int i = 0; i < res2; i++)
{
char ch = buffer[index + i];
string replace = ReplaceWith(ch);
if (sb != null)
{
if (replace == null)
{
sb.Add(ch);
}
else
{
sb.AddRange(replace);
}
}
else if (replace == null)
{
buffer[j] = ch;
j++;
}
else if (replace.Length == 1)
{
buffer[j] = replace[0];
j++;
}
else if (replace.Length == 0)
{
// We do not advance
}
else
{
sb = new List<char>();
sb.AddRange(replace);
}
}
res2 = j;
if (sb != null)
{
int res3 = Math.Min(sb.Count, count - res2);
sb.CopyTo(0, buffer, index + res2, res3);
if (res3 < sb.Count)
{
RemainingChars = new char[sb.Count - res3];
RemainingCharsIndex = 0;
sb.CopyTo(res3, RemainingChars, 0, RemainingChars.Length);
}
res += res3;
}
else
{
res2 = j;
// Can't happen if sb != null (at least a character must
// have been added)
if (res2 == 0)
{
continue;
}
}
res += res2;
return res;
}
}
}
Use it like:
using (var sr = new ReplacingStreamReader(f))
{
sr.ReplaceWith = x =>
{
return x == 0x6 ? " " : null;
// return x == '.' ? " " : null; // Replace all . with
};
var doc = XmlReader.Create(sr, settings);
Be aware that the ReplacingStreamReader doesn't "know" which part of the xml it is modifying, so rarely a "blind" replace is ok :-) Other than this limitation, you can replace any character with any string (null in the ReplaceWith means "keep the current character", equivalent to x.ToString() in the example given. Returning string.Empty is valid, means remove the current character).
The class is quite interesting: it keeps a char[] RemainingChars with the chars that have been read (and filtered by ReplaceWith) but that haven't been returned by a Read() method because the passed buffer was too much small (the ReplaceWith method could "enlarge" the read string, making it too much big for the buffer!). Note that sb is a List<char> instead of a StringBuilder. Probably using one or the other would be nearly equivalent, code-wise.
You could first read the content into a string replace (escape) the content, and then load it into a XmlReader:
foreach (var f in filenames) {
string text;
using (StreamReader s = new StreamReader(f,Encoding.UTF8)) {
text = s.ReadToEnd();
}
text = text.Replace("\x01",#""); //replace the content
//load some settings
var resolver = new XmlUrlOverrideResolver();
resolver.DtdFileMap[#"X1.DTD"] = #"\\location\X1.DTD";
resolver.DtdFileMap[#"R2.DTD"] = #"\\location\X2.DTD";
resolver.DtdFileMap[#"R5.DTD"] = #"\\location\R5.DTD";
XmlReaderSettings settings = new XmlReaderSettings();
settings.DtdProcessing = DtdProcessing.Parse;
settings.XmlResolver = resolver;
XmlReader doc = XmlReader.Create(text, settings);
//perform processing task
//...
}
I'm reading a text file line by line in a While loop. When I reach a specific line I want to skip the the current and the next 3 iterations.
I guess I can do it using a counter and things like that. But I was wondering if there is a more elegant way?
using (var sr = new StreamReader(source))
{
string line;
while ((line = sr.ReadLine()) != null)
{
if (line == "Section 1-1")
{
// skip the next 3 iterations (lines)
}
}
}
Have a for loop to execute sr.ReadLine 3 times and discard the result like:
using (var sr = new StreamReader(source))
{
string line;
while ((line = sr.ReadLine()) != null)
{
if (line == "Section 1-1")
{
for (int i = 0; i < 3; i++)
{
sr.ReadLine();
}
}
}
}
You should check for sr.ReadLine returning null or if the stream has come to an end.
You can use File.ReadAllLines with method extesions:
public static IEnumerable<string> SkipLines(string file, string line, int count)
{
var enumerable = File.ReadLines(file).GetEnumerator();
while (enumerable.MoveNext())
{
var currentLine = enumerable.Current;
if (currentLine == line)
{
var currentCount = 0;
while(enumerable.MoveNext() && currentCount < count)
{
currentCount += 1;
}
}
yield return currentLine;
}
}
usage:
foreach (var line in SkipLines(source, "Section 1-1", 3))
{
// your line
}
Keep in mind: ReadLines is lazy - not all lines are loaded into memory at once.
Make yourself a function that discards a given number of lines (DiscardLines) and use it:
string line;
while ((line = sr.ReadLine()) != null)
{
if (line == "Section 1-1")
{
DiscardLines(sr, 3);
}
}
That keeps the main loop very simple. The counter is now hidden in DiscardLines.
using (var sr = new StreamReader(source))
{
string line;
int linesToSkip = 0;
while ((line = sr.ReadLine()) != null)
{
if (linesToSkip > 0)
{
linesToSkip -= 1;
continue;
}
if (line == "Section 1-1")
{
// skip the next 3 iterations (lines)
linesToSkip = 3;
}
}
}
I would like to replace lines of a text with the existing lines of the file based on some data. I have developed some code blocks but it didn't work.
My Text File is Like This:-
g_start-fd,g_start-cnst,g_start-eq,mv-mv_size,mv-mv_alloy,mv-mv_argmt,mv-mv_ps,xfrmr-kva,g_end-line_t,g_end-str_num,g_end-cmt,g_end-str_coord-Latitude,g_end-str_coord-Longitude
28F1Y,oh,mv oh,120,al,oh_3,P45R24,,i,P45R25,,9.53725695,-0.86668464
28F1Y,oh,mv oh,120,al,oh_3,P45R25,,i,P45R42,,9.5468355,-0.85948875
28F1Y,oh,mv oh,120,al,oh_3,P45R42,,i,P45R49,,9.55073989,-0.85625858
28F1Y,oh,mv oh,120,al,oh_3,P45R49,,a,P45R25,,,
28F1Y,oh,mv oh,120,al,oh_3,P45R54,,i,P45R55,,9.5544981,-0.85359626
28F1Y,oh,mv xfrmr,120,al,oh_3,P45R55,5000,e,P45R56,Substation,9.5549907,-0.85303108
28F1Y,ug,mv,185,al,xlpe_3,P45R56,,e,P45R55,,,
28F1Y,ug,mv,185,al,xlpe_3,P45R57,,s,P45R58,Take off from ring main,9.55387622,-0.8538622
28F1Y,oh,mv oh,120,al,oh_3,P45R58,,a,P45R73,,9.54513187,-0.86060037
28F1Y,oh,mv oh,120,al,oh_3,P45R73,,a,P45R77,,9.5417936,-0.86098952
28F1Y,oh,mv oh,120,al,oh_3,P45R77,,a,P45R80,,9.54144045,-0.85857346
28F1Y,oh,mv oh,120,al,oh_3,P45R80,,a,P45R86,,9.53675765,-0.85935176
28F1Y,oh,mv,120,al,oh_3,P45R86,,e,P45R80,,,
My Application stops working when I run this code:
string fileName1 = "D:\\WriteTextWork\\Line1.txt"; ;
OpenFileDialog pfdg = new OpenFileDialog();
if (pfdg.ShowDialog() == DialogResult.OK)
{
fileName1 = pfdg.FileName;
}
if (File.Exists(fileName1))
{
StreamReader SR = new StreamReader(fileName1);
string Data = null;
int count = 0;
while ((Data = SR.ReadLine()) != null)
{
count++;
if (count > 1)
{
string CopyText = "";
String[] SplitData = Data.Split(',');
if (SplitData[9] != null && SplitData[11] != null)
{
CopyText = Data;
string data1 = SR.ReadLine();
//MessageBox.Show(CopyText);
}
using (StreamReader SR1 = new StreamReader(fileName1))
{
//var SW = new StreamWriter(resultString1);
string line;
while ((line = SR1.ReadLine()) != null)
{
//String TrimData2 = line.Trim();
String[] SplitText = line.Split(',');
if (SplitText[9] == SplitData[9] && SplitText[11] == null)
{
using (StreamWriter SW = new StreamWriter(resultString1))
{
SW.WriteLine(CopyText);
MessageBox.Show(CopyText);
SW.Close();
}
}
}
SR1.Close();
}
}
}
}
I made an event (btn_proceed_Click) to proceed the method, you can place him (method) anywhere you want. Here is the full code, just replace it with yours:
private void btn_proceed_Click(object sender, EventArgs e)
{
// This dictionary contains indices of rows we need to replace.
Dictionary<int, int> replaceable = new Dictionary<int, int>();
replaceable.Add(4, 1);
replaceable.Add(7, 5);
replaceable.Add(13, 11);
string input = String.Empty;
OpenFileDialog pfdg = new OpenFileDialog();
if (pfdg.ShowDialog() == DialogResult.OK)
{
input = pfdg.FileName;
}
// I placed the result into the another file called result.txt. You can use output path as same as input to overwrite the file.
ReplaceLines(replaceable, input, #"C:\Users\Wallstrider\Documents\Visual Studio 2010\Projects\result.txt", 9);
}
/// <summary>
/// Replaces lines of the file depends on 9th item is exist.
/// </summary>
/// <param name="replaceable">Rows incides to replace.</param>
/// <param name="input_path">Path to the input file.</param>
/// <param name="output_path">Path to the output file.</param>
/// <param name="has_value_index">Index of a split data value in the row.</param>
private void ReplaceLines(Dictionary<int, int> replaceable, string input_path, string output_path, int has_value_index)
{
if (File.Exists(input_path))
{
string file;
file = new StreamReader(input_path).ReadToEnd();
string[] lines = file.Split(new char[] { '\n' });
List<string[]> split_data = new List<string[]>();
for (int i = 0; i < lines.Length; i++)
split_data.Add(lines[i].Split(','));
List<int> allowed_for_replace_indices = new List<int>();
List<int> not_allowed_for_replace_indices = new List<int>();
// Check if the row has the value of 9th item then we are allowed to replace rows.
for (int i = 1; i < split_data.Count; i++)
{
if (split_data[i][has_value_index] != String.Empty)
allowed_for_replace_indices.Add(i);
else
not_allowed_for_replace_indices.Add(i);
}
List<int> rows_replaced = new List<int>();
List<int> rows_not_replaced = new List<int>();
// Loop through our replaceable indices dictionary.
for (int i = 0; i < replaceable.Count; i++)
{
int key = replaceable.ElementAt(i).Key;
int value = replaceable.ElementAt(i).Value;
// if both rows have 9th item then we can start replacement.
if (allowed_for_replace_indices.Contains(key) && allowed_for_replace_indices.Contains(value))
{
string temp = lines[value];
lines[value] = lines[key];
lines[key] = temp;
rows_replaced.Add(key);
rows_replaced.Add(value);
}
else
{
rows_not_replaced.Add(key);
rows_not_replaced.Add(value);
}
}
using (StreamWriter sw = new StreamWriter(output_path))
{
for (int i = 0; i < lines.Length; i++)
sw.WriteLine(lines[i]);
sw.Flush();
sw.Close();
}
MessageBox.Show("Rows replaced: " + String.Join("; ", rows_replaced.ToArray()) + " .\nRows not replaced: " + String.Join("; ", rows_not_replaced.ToArray()) + ".\nComplete.");
}
}
The following code will replace (I count from zero) 4, 1; 7, 5; 13, 11; rows if each of has the 9th item if I understand your logic properly.
To force your code work just replace:
if (SplitText[9] == SplitData[9] && SplitText[11] == null)
To:
if (SplitText[9] == SplitData[9] && SplitText[11] == String.Empty)
Because SplitText[11] will never be a null in all situations within the file you have shared.
Edit: See my Solution below...
I had the following Problem to solve:
We receive Files (mostly adress-Information) from different sources, these can be in Windows Standard with CR/LF ('\r''\n') as Line Break or UNIX with LF ('\n').
When reading text in using the StreamReader.ReadLine() method, this is no Problem because it handles both cases equally.
The Problem occurs when you have a CR or a LF somewhere in the File that is not supposed to be there.
This happens for example if you Export a EXCEL-File with Cells that contain LineBreaks within the Cell to .CSV or other Flat-Files.
Now you have a File that for example has the following structure:
FirstName;LastName;Street;HouseNumber;PostalCode;City;Country'\r''\n'
Jane;Doe;co James Doe'\n'TestStreet;5;TestCity;TestCountry'\r''\n'
John;Hancock;Teststreet;1;4586;TestCity;TestCounty'\r''\n'
Now the StreamReader.ReadLine() Method reads the First Line as:
FirstName;LastName;Street;HouseNumber;PostalCode;City;Country
Which is fine but the seccond Line will be:
Jane;Doe;co James Doe
This will either break your Code or you will have false Results, as the following Line will be:
TestStreet;5;TestCity;TestCountry
So we usualy ran the File trough a tool that checks if there are loose '\n' or '\r' arround and delete them.
But this step is easy to Forget and so I tried to implement a ReadLine() method of my own. The requirement was that it would be able to use one or two LineBreak characters and those characters could be defined freely by the consuming logic.
This is the Class that I came up with:
public class ReadFile
{
private FileStream file;
private StreamReader reader;
private string fileLocation;
private Encoding fileEncoding;
private char lineBreak1;
private char lineBreak2;
private bool useSeccondLineBreak;
private bool streamCreated = false;
private bool endOfStream;
public bool EndOfStream
{
get { return endOfStream; }
set { endOfStream = value; }
}
public ReadFile(string FileLocation, Encoding FileEncoding, char LineBreak1, char LineBreak2, bool UseSeccondLineBreak)
{
fileLocation = FileLocation;
fileEncoding = FileEncoding;
lineBreak1 = LineBreak1;
lineBreak2 = LineBreak2;
useSeccondLineBreak = UseSeccondLineBreak;
}
public string ReadLine()
{
if (streamCreated == false)
{
file = new FileStream(fileLocation, FileMode.Open, FileAccess.Read, FileShare.ReadWrite);
reader = new StreamReader(file, fileEncoding);
streamCreated = true;
}
StringBuilder builder = new StringBuilder();
char[] buffer = new char[1];
char lastChar = new char();
char currentChar = new char();
bool first = true;
while (reader.EndOfStream != true)
{
if (useSeccondLineBreak == true)
{
reader.Read(buffer, 0, 1);
lastChar = currentChar;
if (currentChar == lineBreak1 && buffer[0] == lineBreak2)
{
break;
}
else
{
currentChar = buffer[0];
}
if (first == false)
{
builder.Append(lastChar);
}
else
{
first = false;
}
}
else
{
reader.Read(buffer, 0, 1);
if (buffer[0] == lineBreak1)
{
break;
}
else
{
currentChar = buffer[0];
}
builder.Append(currentChar);
}
}
if (reader.EndOfStream == true)
{
EndOfStream = true;
}
return builder.ToString();
}
public void Close()
{
if (streamCreated == true)
{
reader.Close();
file.Close();
}
}
}
This code works fine, it does what it is supposed to do but compared to the original StreamReader.ReadLine() method, it is ~3 Times slower. As we work with large row-Counts the difference is not only messured but also reflected in real world Performance.
(for 700'000 Rows it takes ~ 5 Seconds to read all Lines, extract a Chunk and write it to a new File, with my method it takes ~15 Seconds on my system)
I tried different aproaches with bigger buffers but so far I wasn't able to increase Performance.
What I would be interessted in:
Any suggestions how I could improve the performance of this code to get closer to the original Performance of StreamReader.ReadLine()?
Solution:
This now takes ~6 Seconds (compared to ~5 Sec using the Default 'StreamReader.ReadLine()' ) for 700'000 Rows to do the same things as the code above does.
Thanks Jim Mischel for pointing me in the right direction!
public class ReadFile
{
private FileStream file;
private StreamReader reader;
private string fileLocation;
private Encoding fileEncoding;
private char lineBreak1;
private char lineBreak2;
private bool useSeccondLineBreak;
const int BufferSize = 8192;
int bufferedCount;
char[] rest = new char[BufferSize];
int position = 0;
char lastChar;
bool useLastChar;
private bool streamCreated = false;
private bool endOfStream;
public bool EndOfStream
{
get { return endOfStream; }
set { endOfStream = value; }
}
public ReadFile(string FileLocation, Encoding FileEncoding, char LineBreak1, char LineBreak2, bool UseSeccondLineBreak)
{
fileLocation = FileLocation;
fileEncoding = FileEncoding;
lineBreak1 = LineBreak1;
lineBreak2 = LineBreak2;
useSeccondLineBreak = UseSeccondLineBreak;
}
private int readInBuffer()
{
return reader.Read(rest, 0, BufferSize);
}
public string ReadLine()
{
StringBuilder builder = new StringBuilder();
bool lineFound = false;
if (streamCreated == false)
{
file = new FileStream(fileLocation, FileMode.Open, FileAccess.Read, FileShare.ReadWrite, 8192);
reader = new StreamReader(file, fileEncoding);
streamCreated = true;
bufferedCount = readInBuffer();
}
while (lineFound == false && EndOfStream != true)
{
if (position < bufferedCount)
{
for (int i = position; i < BufferSize; i++)
{
if (useLastChar == true)
{
useLastChar = false;
if (rest[i] == lineBreak2)
{
count++;
position = i + 1;
lineFound = true;
break;
}
else
{
builder.Append(lastChar);
}
}
if (rest[i] == lineBreak1)
{
if (useSeccondLineBreak == true)
{
if (i + 1 <= BufferSize - 1)
{
if (rest[i + 1] == lineBreak2)
{
position = i + 2;
lineFound = true;
break;
}
else
{
builder.Append(rest[i]);
}
}
else
{
useLastChar = true;
lastChar = rest[i];
}
}
else
{
position = i + 1;
lineFound = true;
break;
}
}
else
{
builder.Append(rest[i]);
}
position = i + 1;
}
}
else
{
bufferedCount = readInBuffer();
position = 0;
}
}
if (reader.EndOfStream == true && position == bufferedCount)
{
EndOfStream = true;
}
return builder.ToString();
}
public void Close()
{
if (streamCreated == true)
{
reader.Close();
file.Close();
}
}
}
The way to speed this up would be to have it read more than one character at a time. For example, create a 4 kilobyte buffer, read data into that buffer, and then go character-by-character. If you copy character-by-character to a StringBuilder, it's pretty easy.
The code below shows how to parse out lines in a loop. You'd have to split this up so that it can maintain state between calls, but it should give you the idea.
const int BufferSize = 4096;
const string newline = "\r\n";
using (var strm = new StreamReader(....))
{
int newlineIndex = 0;
var buffer = new char[BufferSize];
StringBuilder sb = new StringBuilder();
int charsInBuffer = 0;
int bufferIndex = 0;
char lastChar = (char)-1;
while (!(strm.EndOfStream && bufferIndex >= charsInBuffer))
{
if (bufferIndex > charsInBuffer)
{
charsInBuffer = strm.Read(buffer, 0, buffer.Length);
if (charsInBuffer == 0)
{
// nothing read. Must be at end of stream.
break;
}
bufferIndex = 0;
}
if (buffer[bufferIndex] == newline[newlineIndex])
{
++newlineIndex;
if (newlineIndex == newline.Length)
{
// found a line
Console.WriteLine(sb.ToString());
newlineIndex = 0;
sb = new StringBuilder();
}
}
else
{
if (newlineIndex > 0)
{
// copy matched newline characters
sb.Append(newline.Substring(0, newlineIndex));
newlineIndex = 0;
}
sb.Append(buffer[bufferIndex]);
}
++bufferIndex;
}
// Might be a line left, without a newline
if (newlineIndex > 0)
{
sb.Append(newline.Substring(0, newlineIndex));
}
if (sb.Length > 0)
{
Console.WriteLine(sb.ToString());
}
}
You could optimize this a bit by keeping track of the starting position so that when you find a line you create a string from buffer[start] to buffer[current], without creating a StringBuilder. Instead you call the String(char[], int32, int32) constructor. That's a little tricky to handle when you cross a buffer boundary. Probably would want to handle crossing the buffer boundary as a special case and use a StringBuilder for temporary storage in that case.
I wouldn't bother with that optimization, though, until after I got this first version working.