How to change some text in a file by StreamWriter - c#

I'm trying to change some text in a .CSV file.
StreamReader sReader = new StreamReader(path1);
while (sReader.Peek() != -1)
{
rowValue = sReader.ReadLine();
if (rowValue == "25")
{
sWriter = new StreamWriter(path1);
rowValue = "27";
sWriter.WriteLine(rowValue);
}
}
Nothing happened. What is the correct way, please?

You can easily accomplish this task with System.IO.File.ReadAllLines and System.IO.File.WriteAllLines
string[] lines = File.ReadAllLines(path1);
for(int i = 0; i < lines.Length; i++)
{
if(lines[i] == "25") lines[i] = "27";
}
File.WriteAllLines(path1, lines);

I don't believe you can do what you're trying to do ... updating a single line. If it's working at all, you're appending the value 27 to the end of your file.
Check out the ReadAllLines and WriteAllLines methods. You need to read the entire file in, change the line(s) you want to change, then write it back.

Well, if the file to read is not a very big one, you could try to read everything in memory and write back
string[] lines = File.ReadAllLines(path1);
using(StreamWrite sw = new StreamWriter(path1))
{
foreach(string line in lines)
{
string lineOut = line;
if (line == "25")
lineOut = "27";
sw.WriteLine(lineOut);
}
sw.Flush();
}

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.

How to terminate reading from text file when file is empty

