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
}
Related
I was searching the web but failed to find the correct example.
The goal is to have a function:
private void InsertLine(string source, string position, string content)
And write to a file using the StreamWriter so you do not need to read all lines because I the file is potentially huge.
My function so far:
private void InsertLine(string source, string position, string content)
{
if (!File.Exists(source))
throw new Exception(String.Format("Source:{0} does not exsists", source));
var pos = GetPosition(position);
int line_number = 0;
string line;
using (var fs = File.Open(source, FileMode.Open, FileAccess.ReadWrite))
{
var destinationReader = new StreamReader(fs);
var writer = new StreamWriter(fs);
while (( line = destinationReader.ReadLine()) != null)
{
if (line_number == pos)
{
writer.WriteLine(content);
break;
}
line_number++;
}
}
}
The function does not work in the file as nothing happens.
You can't just insert a line into a file. A file is a sequence of bytes.
You need to:
Write all of the preceding lines
Write the line to be inserted
Write all of the following lines
Here's some untested code based upon yours:
private void InsertLine(string source, string position, string content)
{
if (!File.Exists(source))
throw new Exception(String.Format("Source:{0} does not exsists", source));
// I don't know what all of this is for....
var pos = GetPosition(position);
int line_number = 0;
string line;
using (var fs = File.Open(source, FileMode.Open, FileAccess.ReadWrite))
{
var destinationReader = new StreamReader(fs);
var writer = new StreamWriter(fs);
while (( line = destinationReader.ReadLine()) != null)
{
writer.WriteLine(line); // ADDED: You need to write every original line
if (line_number == pos)
{
writer.WriteLine(content);
// REMOVED the break; here. You need to write all following lines
}
line_number++; // MOVED this out of the if {}. Always count lines.
}
}
}
This probably won't work as expected, however. You're trying to write to the same file you're reading. You should open a new (temporary) file, perform the copy + insert, and then move/rename the temporary file to replace the original 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.
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);
}
}
}
}
I am trying to go line by line through text file using stream reader, and when I encounter specific text, I want to add a new line into that file. But when I do so, next line's first part(about half) gets cut off. I want to do that only once, that's why "skip" variable is there. Here is what I am doing:
var fileStream = new FileStream(fileName, FileMode.Open);
var writer = new StreamWriter(fileStream);
var reader = new StreamReader(fileName);
string line;
var skip = false;
using (writer) {
using (reader) {
while ((line = reader.ReadLine()) != null) {
if (line.Contains("some text") && !skip) {
writer.WriteLine("some text something else");
skip = true;
}
}
}
}
fileStream.Close();
Am I doing something wrong here?
EDIT: Aargh, I've just seen that you're trying to read and write to the same file.
You shouldn't do that - it's replacing the existing text, rather than inserting extra data. You can't just insert data within a file - that's not how file systems work.
Instead, read from one file, write to another file, and then move the files around afterwards if you need to.
I'd also suggest that you have using statements for the whole scope of a variable, rather than using them the way you have here. Something like this:
void CopyAndEdit(string inputFile, string outputFile)
{
// In .NET 4 you can use File.ReadLines which will
// return an IEnumerable<string>
using (TextReader reader = File.OpenText(inputFile))
{
using (TextWriter writer = File.CreateText(outputFile))
{
bool editing = true;
string line;
while ((line = reader.ReadLine()) != null)
{
writer.WriteLine(line);
if (editing && line.Contains("some text"))
{
editing = false;
writer.WriteLine("some text something else");
}
}
}
}
}
I have a simple log text file with the extension of .txt with a white space line at the end of that text file every time the log file is generated from a 3rd party program.
Therefore is there any methods or codes that I can utilize to delete the last line of the text file?
An example of the log text file:
Sun Jul 22 2001 02:37:46,73882,...b,r/rrwxrwxrwx,0,0,516-128-3,C:/WINDOWS/Help/digiras.chm
Sun Jul 22 2001 02:44:18,10483,...b,r/rrwxrwxrwx,0,0,480-128-3,C:/WINDOWS/Help/cyycoins.chm
Sun Jul 22 2001 02:45:32,10743,...b,r/rrwxrwxrwx,0,0,482-128-3,C:/WINDOWS/Help/cyzcoins.chm
Sun Jul 22 2001 04:26:14,174020,...b,r/rrwxrwxrwx,0,0,798-128-3,C:/WINDOWS/system32/spool/drivers/color/kodak_dc.icm
How about something like :
var lines = System.IO.File.ReadAllLines("...");
System.IO.File.WriteAllLines("...", lines.Take(lines.Length - 1).ToArray());
Explanation:
Technically, you don't delete a line from a file. You read the contents of a file and write them back excluding the content you want deleted.
What this code does is read all the lines into an array, and write these lines back to the file excluding only the last line. (Take() method (Part of LINQ) takes number of lines specified which in our case, is length - 1). Here, var lines can be read as String[] lines.
Use this method to remove the last line of the file:
public static void DeleteLastLine(string filepath)
{
List<string> lines = File.ReadAllLines(filepath).ToList();
File.WriteAllLines(filepath, lines.GetRange(0, lines.Count - 1).ToArray());
}
Edit: Realized the lines variable didn't exist previously, so I updated the code.
If you want to delete last N lines from a file without loading all into memory:
int numLastLinesToIgnore = 10;
string line = null;
Queue<string> deferredLines = new Queue<string>();
using (TextReader inputReader = new StreamReader(inputStream))
using (TextWriter outputReader = new StreamWriter(outputStream))
{
while ((line = inputReader.ReadLine()) != null)
{
if (deferredLines.Count() == numLastLinesToIgnore)
{
outputReader.WriteLine(deferredLines.Dequeue());
}
deferredLines.Enqueue(line);
}
// At this point, lines still in Queue get lost and won't be written
}
What happens is that you buffer each new line in a Queue with dimension numLastLinesToIgnore and pop from it a line to write only when Queue is full. You actually read-ahead the file and you are able to stop numLastLinesToIgnore lines before the end of file is reached, without knowing the total number of lines in advance.
Note that if text has less than numLastLinesToIgnore, result is empty.
I came up with it as a mirror solution to this:
Delete specific line from a text file?
You can't delete the line end, as File.WriteAllLines automatically adds it, however, you can use this method:
public static void WriteAllLinesBetter(string path, params string[] lines)
{
if (path == null)
throw new ArgumentNullException("path");
if (lines == null)
throw new ArgumentNullException("lines");
using (var stream = File.OpenWrite(path))
using (StreamWriter writer = new StreamWriter(stream))
{
if (lines.Length > 0)
{
for (int i = 0; i < lines.Length - 1; i++)
{
writer.WriteLine(lines[i]);
}
writer.Write(lines[lines.Length - 1]);
}
}
}
This is not mine, I found it at .NET File.WriteAllLines leaves empty line at the end of file
I just find a solution from other website, and this is the url https://stackoverrun.com/cn/q/627722 .
With a small number of lines, you could easily use something like this
string filename = #"C:\Temp\junk.txt";
string[] lines = File.ReadAllLines(filename);
However, as the files get larger, you may want to stream the data in and out with something like this
string filename = #"C:\Temp\junk.txt";
string tempfile = #"C:\Temp\junk_temp.txt";
using (StreamReader reader = new StreamReader(filename))
{
using (StreamWriter writer = new StreamWriter(tempfile))
{
string line = reader.ReadLine();
while (!reader.EndOfStream)
{
writer.WriteLine(line);
line = reader.ReadLine();
} // by reading ahead, will not write last line to file
}
}
File.Delete(filename);
File.Move(tempfile, filename);
That should be the best option for using with big files.
using (StreamReader source = new StreamReader(sourceFileName))
{
using (StreamWriter sw = new StreamWriter(newFileName))
{
do
{
line = source.ReadLine();
if (source.Peek() > -1)
{
sw.WriteLine(line);
}
else
{
File.AppendAllText("RemovedLastLine.txt", line);
}
}
while (line != null);
}
}