coroutine/reactive extensions - write lines - c#

I'm using the two functions to read and write huge files (write to multiple files). I want to keep the file operation in the functions because the lines may be read/write from other sources.
Update:
C# doesn't really have coroutine. Is it a good use case for Reactive extensions?
foreach (var line in ReadFrom("filename"))
{
try
{
.... // Some actions based on the line
var l = .....
WriteTo("generatedFile1", l);
}
catch (Exception e)
{
var l = ..... // get some data from line, e and other objects etc.
WriteTo("generatedFile2", l);
}
}
The following function open the file once until all the lines are read and then close and release the resource.
private static IEnumerable<string> ReadFrom(string file)
{
string line;
using (var reader = File.OpenText(file))
{
while ((line = reader.ReadLine()) != null)
yield return line;
}
}
However, the following function, which write the lines instead of read lines, open and close the file for each line it writes. Is it possible to implement it in a way so it only open the file once and continue to write to the file until EOF is sent?
private static void WriteTo(string file, string line)
{
if (!File.Exists(file)) // Remove and recreate the file if existing
using (var tw = File.CreateText(file))
{
tw.WriteLine(line);
}
else
using (var tw = new StreamWriter(file, true))
{
tw.WriteLine(line);
}
}

Just use File.WriteAllLines. It will write all of the lines in a sequence to a file, and it won't open/close the file for each line.

You can remove the entire second method, and replace the call with var writer = new StreamWriter(file, true), as that constructor creates the file if it does not exist.
You can then use writer.WriteLine() until you're done writing, and Dispose() it afterwards.

Related

Streamwriter will not overwrite file, keeps appending or writes only the first line

Been looking through other people's answers and nothing seems to work.
Here is my code:
public void TaskOnClick() //getting multi-values
{
foreach (string inputJson in File.ReadLines("Assets/Text/multi-import.txt"))
{
string temperature = GetTemperatureByRegex(inputJson);
Debug.Log(temperature);
string filename = "Assets/Text/TEMP/multi-export.txt";
{
using (StreamWriter writeFile = new StreamWriter(filename, false))
{
writeFile.AutoFlush = true;
Console.SetOut(writeFile);
writeFile.WriteLineAsync(temperature.ToString());
}
}
}
}
The idea is that my parsing script gets my data and then streamwriter writes the data to a txt file. Problem is that streamwriter keeps appending the txt file instead of overwriting the file.
Whenever I try to use filestream it overwrites the file, yes, but only the first line of the data gets written, no matter what I tried.
My username speaks for itself...
What you do wrong is creating StreamWriter inside a loop. If you provide an overwrite settings it will only write 1 line.
public void TaskOnClick() //getting multi-values
{
string filename = "Assets/Text/TEMP/multi-export.txt";
using (StreamWriter writeFile = new StreamWriter(filename, false))
{
foreach (string inputJson in File.ReadLines("Assets/Text/multi-import.txt"))
{
string temperature = GetTemperatureByRegex(inputJson);
Debug.Log(temperature);
writeFile.AutoFlush = true;
Console.SetOut(writeFile);
writeFile.WriteLine(temperature.ToString());
}
}
}
But there is a shorter way of doing this with the help of LINQ.
public void TaskOnClick() //getting multi-values
{
string filename = "Assets/Text/TEMP/multi-export.txt";
var tempratures = File.ReadAllLines("Assets/Text/multi-import.txt")
.Select(GetTemperatureByRegex).ToArray();
File.WriteAllLines(filename,tempratures); // it creates a new file or overwrites
}
I have to state that above method may be dangerous if input file is too large. Because it reads entire file into memory.
Why don’t you just delete the file before opening the stream writer?
if(File.Exists(filename)){
File.Delete(filename);
}
//here whatever you need to do next

Why is my method adding an extra blank line at the end?

