Add a line in a txt file - c#

I have a txt file with data such as the following:
:10FF800000040B4E00040B4E00047D1400047D148D
:10FF900000040B4E0004CF6200040B4E00040B4E15
:10FFA00000040B4E00040B4E00040B4E00040B4EDD
:10FFB00000047D1400047D1400047D1400047D14ED
:10FFC00000040B4E000000000000000000000000D4
:10FFD0000000000000040B4E0000000000000000C4
:10FFE0000000000000000000000000000000000011
:10FFF0000000000000000000060000000000BFF844
:020000020000FC
:020000040014E6
:043FF0005AC8A58C7A
:00000001FF
what I want to do with my C# program is to add a line after or before a specific line, lets say add the line:
:020000098723060
before this line:
:020000020000FC
I have tried using the File.ReadLines("file.txt").Last(); but that just gives me the last one, what if i want the third or fourth? also, is there any way to identify the ":" in the file?

The simplest way - if you're happy to read the whole file into memory - would be just:
public void InsertLineBefore(string file, string lineToFind, string lineToInsert)
{
List<string> lines = File.ReadLines(file).ToList();
int index = lines.IndexOf(lineToFind);
// TODO: Validation (if index is -1, we couldn't find it)
lines.Insert(index, lineToInsert);
File.WriteAllLines(file, lines);
}
public void InsertLineAfter(string file, string lineToFind, string lineToInsert)
{
List<string> lines = File.ReadLines(file).ToList();
int index = lines.IndexOf(lineToFind);
// TODO: Validation (if index is -1, we couldn't find it)
lines.Insert(index + 1, lineToInsert);
File.WriteAllLines(file, lines);
}
There are significantly more efficient ways of doing this, but this approach is really simple.

A brute force approach
string[] lines = File.ReadAllLines("file.txt");
using(StreamWrite sw = new StreamWriter("file.txt"))
{
foreach(string line in lines)
{
if(line == ":020000020000FC")
sw.WriteLine(":020000098723060");
sw.WriteLine(line);
}
}

I would say it is better to read and write line by line, especially if the target file tend to be of large size:
using (StreamReader r = new StreamReader("Test.txt"))
{
using (StreamWriter w = new StreamWriter("TestOut.txt"))
{
while (!r.EndOfStream)
{
string line = r.ReadLine();
w.WriteLine(line);
if (line == ":020000020000FC")
w.WriteLine(":020000098723060");
}
w.Close();
r.Close();
}
}

Not sure if you're trying to avoid reading the entire file in due to size, etc...but can't you just read the file and then replace...e.g.
var text = readFile(somePath);
writeFile( text.replace(":020000020000FC\n",":020000098723060\n:020000020000FC\n") , somePath);

Here is a solution, though it may not be the best, it does work:
public void AddTextToFile(string filePath, int lineNumber, string txt) //zero based lineNumber
{
Collection<string> newLines = new Collection<string>(File.ReadAllLines(filePath).ToList());
if (lineNumber < newLines.Count)
newLines.Insert(lineNumber, txt);
else
newLines.Add(txt);
using (StreamWriter writer = new StreamWriter(filePath, false))
{
foreach (string s in newLines)
writer.WriteLine(s);
}
}
And to answer your question about determining if ":" exists in a string, the answer is yes, in the example above, you could check if the line contains it by...
if(newLines[idx].Contains(':'))
//do something

The ":" character doesn't really help the implementation, the lines are all newline-delimited already.
Here's an attempt at a method that doesn't load it all to memory or output to a different file.
Never cross the streams.
static Int32 GetCharPos(StreamReader s)
{
var ia = BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.GetField;
Int32 charpos = (Int32)s.GetType().InvokeMember("charPos", ia, null, s, null);
Int32 charlen = (Int32)s.GetType().InvokeMember("charLen", ia, null, s, null);
return (Int32)s.BaseStream.Position - charlen + charpos;
}
static void Appsert(string data, string precedingEntry = null)
{
if (precedingEntry == null)
{
using (var filestream = new FileStream(dataPath, FileMode.Append))
using (var tw = new StreamWriter(filestream))
{
tw.WriteLine(data);
return;
}
}
int seekPos = -1;
using (var readstream = new FileStream(dataPath,
FileMode.Open, FileAccess.Read, FileShare.Write))
using (var writestream = new FileStream(dataPath,
FileMode.Open, FileAccess.Write, FileShare.Read))
using (var tr = new StreamReader(readstream))
{
while (seekPos == -1)
{
var line = tr.ReadLine();
if (line == precedingEntry)
seekPos = GetCharPos(tr);
else if (tr.EndOfStream)
seekPos = (int)readstream.Length;
}
writestream.Seek(seekPos, SeekOrigin.Begin);
readstream.Seek(seekPos, SeekOrigin.Begin);
int readLength = 0;
var readBuffer = new byte[4096];
var writeBuffer = new byte[4096];
var writeData = tr.CurrentEncoding.GetBytes(data + Environment.NewLine);
int writeLength = writeData.Length;
writeData.CopyTo(writeBuffer, 0);
while (true & writeLength > 0)
{
readLength = readstream.Read(readBuffer, 0, readBuffer.Length);
writestream.Write(writeBuffer, 0, writeLength);
var tmp = writeBuffer;
writeBuffer = readBuffer;
writeLength = readLength;
readBuffer = tmp;
}
}
}

