How do I delete all lines in text file below certain text? - c#

I have a code which iterates through the entire text file searching for a specific text "[names]", and "tried" to delete all the lines below the text. I tried File.WriteAllText(INILoc, string.Empty);, but this just deletes everything in the entire text file. How do I make it so only all the lines below "[names]" gets deleted?
I have set up the iteration likes this :
string[] lines = File.ReadAllLines(INILoc);
bool containsSearchResul = false;
foreach (string line in lines)
{
if (containsSearchResul)
{
File.WriteAllText(INILoc, string.Empty);
}
if (line.Contains("[names]"))
{
containsSearchResul = true;
}
}

You need to store lines before "[names]" text into a string variable, and when condition (line.Contains("[names]")) satisfy then just break the loop and write string value into the same file.
Something like,
string[] lines = File.ReadAllLines(INILoc); //Considering INILoc is a string variable which contains file path.
StringBuilder newText = new StringBuilder();
bool containsSearchResul = false;
foreach (string line in lines)
{
newText.Append(line);
newText.Append(Environment.NewLine); //This will add \n after each line so all lines will be well formatted
//Adding line into newText before if condition check will add "name" into file
if (line.Contains("[names]"))
break;
}
File.WriteAllText(INILoc, newText.ToString());
//^^^^^^^ due to string.Empty it was storing empty string into file.
Note: If you are using StringBuilder class, then do not miss to add Using System.Text in your program

Use StreamReader as it will give you the best performance as you don't need to read the whole file. Swap 'PATH TO INPUT FILE' with your file path and the result will be stored at the path you provide for 'PATH TO OUTPUT FILE'.
using (var sr = new StreamReader("PATH TO INPUT FILE"))
{
using (var sw = new StreamWriter("PATH TO OUTPUT FILE"))
{
var line = sr.ReadLine();
while (line != null)
{
sw.WriteLine(line);
if (line.Contains("[names]"))
{
sw.Close();
sr.Close();
}
else
{
line = sr.ReadLine();
}
}
}
}
If you need to write to the same file:
var sb = new StringBuilder();
using (var sr = new StreamReader("PATH TO INPUT FILE"))
{
var line = sr.ReadLine();
while (line != null)
{
sb.AppendLine(line);
if (line.Contains("[names]"))
{
sr.Close();
}
else
{
line = sr.ReadLine();
}
}
}
File.WriteAllText("PATH TO INPUT FILE", sb.ToString());

Based on the requested code I have put together modifications.
string[] lines = File.ReadAllLines(INILoc);
//create a list to hold the lines
List<string> output = new List<string>();
//loop through each line
foreach (string line in lines)
{
//add current line to ouput.
output.Add(line);
//check to see if our line includes the searched text;
if (line.Contains("[names]"))
{
//output to the file and then exit loop causing all lines below this
//one to be skipped
File.WriteAllText(INILoc, output.ToArray());
break;
}
}

The problem with your code is that it delete all the lines before the [names], not after (more exactly, write only the lines after that text). Also, any time you rewrite all the file content, and so remove all previous wrote line. It'll work as follows:
string[] lines = File.ReadAllLines(INILoc);
using (StreamWriter writer = new StreamWriter(INILoc)) // https://learn.microsoft.com/en-us/dotnet/standard/io/how-to-write-text-to-a-file
{
bool containsSearchResul = false;
foreach (string line in lines)
{
if (!containsSearchResul)
{
writer.Write(INILoc, string.Empty);
}
if (line.Contains("[names]"))
{
containsSearchResul = true;
}
}
}
You have another, better option to do this with break:
string[] lines = File.ReadAllLines(INILoc);
using (StreamWriter writer = new StreamWriter(INILoc)) // https://learn.microsoft.com/en-us/dotnet/standard/io/how-to-write-text-to-a-file
{
foreach (string line in lines)
{
if (line.Contains("[names]"))
{
break;
}
writer.WriteLine(INILoc, string.Empty);
}
}
But you can do this in prefered, more-readable way, by using LINQ:
using System.Linq;
// ...
string[] lines = File.ReadAllLines(INILoc);
string[] linesTillNames = lines
.Take( // Take just N items from the array
Array.IndexOf(lines, "[names]") // Until the index of [names]
)
.ToArray();
File.WriteAllLines(INILoc, linesTillNames);