I use the below method to remove all blank lines from a file but it is for some reason adding an extra line at the end of the document?
var tempFileName = Path.GetTempFileName();
try
{
using (var streamReader = new StreamReader(file))
using (var streamWriter = new StreamWriter(tempFileName))
{
string line;
while ((line = streamReader.ReadLine()) != null)
{
if (!string.IsNullOrWhiteSpace(line))
streamWriter.WriteLine(line);
}
}
File.Copy(tempFileName, file, true);
}
finally
{
File.Delete(tempFileName);
}
How do I fix this?
Also can the code be made shorter?
Also can the code be made shorter?
A more efficient solution to the other answer:
File.WriteAllLines(file, File.ReadLines("some/path").Where(l => !string.IsNullOrWhiteSpace(l)));
File.ReadLines() is more efficient than File.ReadAllLines() because it allows you to query the IEnumerable<string> without reading it all into memory first.
We then take the resulting IEnumerable<string> from our Where() method and pass it to an overload of File.WriteAllLines() which takes an IEnumerable<string> as its second parameter.
StreamWriter.WriteLine() will always append a new line (a carriage return, line feed pair) after the string, so I assume that's what you're referring to. To me, it's normal and best to have the last line followed by a new line. But if you don't want it, don't write one.
As far as the length of your code, it looks about right to me. If it seems to detract from your program logic, just move it into its own method.
For efficiency, you might try deleting the original file and then renaming the temporary file. That would be more efficient than copying the entire file.
var tempFileName = Path.GetTempFileName();
try
{
using (var streamReader = new StreamReader(file))
using (var streamWriter = new StreamWriter(tempFileName))
{
string line;
bool isFirstLine = true;
while ((line = streamReader.ReadLine()) != null)
{
if (!string.IsNullOrWhiteSpace(line))
{
if (!isFirstLine)
streamWrite.WriteLine();
streamWriter.Write(line);
isFirstLine = false;
}
}
}
File.Delete(file);
File.Move(tempFileName, file);
}
finally
{
File.Delete(tempFileName);
}
The problem is that StreamReader.ReadLine does not return the line breaks themselves, thus destroying line break information. In other words, those two input files:
File 1: A\r\nB\r\n
File 2: A\r\nB
Will yield the same input to your method. You can't determine that way whether there was a final line break or not.
If you never want a final line break, use Write instead of WriteLine and manually add a line break at the beginning of your loop in every iteration except for the first one:
...
string line;
bool first = true;
while ((line = streamReader.ReadLine()) != null)
{
if (string.IsNullOrWhiteSpace(line))
continue;
if (!first)
streamWriter.WriteLine();
streamWriter.Write(line);
first = false;
}
...

File is already use in some other process