When I run my program and there is data in my text file it works as I intended. However, the program crashes if the text file is empty. Therefore, I was wondering how I remedy this issue.
Here is my code for reading from text file:
//This method reads the order data from a text file and assigns the values to each object's variables
void Read_Order_Data()
{
FileStream fs = new FileStream("i:\\OrderData.txt", FileMode.Open, FileAccess.Read);
StreamReader reader = new StreamReader(fs);
while (!reader.EndOfStream)
{
string line = reader.ReadLine();
string[] fields = line.Split('#');
Order[Number_Of_Orders].Order_Number = fields[0];
Order[Number_Of_Orders].Type_Of_Bean = fields[1];
Order[Number_Of_Orders].Quantity_Of_Order = fields[2];
Order[Number_Of_Orders].Date_Of_Purchase = fields[3];
Number_Of_Orders++;
}//end of while statement
reader.Close();
}//end of Read_Order_Data()
If the text file is empty my program stops on this line:
Order[Number_Of_Orders].Type_Of_Bean = fields[1];
When it stops it says Type_Of_Bean is null.
Thanks for any assistance you can offer.
Change your while loop a bit:
string line;
while ((line = reader.ReadLine()) != null)
{
string[] fields = line.Split('#');
...
}
line will be null if the end of the input stream has been reached.
A simpler option may be to use Peek(). I modified the MSDN's example for StreamReader a tiny bit:
string line;
while (reader.Peek() >= 0)
{
line = reader.ReadLine();
...
}
Peek() will return -1 if there are no characters to be read (empty file).
And as others have suggested, make sure your split string array result has the number of fields you plan on indexing. If it doesn't, it might suggest a problem with the file you're reading.
Replace This:
string[] fields = line.Split('#');
With This:
string[] fields = line.Split(new []{'#'},StringSplitOptions.RemoveEmptyEntries);
You should add checking for length of fields in your code, like this:
...
string[] fields = line.Split(new[] {'#'}, StringSplitOptions.RemoveEmptyEntries);
if (fields.Length < 4) break;
...
you can check if field have the correct length
void Read_Order_Data()
{
FileStream fs = new FileStream("i:\\OrderData.txt", FileMode.Open, FileAccess.Read);
StreamReader reader = new StreamReader(fs);
while (!reader.EndOfStream)
{
string line = reader.ReadLine();
string[] fields = line.Split('#');
if (fields.length == 4){
Order[Number_Of_Orders].Order_Number = fields[0];
Order[Number_Of_Orders].Type_Of_Bean = fields[1];
Order[Number_Of_Orders].Quantity_Of_Order = fields[2];
Order[Number_Of_Orders].Date_Of_Purchase = fields[3];
Number_Of_Orders++;
}
}//end of while statement
reader.Close();
}//end of Read_Order_Data()
You can also do
string[] lines = System.IO.File.ReadAllLines("File");
foreach (var line in lines)
{
string[] fields = line.Split('#');
}
If you file is empty, then the following line
string[] fields = line.Split('#');
will give you a string array with only one element (empty string).
Which means that when trying to access the second element (fields[1]), your code will raise an exception.
Short story: you should check the size of the fields array before accessing it.
You can enumerate a line at a time without having to buffer the entire contents like so:
foreach (string line in File.ReadLines("i:\\OrderData.txt"))
{
string[] fields = line.Split('#');
if (fields.Length < 4) // You might also need to add this.
continue;
...
If the file is empty, it will not even enter the body of the loop. It also does not make a copy of the entire file in memory - only one line is buffered in memory at once.

Deleting some content from the text file in c#

I have text file called Load.txt which contains approximately 200 lines. I have a checkbox, If that is checked then I want to create a new file which had only first 100 lines from the Load.txt. And I am using c# for this program. Actually my real requirement is that I have to delete from line 110 to 201.And my code is below and because of some reason its deleting from line 1 to 92. I dnt know whats happening.
String line = null;
String tempFile = Path.GetTempFileName();
String filePath = saveFileDialog1.FileName;
int line_number = 110;
int lines_to_delete = 201;
using (StreamReader reader = new StreamReader(sqlConnectionString))
{
using (StreamWriter writer = new StreamWriter(saveFileDialog1.FileName))
{
while ((line = reader.ReadLine()) != null)
{
line_number++;
if (line_number <= lines_to_delete)
continue;
writer.WriteLine(line);
}
}
}
So I figured out this issue. But my next issue is that: I am updating some of variables in the text file. Before my that code was alright . But now That code is conflicting with my delete lines code. If I am able to delete lines then I m not able to update those variables.
My Code is:
File.WriteAllLines(saveFileDialog1.FileName, System.IO.File.ReadLine(sqlConnectionString).Take(110));
File.WriteAllText(saveFileDialog1.FileName, fileContents);
File.WriteAllLines("new.txt", File.ReadLines("Load.txt").Take(100));
After update...
var desired = File.ReadLines("Load.txt")
.Take(110) // "And I want to keep 1-110" -- OP
.Select(line => UpdateLine(line)); // "And I also want to update variables between 1-110" -- OP
File.WriteAllLines("new.txt", desired);
...
static string UpdateLine(string given)
{
var updated = given;
// other ops
return updated;
}
MSDN File.WriteAllLines
MSDN File.ReadLines
THis should accomplish what you need. It reads the text then dumps 100 lines of it.
System.IO.File.WriteAllLines("newLoad.txt", System.IO.File.ReadLines("Load.txt").Take(100));
"I want to create a new file which had only first 100 lines"
Keeping with your original model, here's one way to keep just the first 100 lines:
int LinesToKeep = 100;
using (StreamReader reader = new StreamReader(sqlConnectionString))
{
using (StreamWriter writer = new StreamWriter(saveFileDialog1.FileName))
{
for (int i = 1; (i <= LinesToKeep) && ((line = reader.ReadLine()) != null); i++)
{
writer.WriteLine(line);
}
}
}
"my real requirement is that I have to delete from line 110 to 201"
So copy the file, but skip lines 110 to 201?
int currentLine = 0;
using (StreamReader reader = new StreamReader(sqlConnectionString))
{
using (StreamWriter writer = new StreamWriter(saveFileDialog1.FileName))
{
while ((line = reader.ReadLine()) != null)
{
currentLine++;
if (currentLine < 110 || currentLine > 201)
{
writer.WriteLine(line);
}
}
}
}

Edit a specific Line of a Text File in C#

I have two text files, Source.txt and Target.txt. The source will never be modified and contain N lines of text. So, I want to delete a specific line of text in Target.txt, and replace by an specific line of text from Source.txt, I know what number of line I need, actually is the line number 2, both files.
I haven something like this:
string line = string.Empty;
int line_number = 1;
int line_to_edit = 2;
using StreamReader reader = new StreamReader(#"C:\target.xml");
using StreamWriter writer = new StreamWriter(#"C:\target.xml");
while ((line = reader.ReadLine()) != null)
{
if (line_number == line_to_edit)
writer.WriteLine(line);
line_number++;
}
But when I open the Writer, the target file get erased, it writes the lines, but, when opened, the target file only contains the copied lines, the rest get lost.
What can I do?
the easiest way is :
static void lineChanger(string newText, string fileName, int line_to_edit)
{
string[] arrLine = File.ReadAllLines(fileName);
arrLine[line_to_edit - 1] = newText;
File.WriteAllLines(fileName, arrLine);
}
usage :
lineChanger("new content for this line" , "sample.text" , 34);
You can't rewrite a line without rewriting the entire file (unless the lines happen to be the same length). If your files are small then reading the entire target file into memory and then writing it out again might make sense. You can do that like this:
using System;
using System.IO;
class Program
{
static void Main(string[] args)
{
int line_to_edit = 2; // Warning: 1-based indexing!
string sourceFile = "source.txt";
string destinationFile = "target.txt";
// Read the appropriate line from the file.
string lineToWrite = null;
using (StreamReader reader = new StreamReader(sourceFile))
{
for (int i = 1; i <= line_to_edit; ++i)
lineToWrite = reader.ReadLine();
}
if (lineToWrite == null)
throw new InvalidDataException("Line does not exist in " + sourceFile);
// Read the old file.
string[] lines = File.ReadAllLines(destinationFile);
// Write the new file over the old file.
using (StreamWriter writer = new StreamWriter(destinationFile))
{
for (int currentLine = 1; currentLine <= lines.Length; ++currentLine)
{
if (currentLine == line_to_edit)
{
writer.WriteLine(lineToWrite);
}
else
{
writer.WriteLine(lines[currentLine - 1]);
}
}
}
}
}
If your files are large it would be better to create a new file so that you can read streaming from one file while you write to the other. This means that you don't need to have the whole file in memory at once. You can do that like this:
using System;
using System.IO;
class Program
{
static void Main(string[] args)
{
int line_to_edit = 2;
string sourceFile = "source.txt";
string destinationFile = "target.txt";
string tempFile = "target2.txt";
// Read the appropriate line from the file.
string lineToWrite = null;
using (StreamReader reader = new StreamReader(sourceFile))
{
for (int i = 1; i <= line_to_edit; ++i)
lineToWrite = reader.ReadLine();
}
if (lineToWrite == null)
throw new InvalidDataException("Line does not exist in " + sourceFile);
// Read from the target file and write to a new file.
int line_number = 1;
string line = null;
using (StreamReader reader = new StreamReader(destinationFile))
using (StreamWriter writer = new StreamWriter(tempFile))
{
while ((line = reader.ReadLine()) != null)
{
if (line_number == line_to_edit)
{
writer.WriteLine(lineToWrite);
}
else
{
writer.WriteLine(line);
}
line_number++;
}
}
// TODO: Delete the old file and replace it with the new file here.
}
}
You can afterwards move the file once you are sure that the write operation has succeeded (no excecption was thrown and the writer is closed).
Note that in both cases it is a bit confusing that you are using 1-based indexing for your line numbers. It might make more sense in your code to use 0-based indexing. You can have 1-based index in your user interface to your program if you wish, but convert it to a 0-indexed before sending it further.
Also, a disadvantage of directly overwriting the old file with the new file is that if it fails halfway through then you might permanently lose whatever data wasn't written. By writing to a third file first you only delete the original data after you are sure that you have another (corrected) copy of it, so you can recover the data if the computer crashes halfway through.
A final remark: I noticed that your files had an xml extension. You might want to consider if it makes more sense for you to use an XML parser to modify the contents of the files instead of replacing specific lines.
When you create a StreamWriter it always create a file from scratch, you will have to create a third file and copy from target and replace what you need, and then replace the old one.
But as I can see what you need is XML manipulation, you might want to use XmlDocument and modify your file using Xpath.
You need to Open the output file for write access rather than using a new StreamReader, which always overwrites the output file.
StreamWriter stm = null;
fi = new FileInfo(#"C:\target.xml");
if (fi.Exists)
stm = fi.OpenWrite();
Of course, you will still have to seek to the correct line in the output file, which will be hard since you can't read from it, so unless you already KNOW the byte offset to seek to, you probably really want read/write access.
FileStream stm = fi.Open(FileMode.OpenOrCreate, FileAccess.ReadWrite, FileShare.None);
with this stream, you can read until you get to the point where you want to make changes, then write. Keep in mind that you are writing bytes, not lines, so to overwrite a line you will need to write the same number of characters as the line you want to change.
I guess the below should work (instead of the writer part from your example). I'm unfortunately with no build environment so It's from memory but I hope it helps
using (var fs = File.Open(filePath, FileMode.Open, FileAccess.ReadWrite)))
{
var destinationReader = StreamReader(fs);
var writer = StreamWriter(fs);
while ((line = reader.ReadLine()) != null)
{
if (line_number == line_to_edit)
{
writer.WriteLine(lineToWrite);
}
else
{
destinationReader .ReadLine();
}
line_number++;
}
}
The solution works fine. But I need to change single-line text when the same text is in multiple places. For this, need to define a trackText to start finding after that text and finally change oldText with newText.
private int FindLineNumber(string fileName, string trackText, string oldText, string newText)
{
int lineNumber = 0;
string[] textLine = System.IO.File.ReadAllLines(fileName);
for (int i = 0; i< textLine.Length;i++)
{
if (textLine[i].Contains(trackText)) //start finding matching text after.
traced = true;
if (traced)
if (textLine[i].Contains(oldText)) // Match text
{
textLine[i] = newText; // replace text with new one.
traced = false;
System.IO.File.WriteAllLines(fileName, textLine);
lineNumber = i;
break; //go out from loop
}
}
return lineNumber
}

Categories

Resources