You can also use: WriteAllLines(string path, IEnumerable<string> contents) like this:
string[] lines = File.ReadAllLines(INILoc);
List<string> linesToWrite = new List<string>();
foreach(string line in lines)
{
linesToWrite.Add(line);
if (line.Contains("[names]")) break;
}
File.WriteAllLines(INILoc, linesToWrite);

Related

Read file, check correctness of column, write file C#

I need to check certain columns of data to make sure there are no trailing blank spaces. At first thought I thought it would be very easy, but after attempting to achieve the goal I have got stuck.
I know that there should be 6-digits in the column I need to check. If there is less I will reject, if there are more I will trim the blank spaces. After doing that for the entire file, I want to write it back to the file with the same delimiters.
This is my attempt:
Everything seems to be working correctly except for writing the file.
if (File.Exists(filename))
{
using (StreamReader sr = new StreamReader(filename))
{
string lines = sr.ReadLine();
string[] delimit = lines.Split('|');
while (delimit[count] != "COLUMN_DATA_TO_CHANGE")
{
count++;
}
string[] allLines = File.ReadAllLines(#filename);
foreach(string nextLine in allLines.Skip(1)){
string[] tempLine = nextLine.Split('|');
if (tempLine[count].Length == 6)
{
checkColumn(tempLine);
writeFile(tempLine);
}
else if (tempLine[count].Length > 6)
{
tempLine[count] = tempLine[count].Trim();
checkColumn(tempLine);
}
else
{
throw new Exception("Not enough numbers");
}
}
}
}
}
public static void checkColumn(string[] str)
{
for (int i = 0; i < str[count].Length; i++)
{
char[] c = str[count].ToCharArray();
if (!Char.IsDigit(c[i]))
{
throw new Exception("A non-digit is contained in data");
}
}
}
public static void writeFile(string[] str)
{
string temp;
using (StreamWriter sw = new StreamWriter(filename+ "_tmp", false))
{
StringBuilder builder = new StringBuilder();
bool firstColumn = true;
foreach (string value in str)
{
if (!firstColumn)
{
builder.Append('|');
}
if (value.IndexOfAny(new char[] { '"', ',' }) != -1)
{
builder.AppendFormat("\"{0}\"", value.Replace("\"", "\"\""));
}
else
{
builder.Append(value);
}
firstColumn = false;
}
temp = builder.ToString();
sw.WriteLine(temp);
}
}
If there is a better way to go about this, I would love to hear it. Thank you for looking at the question.
edit:
file structure-
country| firstname| lastname| uniqueID (column I am checking)| address| etc
USA|John|Doe|123456 |5 main street|
notice the blank space after the 6
var oldLines = File.ReadAllLines(filePath):
var newLines = oldLines.Select(FixLine).ToArray();
File.WriteAllLines(filePath, newLines);
string FixLine(string oldLine)
{
string fixedLine = ....
return fixedLine;
}
The main problem with writing the file is that you're opening the output file for each output line, and you're opening it with append=false, which causes the file to be overwritten every time. A better approach would be to open the output file one time (probably right after validating the input file header).
Another problem is that you're opening the input file a second time with .ReadAllLines(). It would be better to read the existing file one line at a time in a loop.
Consider this modification:
using (StreamWriter sw = new StreamWriter(filename+ "_tmp", false))
{
string nextLine;
while ((nextLine = sr.ReadLine()) != null)
{
string[] tempLine = nextLine.Split('|');
...
writeFile(sw, tempLine);

Replace data/value of csv file

I want to count the number of some strings and store it into a csv file. I've tried it but I don't know if this is the correct way and in addition, there are two problems.
First of all, here is my method:
public void CountMacNames(String macName)
{
string path = #"D:\Counter\macNameCounter.csv";
if (!File.Exists(path))
{
File.Create(path).Close();
}
var lines = File.ReadLines(path);
foreach (var line in lines)
{
bool isExists = line.Split(',').Any(x => x == macName);
if (isExists)
{
// macName exists, increment it's value by 1
}
else
{
// macName does not exists, add macName to CSV file and start counter by 1
var csv = new StringBuilder();
var newLine = string.Format("{0},{1}", macName, 1);
csv.AppendLine(newLine);
File.WriteAllText(path, csv.ToString());
}
}
}
The first problem is this IOException:
The process cannot access the file 'D:\Counter\macNameCounter.csv'
because it is being used by another process.
The second problem is, that I don't know how to increment the value by one, if a macName exists in the csv file (see first comment)
EDIT: Example for method "CountMacNames" call:
CountMacNames("Cansas");
CountMacNames("Wellback");
CountMacNames("Newton");
CountMacNames("Cansas");
CountMacNames("Princet");
Then, the CSV file should contain:
Cansas, 2
Wellback, 1
Newton, 1
Princet, 1
OK, this is what I'd do:
public void CountMacNames(String macName)
{
string path = #"D:\Counter\macNameCounter.csv";
// Read all lines, but only if file exists
string[] lines = new string[0];
if (File.Exists(path))
lines = File.ReadAllLines(path);
// This is the new CSV file
StringBuilder newLines = new StringBuilder();
bool macAdded = false;
foreach (var line in lines)
{
string[] parts = line.Split(',');
if (parts.Length == 2 && parts[0].Equals(macName))
{
int newCounter = Convert.ToIn32(parts[1])++;
newLines.AppendLine(String.Format("{0},{1}", macName, newCounter));
macAdded = true;
}
else
{
newLines.AppendLine(line.Trim());
}
}
if (!macAdded)
{
newLines.AppendLine(String.Format("{0},{1}", macName, 1));
}
File.WriteAllText(path, newLines.ToString());
}
This code does this:
Read all the lines from file only if it exists - otherwise we start a new file
Iterate over all the lines
If the first part of a 2-part line equals the mac, add 1 to counter and add line to output
If the first part doesn't match or the line format is wrong, add the line to output as is
If we didn't find the mac in any line, add a new line for the mac with counter 1
Write the file back
You can't read and write to the same file at the same time (in a simple way).
For small files, there are already answers.
If your file is really large (too big to fit in memory) you need another approach:
Read input file line by line
optinally modify the current line
write line to a temporary file
If finished delete input file, rename temporary file
For the first problem you can either read all the lines into memory and work there then write it all out again, or use streams.
using (FileStream fs = File.Open(filePath, FileMode.Create, FileAccess.ReadWrite))
{
var sw = new StreamWriter(fs);
var sr = new StreamReader(fs);
while(!streamReader.EndOfStream)
{
var line = sr.ReadLine();
//Do stuff with line.
//...
if (macExists)
{
//Increment the number, Note that in here we can only replace characters,
//We can't insert extra characters unless we rewrite the rest of the file
//Probably more hassle than it's worth but
//You could have a fixed number of characters like 000001 or 1
//Read the number as a string,
//Int.Parse to get the number
//Increment it
//work out the number of bytes in the line.
//get the stream position
//seek back to the beginning of the line
//Overwrite the whole line with the same number of bytes.
}
else
{
//Append a line, also harder to do with streams like this.
//Store the current position,
//Seek to the end of the file,
//WriteLine
//Seek back again.
}
}
}
You need to read the file in and release it, like this, to avoid the IO exception:
string[] lines = null;
using (var sr = new System.IO.StreamReader(path))
lines = sr.ReadToEnd().Split(new string[] {"\r", "\n"}, StringSplitOptions.RemoveEmptyEntries);
As for the count, you can just add an int value, change the method return type as int, too.
public int CountMacNames(String macName, String path)
{
if (!File.Exists(path))
{
File.Create(path).Close();
}
string[] lines = null;
using (var sr = new System.IO.StreamReader(path))
lines = sr.ReadToEnd().Split(new string[] {"\r", "\n"}, StringSplitOptions.RemoveEmptyEntries);
return lines.Where(p => p.Split(',').Contains(macName)).Count();
}
and inside the method that calls it:
var path = #"<PATH TO FILE>";
var cnt = CountMacNames("Canvas", path);
if (cnt > 0)
{
using (var sw = new StreamWriter(path, true, Encoding.Unicode))
sw.WriteLine(string.Format("Canvas,{0}", cnt));
}
Now, var res = CountMacNames("Canvas","PATH"); will return 2, and the lines "Canvas,2" or "Newton,1" will be appended to the file, without overwriting it.

Read text file and split it over

So, I know my headline is a bit confusing, I will explain.
My code looks like this:
string filename = "C:\\C#\\maplist.txt"; // please put the text file path.
string filename2 = "C:\\C#\\zemaplist.txt";
string map;
StreamReader sr = new StreamReader(filename);
StreamWriter sw = new StreamWriter(filename2);
List<string> maps = new List<string> { };
while ((map = sr.ReadLine()) != null)
{
maps.Add(map);
}
sr.Close();
for (int i = 0; i < maps.Count; i++)
{
Console.WriteLine(maps[i]);
sw.WriteLine(maps[i]);
}
sw.Close();
and what i need to do is when the code read a new line, in my line there is
"Hey,Hey"
I want to split the , from each other so I can take both of them as other parameters, so that the first Hey will be added to maps and the other hey will be maps2,
How can I do that?
You can use Split() function to Split the given String based on delimiter.
Try This:
while ((map = sr.ReadLine()) != null)
{
maps.Add(map.Split(',')[0].Trim());
maps2.Add(map.Split(',')[1].Trim());
}
Simple Code:
using System.IO;
string filename = "C:\\C#\\maplist.txt"; // please put the text file path.
string filename2 = "C:\\C#\\zemaplist.txt";
string map;
StreamWriter sw = new StreamWriter(filename2);
List<string> maps = new List<string> { };
List<string> maps2 = new List<string> { };
String [] allLines = File.ReadAllLines(filename);
foreach(String line in allLines)
{
maps.Add(line.Split(',')[0].Trim());
maps2.Add(line.Split(',')[1].Trim());
}
for (int i = 0; i < maps.Count; i++)
{
Console.WriteLine(maps[i]);
sw.WriteLine(maps[i]);
}
sw.Close();
Solution 2:
String mapItem1="";
String mapItem2="";
if(maps.Count == maps2.Count)
{
for(int i=0;i<maps.Count;i++)
{
mapItem1=maps[i];
mapItem2=maps2[i];
}
}
while ((map = sr.ReadLine()) != null)
{
string[] split = map.Split(',');
//First Hey would be split[0], second Hey would be split[1]
maps.Add(split[0].Trim());
maps2.Add(split[1].Trim());
}
The Split method should help you out with that.
If you want to trim leading whitespace characters, you can use the .Trim() method on a string.
Use Split().
string heys = "Hey,Hey";
string[] splitArray = heys.Split(',');
Then you have:
splitArray[0] = "Hey";
splitArray[1] = "Hey";
Why even bother reading line by line? Read the entire file, replace the new line chars for a "," (to prevent last and first elements from different lines to be treated as one), and loop through a clean string.
string fileContent = Regex.Replace(File.ReadAllText("test.txt"), #"\r", ",");
List<string> mapList = new List<string>();
foreach (string map in Regex.Split(fileContent.Replace(#"\s+", ""), ","))
{
mapList.Add(map.Trim());
}

How to use textfieldParser to edit a CSV file?

I wrote a small function that reads a csv file using textField line by line , edit it a specific field then write it back to a CSV file.
Here is the code :
private void button2_Click(object sender, EventArgs e)
{
String path = #"C:\file.csv";
String dpath = #"C:\file_processed.csv";
List<String> lines = new List<String>();
if (File.Exists(path))
{
using (TextFieldParser parser = new TextFieldParser(path))
{
String line;
parser.HasFieldsEnclosedInQuotes = true;
parser.Delimiters = new string[] { "," };
while ((line = parser.ReadLine()) != null)
{
string[] parts = parser.ReadFields();
if (parts == null)
{
break;
}
if ((parts[12] != "") && (parts[12] != "0"))
{
parts[12] = parts[12].Substring(0, 3);
//MessageBox.Show(parts[12]);
}
lines.Add(line);
}
}
using (StreamWriter writer = new StreamWriter(dpath, false))
{
foreach (String line in lines)
writer.WriteLine(line);
}
MessageBox.Show("CSV file successfully processed ! ");
}
}
The field I want to edit is the 12th one (parts[12]):
for example : if parts[12] = 000,000,234 then change to 000
the file is created the problem is it does not edit the file and half the records are missing. I am hoping someone could point the mistake.
You call both parser.ReadFields() and parser.ReadLine(). Each of them advance the cursor by one. That's why you're missing half the rows. Change the while to:
while(!parser.EndOfData)
Then add parts = parser.ReadFields(); to the end of the loop. Not having this is why you're edit isn't being seen.
You can also remove:
if (parts == null)
{
break;
}
Since you no longer have line, you'll need to use the fields to keep track of your results:
lines.Add(string.Join(",", parts));//handle string escaping fields if needed.

C# text creation issue

This is whats going on. I have a huge text file that is suppose to be 1 line per entry. The issue is sometimes the line is broken with a new line.
I edit this entire file and wherever the file doesn't begin with ("\"A) i need to append the current line to the previous line ( replacing \n with " "). Everything I come up with keeps appending the line to a new line. Any help is appricated...
CODE:
public void step1a()
{
string begins = ("\"A");
string betaFilePath = #"C:\ext.txt";
string[] lines = File.ReadAllLines(betaFilePath);
foreach (string line in lines)
{
if (line.StartsWith(begins))
{
File.AppendAllText(#"C:\xt2.txt",line);
File.AppendAllText(#"C:\xt2.txt", "\n");
}
else
{
string line2 = line.Replace(Environment.NewLine, " ");
File.AppendAllText(#"C:\xt2.txt",line2);
}
}
}
Example:
Orig:
"\"A"Hero|apple|orange|for the fun of this
"\"A"Hero|apple|mango|lots of fun always
"\"A"Her|apple|fruit|no
pain is the way
"\"A"Hero|love|stackoverflowpeople|more fun
Resulting:
"\"A"Hero|apple|orange|for the fun of this
"\"A"Hero|apple|mango|lots of fun always
"\"A"Her|apple|fruit|no pain is the way
"\"A"Hero|love|stackoverflowpeople|more fun
my problem isnt the finding the if (line.StartsWith(begins)) its the else statement, it appends line2 to a new line
it seems like your string is not well formated...
try this "\"\\\"A\"" instead
public void step1a()
{
string begins = ("\"\\\"A\"");
string betaFilePath = #"C:\ext.txt";
string[] lines = File.ReadAllLines(betaFilePath);
foreach (string line in lines)
{
if (line.StartsWith(begins))
{
File.AppendAllText(#"C:\xt2.txt",line);
File.AppendAllText(#"C:\xt2.txt", "\n");
}
else
{
string line2 = line.Replace(Environment.NewLine, " ");
File.AppendAllText(#"C:\xt2.txt",line2);
}
}
}
This does what you want:
CopyFileRemovingStrayNewlines(#"C:\ext.txt", #"C:\xt2.txt", #"""\""A");
With this method:
public static void CopyFileRemovingStrayNewlines(string sourcePath, string destinationPath, string linePrefix)
{
string[] lines = File.ReadAllLines(sourcePath);
bool firstLine = true;
foreach (string line in lines)
{
if (line.StartsWith(linePrefix))
{
if (!firstLine)
File.AppendAllText(destinationPath, Environment.NewLine);
else
firstLine = false;
File.AppendAllText(destinationPath, line);
}
else
{
File.AppendAllText(destinationPath, " ");
File.AppendAllText(destinationPath, line);
}
}
}
It does have the problem of appending to an existing file, though. I suggest using a StreamWriter rather than AppendAllText. Like this:
public static void CopyFileRemovingStrayNewlines(string sourcePath, string destinationPath, string linePrefix)
{
string[] lines = File.ReadAllLines(sourcePath);
bool firstLine = true;
using (StreamWriter writer = new StreamWriter(destinationPath, false))
{
foreach (string line in lines)
{
if (line.StartsWith(linePrefix))
{
if (!firstLine)
writer.WriteLine();
else
firstLine = false;
writer.Write(line);
}
else
{
writer.Write(" ");
writer.Write(line);
}
}
}
}
Your problem is that the \ is a C# escape code.
Your string is parsed as "A, because \" is the escape code for a single ".
You should make the begins string an #-string, which does not use escape codes.
You will then need to escape the " by doubling it up.
For example:
const string begins = #"\""A";
Note that the best way to do this is to use a StreamWriter, like this:
using(StreamWriter writer = File.Create(#"C:\xt2.txt"))
{
foreach (string line in lines)
{
if (line.StartsWith(begins))
writer.WriteLine(); //Close the previous line
writer.Write(line);
}
}
Based on #SLaks's example here is some code that should do the trick:
public static void step1a()
{
string betaFilePath = #"C:\ext.txt";
string[] lines = File.ReadAllLines(betaFilePath);
using (StreamWriter writer = new StreamWriter(File.Create(#"C:\xt2.txt")))
{
string buffer = null;
foreach (string line in lines)
{
if (!line.StartsWith(begins))
{
writer.WriteLine(buffer + line);
buffer = null;
}
else
{
if (buffer != null)
writer.WriteLine(buffer);
buffer = line;
}
}
if(buffer != null)
Console.Out.WriteLine(buffer);
}
}

Categories

Resources