Related

StreamWriter: Starting and ending on a specific line number

I would like to ask some tips and help on a reading/writing part of my C#.
Situation:
I have to read a CSV file; - OK
If the CSV file name starts with "Load_", I want to write on another CSV the data from line 2 to the last one;
If the CSV file name starts with "RO_", I want to write on 2 different CSVs, 1 with the line 1 to 4 and the other 4 to the last one;
What I have so far is:
public static void ProcessFile(string[] ProcessFile)
{
// Keeps track of your current position within a record
int wCurrLine = 0;
// Number of rows in the file that constitute a record
const int LINES_PER_ROW = 1;
int ctr = 0;
foreach (string filename in ProcessFile)
{
var sbText = new System.Text.StringBuilder(100000);
int stop_line = 0;
int start_line = 0;
// Used for the output name of the file
var dir = Path.GetDirectoryName(filename);
var fileName = Path.GetFileNameWithoutExtension(filename);
var ext = Path.GetExtension(filename);
var folderbefore = Path.GetFullPath(Path.Combine(dir, #"..\"));
var lineCount = File.ReadAllLines(#filename).Length;
string outputname = folderbefore + "output\\" + fileName;
using (StreamReader Reader = new StreamReader(#filename))
{
if (filename.Contains("RO_"))
{
start_line = 1;
stop_line = 5;
}
else
{
start_line = 2;
stop_line = lineCount;
}
ctr = 0;
while (!Reader.EndOfStream && ctr < stop_line)
{
// Add the text
sbText.Append(Reader.ReadLine());
// Increment our current record row counter
wCurrLine++;
// If we have read all of the rows for this record
if (wCurrLine == LINES_PER_ROW)
{
// Add a line to our buffer
sbText.AppendLine();
// And reset our record row count
wCurrLine = 0;
}
ctr++;
} // end of the while
}
int total_lenght = sbText.Length
// When all of the data has been loaded, write it to the text box in one fell swoop
using (StreamWriter Writer = new StreamWriter(dir + "\\" + "output\\" + fileName + "_out" + ext))
{
Writer.Write.(sbText.);
}
} // end of the foreach
} // end of ProcessFile
I was thinking about using the IF/ELSE: "using (StreamWriter Writer = new StreamWriter(dir + "\" + "output\" + fileName + "_out" + ext))" part. However, I am not sure how to pass, to StreamWriter, to only write from/to a specific line number.
Any Help is welcome! If I am missing some information, please, let me know (I am pretty new on stackoverflow).
Thank you.
Code is way too complicated
using System.Collections.ObjectModel;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
namespace ConsoleApplication57
{
class Program
{
static void Main(string[] args)
{
}
public static void ProcessFile(string[] ProcessFile)
{
foreach (string filename in ProcessFile)
{
// Used for the output name of the file
var dir = Path.GetDirectoryName(filename);
var fileName = Path.GetFileNameWithoutExtension(filename);
var ext = Path.GetExtension(filename);
var folderbefore = Path.GetFullPath(Path.Combine(dir, #"..\"));
var lineCount = File.ReadAllLines(#filename).Length;
string outputname = folderbefore + "output\\" + fileName;
using (StreamWriter Writer = new StreamWriter(dir + "\\" + "output\\" + fileName + "_out" + ext))
{
int rowCount = 0;
using (StreamReader Reader = new StreamReader(#filename))
{
rowCount++;
string inputLine = "";
while ((inputLine = Reader.ReadLine()) != null)
{
if (filename.Contains("RO_"))
{
if (rowCount <= 4)
{
Writer.WriteLine(inputLine);
}
if (rowCount == 4) break;
}
else
{
if (rowCount >= 2)
{
Writer.WriteLine(inputLine);
}
}
} // end of the while
Writer.Flush();
}
}
} // end of the foreach
} // end of ProcessFile
}
}
You can use LINQ to Take and Skip lines.
public abstract class CsvProcessor
{
private readonly IEnumerable<string> processFiles;
public CsvProcessor(IEnumerable<string> processFiles)
{
this.processFiles = processFiles;
}
protected virtual IEnumerable<string> GetAllLinesFromFile(string fileName)
{
using(var stream = new FileStream(fileName, FileMode.Open, FileAccess.Read, FileShare.Read))
using(var reader = new StreamReader(stream))
{
var line = String.Empty;
while((line = reader.ReadLine()) != null)
{
yield return line;
}
}
}
protected virtual void ProcessFiles()
{
var sb1 = new StringBuilder();
var sb2 = new StringBuilder();
foreach(var file in this.processFiles)
{
var fileName = Path.GetFileNameWithoutExtension(file);
var lines = GetAllLinesFromFile(file);
if(fileName.StartsWith("RO_", StringComparison.InvariantCultureIgnoreCase))
{
sb1.AppendLine(lines.Take(4)); //take only the first four lines
sb2.AppendLine(lines.Skip(4).TakeWhile(s => !String.IsNullOrEmpty(s))); //skip the first four lines, take everything else
}
else if(fileName.StartsWith("Load_", StringComparison.InvariantCultureIgnoreCase)
{
sb2.AppendLine(lines.Skip(1).TakeWhile(s => !String.IsNullOrEmpty(s)));
}
}
// now write your StringBuilder objects to file...
}
protected virtual void WriteFile(StringBuilder sb1, StringBuilder sb2)
{
// ... etc..
}
}

c# Write a line into a text file at specific line number

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.

Csharp substring text and add it to list

I have file.txt like:
EDIT: I didn't wrote but this is important i guess- In file.txt there can be others lines!
folder=c:\user;c:\test;c:\something;
I need to add one path like one list item (List<string> Folders).
So my List should looks like:
Folders[0] = c:\user
Folders[1] = c:\test
etc. (without text "folder=" which starts line in file.txt and ";" which means end of path).
file can contain much more paths.
I did something like this:
using (FileStream fss = new FileStream(path, FileMode.Open))
{
using (StreamReader sr = new StreamReader(fss))
{
while (sr.EndOfStream == false)
{
string line = sr.ReadLine();
if(line.StartsWith("folders"))
{
int index = line.IndexOf("=");
int index1 = line.IndexOf(";");
string folder = line.Substring(index + 1, index1 - (index + 1));
Folders.Add(folder);
Now in List Folders i have first path but what now? I can't go ahead :(
using(var sr = new StreamReader(path))
{
var folders = sr.ReadToEnd()
.Split(new char[]{';','\n','\r'}, StringSplitOptions.RemoveEmptyEntries)
.Select(o => o.Replace("folder=",""))
.ToArray();
Folders.AddRange(folders);
}
You can try following code, using File.ReadAllText
string Filepath = "c:\abc.txt";
string filecontent = File.ReadAllText(Filepath);
string startingString = "=";
var startIndex = filecontent.IndexOf(startingString);
filecontent = filecontent.Substring(startIndex + 1, filecontent.Length - startIndex - 2);
List<String> folders = filecontent.Split(';').ToList();
Here's a simple example:
List<String> Folders = new List<string>();
private void button1_Click(object sender, EventArgs e)
{
string path = #"C:\Users\mikes\Documents\SomeFile.txt";
string folderTag = "folder=";
using (FileStream fss = new FileStream(path, FileMode.Open))
{
using (StreamReader sr = new StreamReader(fss))
{
while (!sr.EndOfStream)
{
string line = sr.ReadLine();
if (line.StartsWith(folderTag))
{
line = line.Substring(folderTag.Length); // remove the folderTag from the beginning
Folders.AddRange(line.Split(";".ToCharArray(), StringSplitOptions.RemoveEmptyEntries));
}
}
}
}
foreach(string folder in Folders)
{
Console.WriteLine(folder);
}
}
I'd use this approach if you're going to read line by line, and do something else based on what each line starts with. In that case you could add different else if(...) blocks:
if (line.StartsWith(folderTag))
{
line = line.Substring(folderTag.Length); // remove the folderTag from the beginning
Folders.AddRange(line.Split(";".ToCharArray(), StringSplitOptions.RemoveEmptyEntries));
}
else if(line.StartsWith("parameters="))
{
// do something different with a line starting with "parameters="
}
else if (line.StartsWith("unicorns="))
{
// do something else different with a line starting with "unicorns="
}

writing to text file does not alway work/save

i have this code that compares two text files and write the difference to a log file but for some reason the log.txt file is some times blank even when is test with some lines starting with a * these are not always written either do I have to save the text file when finished writing although this does not explain why sometimes it works any help would be great
private void compare()
{
string FilePath = #"c:\snapshot\1.txt";
string Filepath2 = #"c:\snapshot\2.txt";
int counter = 0;
string line;
string line2;
var dir = "c:\\snapshot\\log.txt";
using (FileStream fs = File.Create(dir))
{
fs.Dispose();
}
StreamWriter dest = new StreamWriter(dir);
if (File.Exists(FilePath) & File.Exists(Filepath2))
{
// Read the file and display it line by line.
using (var file = File.OpenText(FilePath))
using (var file2 = File.OpenText(Filepath2))
{
while (((line = file.ReadLine()) != null & (line2 = file2.ReadLine()) != null))
{
if (line.Contains("*"))
{
dest.WriteLine(line2);
}
else if (!line.Contains(line2))
{
dest.WriteLine(line2);
}
counter++;
}
}
}
dest.Close();
}
Everything left in the buffer should be written out once you hit the close statement on your StreamReader. If you are missing stuff then it might be that you aren't reaching that line for some reason (i.e. you crash). Also, if you are trying to look at the file while it's being written (i.e. while the program is still running), you won't necessarily see everything (since it hasn't closed).
Generally, it's better to use a using statement with the StreamReader. That should ensure that it always gets closed.
private void compare()
{
string FileName1 = #"c:\snapshot\1.txt";
string FileName2 = #"c:\snapshot\2.txt";
string FileNameOutput = #"c:\snapshot\log.txt"; //dir ???
int counter = 0; // um what's this for you aren't using it.
using (FileStream fso = new FileStream(FileNameOutput, FileMode.Create, FileAccess.Write))
{
TextWriter dest = new StreamWriter(fso);
using(FileStream fs1 = new FileStream(FileName1, FileMode.Open, FileAccess.Read))
{
using (FileStream fs2 = new FileStream(FileName2, FileMode.Open, FileAccess.Read))
{
TextReader firstFile = new StreamReader(fs1);
TextReader secondFile = new StreamReader(fs2);
while (((line1 = firstFile.ReadLine()) != null & (line2 = secondFile.ReadLine()) != null))
{
if ((line1.Contains("*") || (!line1.Contains(line2)))
{
dest.Write(line2); // Writeline would give you an extra line?
}
counter++; //
}
}
}
fso.Flush();
}
I commend the overloads of FileStream to you. Do it the way I have and the code will crash at the point you instance the stream if the user running it doesn't have all the required permissions. It's a nice way of showing what you intend, and what you don't.
PS You do know contains is case and culture sensitive?
Not sure if I'm understanding your comparing logic right, but as long as I separated comparing from whole code you can adjust it to your own needs:
public static void WriteDifferences(string sourcePath, string destinationPath, string differencesPath)
{
var sourceLines = File.ReadAllLines(sourcePath).ToList();
var destinationLines = File.ReadAllLines(destinationPath).ToList();
// make lists equal size
if (sourceLines.Count > destinationLines.Count)
{
destinationLines.AddRange(Enumerable.Range(0, sourceLines.Count - destinationLines.Count).Select(x => (string)null));
}
else
{
sourceLines.AddRange(Enumerable.Range(0, destinationLines.Count - sourceLines.Count).Select(x => (string)null));
}
var differences = sourceLines.Zip(destinationLines, (source, destination) => Compare(source, destination));
File.WriteAllLines(differencesPath, differences.Where(x => x != null));
}
private static string Compare(string source, string destination)
{
return !source.Contains(destination) || source.Contains("*") ? destination : null;
}

Using file.Close() with StreamWriter

I am having problems trying to use the file.Close with StreamWriter in this method, it doesn't seem to like it. Could someone demonstrate how this could be done. (The reason so, is that another method accesses a file being used and hence can't, because the file is still in use by the other method.)
Code so far:
private static void TrimColon()
{
using (StreamWriter sw = File.AppendText(#"process_trimmed.lst"))
{
StreamReader sr = new StreamReader(#"process_trim.lst");
string myString = "";
while (!sr.EndOfStream)
{
myString = sr.ReadLine();
int index = myString.LastIndexOf(":");
if (index > 0)
myString = myString.Substring(0, index);
sw.WriteLine(myString);
}
}
}
private static void TrimColon(String inputFilePath, String outputFilePath)
{
//Error checking file paths
if (String.IsNullOrWhiteSpace(inputFilePath))
throw new ArgumentException("inputFilePath");
if (String.IsNullOrWhiteSpace(outputFilePath))
throw new ArgumentException("outputFilePath");
//Check to see if files exist? - Up to you, I would.
using (var streamReader = File.OpenText(inputFilePath))
using (var streamWriter = File.AppendText(outputFilePath))
{
var text = String.Empty;
while (!streamReader.EndOfStream)
{
text = streamReader.ReadLine();
var index = text.LastIndexOf(":");
if (index > 0)
text = text.Substring(0, index);
streamWriter.WriteLine(text);
}
}
}
The StreamWriter is closed aswell as flushed due to the "using" statement. So no need to call close.

Categories

Resources