C# add line numbers to a text file - c#

I am trying to read a text file in C# and add line numbers to the lines.
This my input file:
This is line one
this is line two
this is line three
And this should be the output:
1 This is line one
2 this is line two
3 this is line three
This is my code so far:
class Program
{
public static void Main()
{
string path = Directory.GetCurrentDirectory() + #"\MyText.txt";
StreamReader sr1 = File.OpenText(path);
string s = "";
while ((s = sr1.ReadLine()) != null)
{
for (int i = 1; i < 4; i++)
Console.WriteLine(i + " " + s);
}
sr1.Close();
Console.WriteLine();
StreamWriter sw1 = File.AppendText(path);
for (int i = 1; i < 4; i++)
{
sw1.WriteLine(s);
}
sw1.Close();
}
}
I am 90% sure I need to use for cycle to get the line numbers there but so far with this code I get this output in the console:
1 This is line one
2 This is line one
3 This is line one
1 this is line two
2 this is line two
3 this is line two
1 this is line three
2 this is line three
3 this is line three
And this is in the output file:
This is line number one.
This is line number two.
This is line number three.1
2
3
I am not sure why the string variable s is not used when writing in the file even though it is defined earlier (another block, another rules maybe?).

using System;
using System.Collections.Generic;
using System.IO;
using System.Text;
namespace AppendText
{
class Program
{
public static void Main()
{
string path = Directory.GetCurrentDirectory() + #"\MyText.txt";
StreamReader sr1 = File.OpenText(path);
string s = "";
int counter = 1;
StringBuilder sb = new StringBuilder();
while ((s = sr1.ReadLine()) != null)
{
var lineOutput = counter++ + " " + s;
Console.WriteLine(lineOutput);
sb.Append(lineOutput);
}
sr1.Close();
Console.WriteLine();
StreamWriter sw1 = File.AppendText(path);
sw1.Write(sb);
sw1.Close();
}
}
}

IEnumerable<string> lines = File.ReadLines(file)
.Select((line,i)=>i + " " + line)
.ToList();
File.WriteAllLines(file, lines);

OPEN STREAM
read the whole line and store it in a temp variable.
Use a counter to keep track which line you have read.
concatenate the counter with the temp variable.
save it to the file.
move your line pointer to next line and
repeat.
THEN CLOSE YOUR STREAM

I could provide you the right code, but because it is home work I will just ask you question that should lead you to the right answer:
why do you close the StreamReader the while inside your loop ? You will still access it after, that can cause an error.
why do you write in your StreamWriter without the prepended index ?
Why do you open the StreamWriter inside the loop ? Wouldn't it be better to open the StreamWriter and StreamReader outside the loop. Do you job in the loop and then close the Streams ?

You need to prepend the line number to each line string. Check out String.Format. Also, try a counter variable that sits outside the while loop to keep the line number count.
Hopefully that's enough to get you on the right path without handing you the exact answer.

Are you sure you want to close stream inside the loop while?

Watch out the FOR loops, you put them inside the While, so basically you are saying:
while ((s = sr1.ReadLine()) != null)
Every row read
for (int i = 1; i < 4; i++)
Repeat 3 times a write.
Also, you are closing the stream inside the while, so after the first row read.

Here is one major issue for you:
for (int i = 1; i < 4; i++)
Console.WriteLine(i + " " + s);
}
You are closing the for loop with a curly brace but not using a curly brace to open it. This means that the curly brace quoted above is actually closing the while loop so you loop through doing all the console.writeline and then when you come to writing to the file you are actually not reading from the file at all - s is "" due to scoping.

An alternative to #Hasan's answer for in-memory strings as a one-liner:
function AddLineNumbers(string input) =>
String.Join('\n', input.Split('\n').Select((text, i) => $"{i+1}: {text}"));

Related

WPF C#: Splitting a long string