Here I want to delete line in a textfiles containg specific string like "21309#003" where item1 is a filename but It shows runtime exception that item1 (file) is already use in some process.How I Solve this problem.I am new in .net C#.
private void button1_Click(object sender, EventArgs e)
{
var selectedItems = listBox1.SelectedItems.Cast<String>().ToList();
foreach (var item in selectedItems)
{
listBox1.Items.Remove(item);
}
foreach (var item1 in selectedItems)
{
listBox1.Items.Remove(item1);
string line = null;
//string line_to_delete = "the line i want to delete";
using (StreamReader reader = new StreamReader(item1))
//item1= "C:\\IMP2711\\textpresent.txt"
{
using (StreamWriter writer = new StreamWriter(item1))
{
while ((line = reader.ReadLine()) != null)
{
//if (String.Compare(line, #"*21349#003*") == 0)
//if (!line.Contains("21349#003") )
if (!line.StartsWith("21349#003"))
{**strong text**
writer.WriteLine(line);
}
}
}
You are reading and writing to the same file at the same time.
var item2 = item1;
If the file is not to big you can read the lines into memory and then write the lines you want to keep back to the file. We can even simplify your code a little bit.
File.WriteAllLines(item1,
File.ReadLines(item1).Where(l => !l.StartsWith("21349#003")).ToList());
Another option if the file is very large is to write to a temporary file. Delete the original and then rename the temporary.
var tmp = Path.GetTempFileName();
File.WriteAllLines(tmp, File.ReadLines(item1).Where(l => !l.StartsWith("21349#003")));
File.Delete(item1);
File.Move(tmp, item1);
If your file is small first read it to memory and then try to write on it, you have two stream on the same file, a file can share between multiple streams but you can not modify a file when it is open by another stream, if your file is huge and you can not moved to memory you can create a temp file and write to temp file when your reading finished replacing original file with temp file and removing temp file.
There's some process that's locking the file c:\imp2711\textpresent.txt. You have to find and kill it.
To find it out, please refer to this question: https://superuser.com/questions/117902/find-out-which-process-is-locking-a-file-or-folder-in-windows

Why my List<string> isn't totaly written in my .CSV?

I'm trying to write a .csv file in C# from a list<string> with roughly 350 lines (for 13 columns).
I write in the file with a loop but only a part of my list is written in the file (206 lines and a half).
This is my code :
StreamWriter file = new StreamWriter(#"C:\test.csv", true);
foreach (string s in MyListString)
{
Console.WriteLine(s); // Display all the data
file.WriteLine(s); // Write only a part of it
}
Why my file isn't properly filled ? Is there any limit to be considered ?
Looks like you might need to Flush or Close the writer. Also, most of the time you'd likely want to wrap the writer in a using statement.
Fortunately on dispose it automatically closes the writer, flushing the final batch of items to write, so it also solves your issue as well as disposing with any unmanaged items you are now finished with.
Try the following:
using (StreamWriter file = new StreamWriter(#"C:\test.csv", true))
{
foreach (string s in MyListString)
{
Console.WriteLine(s); // Display all the data
file.WriteLine(s); // Write only a part of it
}
}
You have to close your stream:
using(StreamWriter file = new StreamWriter(#"C:\test.csv", true))
{
foreach (string s in MyListString)
{
Console.WriteLine(s); // Display all the data
file.WriteLine(s); // Write only a part of it
}
}
using (StreamWriter file = new StreamWriter(#"C:\test.csv", true)){
foreach (string s in MyListString)
{
Console.WriteLine(s); // Display all the data
file.WriteLine(s); // Write only a part of it
}
}

C# method to read text error log file

I am trying to read a file I create that contains all the logs lines throughout my program. I have the following cod:
private string ReadEmailLog(string EmailLog)
{
TextReader tr = new StreamReader(EmailLog);
tr.ReadLine();
tr.Close();
}
I need to read the EmailLog file, every line of it, and then put return it into a string called message. How would I get this method to return the whole log file, every line?
You can use File.ReadAllText or File.ReadAllLines.
If you're using .NET 4.0, you can also use File.ReadLines:
var files = from file in Directory.EnumerateFiles(#"c:\",
"*.txt", SearchOption.AllDirectories)
from line in File.ReadLines(file)
where line.Contains("Microsoft")
select new
{
File = file,
Line = line
};
foreach (var f in files)
{
Console.WriteLine("{0}\t{1}", f.File, f.Line);
}
This allows you to make file I/O part of a LINQ operation.
Try
tr.ReadToEnd();
which will return a string that contains all the content of your file.
TextReader.ReadToEnd Method
If you want to get the lines in a string[], then
tr.ReadToEnd().Split("\n");
should do it, while it will separate the lines to the "\n" character, which represents a carriage return and line feed combined characters (new line character).
simply use:
String text = tr.ReadToEnd();
You can read all the contents or the log and return it. For example:
private string void ReadEmailLog(string EmailLog)
{
using(StreamReader logreader = new StreamReader(EmailLog))
{
return logreader.ReadToEnd();
}
}
Or if you want each line one at a time:
private IEnumerable<string> ReadEmailLogLines(string EmailLog)
{
using(StreamReader logreader = new StreamReader(EmailLog))
{
string line = logreader.ReadLine();
while(line != null)
{
yield return line;
}
}
}
tr.ReadToEnd(); //read whole file at once
// or line by line
While ( ! tr.EOF)
tr.ReadLine()//

Categories