I have been thinking about this problem for a long time, but now I managed to request help from those who know. I have a code, that is supposed to read text from a big file (a couple of Gbs) line by line. Every line can be around 500Mb as it must be a video, converted to base64 connected with video name. Here I read current line and separate video name from its' content (start from else).
string[] fileline = GetFileLine(resPath, currentRow).Split(); //Here split causes SystemOutOfMemory
try
{
string base64 = fileline[0].Replace(specSymbol, ' ');
try
{
if (!IsVideo(ref base64) && !IsGif(ref base64))
{
ShowPrimary();
imgFile.Source = BytesToBitmap(Convert.FromBase64String(base64));
}
else
btnLoadFile.Background = readyColor;
if (fileline.Length > 1)
return fileline[1].Replace(specSymbol, ' ');
}
catch (Exception ex3) { MessageBox.Show("Next(4):" + ex3.Message); }
}
catch (Exception ex2) { MessageBox.Show("Next(3):" + ex2.Message); }
So my question is: does the way to split long strings exist or I only have to store names in a separate file without splitting?
UPD1: I have wrote a method using an advice #canton7 gave me. I have tested it on really small files (around 100 symbols), where it works good, but I am testing it now on 25Mb file, and the speed of the reading is awful (like 10Mb in an hour), even though, the reading of really big files didn't make the program to crash, so I think I'm on the right way.
I still wonder if there is a better method. If you have some advice on the ready method improvement - please give it here.
static string ReadFirstHalfAfter(string path, int skips = 0)
{
int skipsDone = 0;
int ri = 0;
char[] buffer = new char[1];
StreamReader reader = new StreamReader(path);
while (reader.Peek() >= 0)//while reader is not at the end of file
{
reader.Read(buffer, ri, 1);//reading one element from the current position
if (skipsDone < skips)//line skips not enough
{
if (buffer[buffer.Length - 1] == '\n')//current symbol is line end
{
skipsDone++;//line skip counted
continue;
}
}
else//enough line skips
{
if (buffer[buffer.Length - 1] == ' ') break; //if line separator - stop
ExpandArray(ref buffer); //adding one more free element
ri++; //switching element to read next
}
if (ri % 10000 == 0) Console.Write('.');
}
return new string(buffer).Trim(' ');
}
To separate string into 2 pieces you can use substring to save memory, but if you want more memory to be saved - there is only one way through writing the line parts in the different rows.

C# - Reading Text Files(System IO)

I would like to consecutively read from a text file that is generated by my program. The problem is that after parsing the file for the first time, my program reads the last line of the file before it can begin re-parsing, which causes it to accumulates unwanted data.
3 photos: first is creating tournament and showing points, second is showing text file and the third is showing that TeamA got more 3 points
StreamReader = new StreamReader("Torneios.txt");
torneios = 0;
while (!rd.EndOfStream)
{
string line = rd.ReadLine();
if (line == "Tournament")
{
torneios++;
}
else
{
string[] arr = line.Split('-');
equipaAA = arr[0];
equipaBB = arr[1];
res = Convert.ToChar(arr[2]);
}
}
rd.Close();
That is what I'm using at the moment.
To avoid mistakes like these, I highly recommend using File.ReadAllText or File.ReadAllLines unless you are using large files (in which case they are not good choices), here is an example of an implementation of such:
string result = File.ReadAllText("textfilename.txt");
Regarding your particular code, an example using File.ReadAllLines which achieves this is:
string[] lines = File.ReadAllLines("textfilename.txt");
for(int i = 0; i < lines.Length; i++)
{
string line = lines[i];
//Do whatever you want here
}
Just to make it clear, this is not a good idea if the files you intend to read from are large (such as binary files).

add an empty line inside text file

So let me give you an example of how my file could look:
Hello
my
name
is
Now, for example, I want to go through the different lines of this file with System.IO.File.ReadAllLines(); and then check with a loop whether the current line has the word "my" in it (so the second line in this case).
As my next step, I want to add a new line right after "my", so it looks like this:
Hello
my
name
is
I approached this with 2 methods now. I was hoping File.Append(); would offer a method where I could append anything after it has found the string I am looking for, but obviously it only offers methods to append strings to the end of files.
My second approach was to read in all the lines with string[] test=System.IO.File.ReadAllLines();
and then iterate through all the lines, checking each line with
for (int i = 0; i < (test.Length - 1); i++)
{
if(test[i].Contains("my"))
{
test[i] = test[i] + Environment.NewLine;
}
}
and then write all this back in the file with System.IO.File.WriteAllLines();
The problem I am facing here is the fact that this command does not really add a real new line to the file, as I've checked test.Length before and after, and both time I got 4 as a result.
Another option is to add the lines to a List which would give you the Insert() method:
*Only use this for relatively small files.
Something like:
string path = #"c:\some\path\file.txt";
List<String> lines = new List<string>(System.IO.File.ReadAllLines(path));
for (int i = 0; i < lines.Count; i++)
{
if (lines[i].Contains("my"))
{
if (i < lines.Count -1)
{
lines.Insert(i + 1, "");
}
else
{
lines.Add("");
}
}
}
System.IO.File.WriteAllLines(path, lines.ToArray());
First, I suggest you use StringBuilder. It's best to use it when you're adding many strings, since strings are immutable and thus each string is created again when you do +=, or simply assigning a new one to an array slot.
This code will do what you're looking for, and it treats the no new line edge case:
var filePath = //your file path
var test = File.ReadAllLines(filePath);
var sb = new StringBuilder();
for (int i = 0; i < (test.Length - 1); i++)
{
sb.Append(test[i]);
sb.Append(Environment.NewLine);
if (test[i].Contains("my"))
{
// This adds that extra new line
sb.Append(Environment.NewLine);
}
}
sb.Append(test[test.Length-1]);
File.WriteAllText(filePath, sb.ToString());
[TestMethod]
public void InsertLines()
{
var test = File.ReadAllLines(#"c:\SUService.log");
var list = new List<string>();
for (int i = 0; i < (test.Length - 1); i++)
{
list.Add(test[i]);
if (test[i].Contains("my"))
{
list.Add(Environment.NewLine);
}
}
File.WriteAllLines(#"c:\SUService.log", list);
}
Not for the above question: But in general if you want to add a new line after a line you just have to add a new line character -> so if you want to add an empty line after a line Just add 2 newline characters
File.WriteAllText(filePath,"\n\n");
Here the the writer will jump 2 times and add the upcoming contents in the send line

StreamReader doesn't stop reading text file

I have a program to read a million- line file. Each line has one floating-point value on it. The value is to be read in and put in an element in an array.
using System;
using System.Diagnostics;
using System.IO;
namespace sort1mRandFloat
{
public class Program
{
static void Main()
{
Console.WriteLine("Creating Single array...");
Single[] fltArray = new Single[1000000];
Console.WriteLine("Array created, making string...");
String line;
Console.WriteLine("String created, opening file...");
StreamReader file = new StreamReader(#"C:\\Users\\Aaron\\Desktop\\rand1mFloats.txt");
Console.WriteLine("File opened, creating stopwatch and starting main execution event. See you on the other side.");
int i;
Stopwatch stopWatch = new Stopwatch();
stopWatch.Start();
while((line = file.ReadLine()) != null)
{
for(i=0; i < 1000000; i++)
{
fltArray[i] = Convert.ToSingle(line);
if (i == 999999)
Console.WriteLine("At 999999");
}
}
file.Close();
stopWatch.Stop();
TimeSpan ts = stopWatch.Elapsed;
String elapsedTime = String.Format("{0:00}:{1:00}:{2:00}.{3:00}",
ts.Hours, ts.Minutes, ts.Seconds, ts.Milliseconds/10);
Console.WriteLine("It took " + elapsedTime + " to read a thousand lines into the array.\n");
Console.WriteLine("Element 0 is: " + fltArray[0]);
Console.WriteLine("Element 999999 is: " + fltArray[999999]);
Console.ReadLine();
}
}
}
When this code is run on the file, it doesn't ever stop. It's looking for something to tell it that it's at the end of the tile or something, and it's not finding it. Upon filling the 999,999th element, it loops back to 0 and starts again.
This code is more or less based on what Microsoft recommends on their website... any idea on what I'm doing wrong?
The file can be found below. As I have not been able to store the file in the array yet, I cannot say how long it will take for it to work. There's quite a few values in the file. Metered connection warning: 18 MB file.
1 million line file- OneDrive
You should not have for inside while. You only need one loop:
var i = 0;
while((line = file.ReadLine()) != null)
{
fltArray[i] = Convert.ToSingle(line);
if (i == 999999)
Console.WriteLine("At 999999");
i++;
}
or with for:
for(i=0; i < 1000000 && (line = file.ReadLine()) != null; i++)
{
fltArray[i] = Convert.ToSingle(line);
if (i == 999999)
Console.WriteLine("At 999999");
}
Update
I'm getting following results for your file:
Creating Single array...
Array created, making string...
String created, opening file...
File opened, creating stopwatch and starting main execution event. See you on the other side.
At 999999
It took 00:00:00.42 to read a thousand lines into the array.
Element 0 is: 0,9976465
Element 999999 is: 0,04730097
Release build, run outside of VS, i5-3317U # 1.7GHz.
I'm on my phone, so I apologize for the brevity. Your outer while loop will hit each of your 1 million lines, and your inner for loop is iterating 1 million times for a total of 1 trillion iterations. Also, your while condition can utilize the file.EndOfStream property.
Basically you are converting every line 1000000 times, because you have the for-loop within your while-loop that does the reading.
Simply remove the for-loop and replace it with i++
Every time file.ReadLine is called it reads a single line from file until it reaches the end of the file and become null (therefor exiting your while-loop).

After adding StreamReader code, can't add variables to my text file StreamWriter

I have a problem.
This is the code:
StreamReader lasa = new StreamReader("klubbar.txt");
while (true)
{
string line = lasa.ReadLine();
if (line == null)
{
break;
}
antal++;
Console.WriteLine(line);
}
lasa.Close();
These line of code has caused me a problem with writing my variable to my text file:
StreamWriter sparaklubb = new StreamWriter("klubbar.txt");
for (int i = 1; i <= antal; i++)
{
sparaklubb.Write(klubbar[i].id + "\t");
sparaklubb.Write(klubbar[i].klubbnamn + "\t");
sparaklubb.Write(klubbar[i].vip + "\t");
sparaklubb.Write(klubbar[i].dansgolv + "\t");
sparaklubb.Write(klubbar[i].intrade + "\t");
sparaklubb.Write(klubbar[i].casino + "\t");
sparaklubb.WriteLine(klubbar[i].genre);
}
sparaklubb.Close();
Because I get this error:
I don't have a clue how to fix it, if I remove the StreamReader code everything works.
The problem is that you are not resetting antal to 0 after the reader code. This is causing your code that is inserting into the klubbar array to start at whatever the current value of antal is, for example maybe the reader read in 5 lines so antal is 5. That means you didn't put an object into the klubbar array at positions 1-4 (you really should start an index at 0). Then in your writer code you are trying to reference members on a null value at klubbar[1]. Removing the reader code "fixes" the problem because antal will be 0 when you start putting objects into the klubbar array. My suggestion is to use a List<klubb> instead of an array.
based on a quick look at your code you never populate your array, as such each entity in your array is null, so when you attempt to access a field on your object, the object itself is null.
Hence the object not set exception.
You need to correctly implement the population from the text file in your streamreader section
klubb[] klubbar = new klubb[100]; //Skapa en vektor där varje element kan behålla flera olika variblar från klassen "klubb"
//IF YOU REMOVE THIS STREAMREADERCODE WITH THE WHILE LOOP EVERYTHING WILL WORK FINE
StreamReader lasa = new StreamReader("klubbar.txt");
while (true)
{
string line = lasa.ReadLine();
if (line == null)
{
break;
}
antal++;
Console.WriteLine(line);
// klubbar[antal] = line; //ADD EACH LINE IN TEXTFILE TO AN SEPAREATE INDEX IN ARRAY klubar
}
lasa.Close();
As you read in each line you need to parse that line into your Klubb object. which should be simple enough as it looks like your file is tab delimited.
1 Harrys True 6 100 blackjack klubbfan
2 xoY True 6 100 blackajacka klubb
3 lounge True 6 100 blackjacka klubb
in response to comment, so you could do something like:
klubbar[antal] = GetKlubbFromString(line);
^ this line is to replace the line in your stream reader section
private static klubb GetKlubbFromString(string line)
{
string[] lineItems = line.Split('\t');
//1 Harrys True 6 100 blackjack klubbfan
return new klubb
{
id = Convert.ToInt32(lineItems[0]),
klubbnamn = lineItems[1],
vip = Convert.ToBoolean(lineItems[2]),
dansgolv = Convert.ToInt32(lineItems[3]),
intrade = Convert.ToInt32(lineItems[4]),
casino = lineItems[5],
genre = lineItems[6]
};
}
i personally wouldn't use Convert.To due to exceptions, i favor TryParse, and i'm sure some brighter SO members will come along and offer better ways to do this.

Categories

Resources