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;
}
...
Related
StreamReader reader = new StreamReader("randomTextFile.txt");
string line = "";
while (line != null)
{
line = reader.ReadLine();
if (line != null)
{
Console.WriteLine(line);
}
}
reader.Close();
Console.ReadLine();
In the above code, there is an if statment inside the while statment, even though they specify the same thing (line != null). If I remove said if statment, a new line will be added after the txt file contents (instead of "11037", the console will show "11037" + an empty line).
The while-loop exit condition will only be checked when it is invoked, so at the beginning of each iteration, not everytime inside it's scope.
MSND: the test of the while expression takes place before each execution of
the loop
You could use this loop:
string line;
using (var reader = new StreamReader("randomTextFile.txt"))
{
while ((line = reader.ReadLine()) != null)
{
Console.WriteLine(line);
}
}
You should also use the using-statement as shown above on every object implementing IDisposable. On that way it is ensured that unmanaged resources are disposed.
According to the Console.WriteLine specific question why it writes a new line even if the value is null, that's documented:
If value is null, only the line terminator is written to the standard
output stream.
Its because you are supposed to check after you read, so try read then check.
This is what you code should look like.
var reader = new StreamReader("randomTextFile.txt");
var line = reader.ReadLine();
while (line != null)
{
Console.WriteLine(line);
line = reader.ReadLine();
}
reader.Close();
Console.ReadLine();
As a side note, you are using a StreamReader which implements IDisposable and you should wrap this in a using block.
I have the following code in C# that writes an array of lines to a file. The difference between this and File.WriteAllLines(string, string[]); is that mine does not leave an extra newline at the end.
public static void WriteAllLinesCompact(string path, IEnumerable<string> contents)
{
if (path == null)
throw new ArgumentNullException("path");
if (contents == null)
throw new ArgumentNullException("contents");
bool isFirst = true;
using (FileStream stream = File.OpenWrite(path))
using (StreamWriter writer = new StreamWriter(stream))
{
foreach (string line in contents)
{
if (isFirst)
{
writer.Write(line);
isFirst = false;
continue;
}
writer.Write("\r\n" + line);
}
}
}
The problem is that it does not terminate the file after the last line. For example, if the last line was "text = absolute", after replacing the last line with "tempor" and saving using the above method, the file's last line would be "tempor absolute" instead of just "tempor".
Please let me know if you need more information.
EDIT : I will try to explain more clearly what happens in the replace process.
Step 1 : Load any .txt file with File.ReadAllLines(string);
Step 2 : Replace the last line with one that's shorter than the previous one. For example, if the length of the last value was 10 chars, the new one should perhaps be 7 chars.
Step 3 : Save using the given method to the same file as before.
As suggested via comments, the best solution in this kind of situations is keeping it simple and relying on a for-sure-working solution. For example:
bool isFirst = true;
using (StreamWriter writer = new StreamWriter(path))
{
foreach (string line in contents)
{
if (isFirst)
{
writer.Write(line);
isFirst = false;
continue;
}
writer.Write(Environment.NewLine + line);
}
}
Personally, I would prefer to use writer.WriteLine, but it would go against the OP's requirement of not including a final new line.
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.
So im trying to close a file (transactions.txt) that has been open that i've used to read into a textbox and now I want to save back to the file but the problem debug says that the file is in use so I need to find a way to close it. Can anyone help me with this? Thanks!
SearchID = textBox1.Text;
string ID = SearchID.ToString();
bool idFound = false;
int count = 0;
foreach (var line in File.ReadLines("transactions.txt"))
{
//listView1.Items.Add(line);
if (line.Contains(ID))
{
idFound = true;
}
//Displays Transactions if the variable SearchID is found.
if (idFound && count < 8)
{
textBox2.Text += line + "\r\n";
count++;
}
}
}
private void SaveEditedTransaction()
{
SearchID = textBox1.Text;
string ID = SearchID.ToString();
bool idFound = false;
int count = 0;
foreach (var lines in File.ReadLines("transactions.txt"))
{
//listView1.Items.Add(line);
if (lines.Contains(ID))
{
idFound = true;
}
if (idFound)
{
string edited = File.ReadAllText("transactions.txt");
edited = edited.Replace(lines, textBox2.Text);
File.WriteAllText("Transactions.txt", edited);
}
The problem here is that File.ReadLines keeps the file open while you read it, since you've put the call to write new text to it inside the loop, the file is still open.
Instead I would simply break out of the loop when you find the id, and then put the if-statement that writes to the file outside the loop.
This, however, means that you will also need to maintain which line to replace in.
So actually, instead I would switch to using File.ReadAllLines. This reads the entire file into memory, and closes it, before the loop starts.
Now, pragmatic minds might argue that if you have a lot of text in that text file, File.ReadLines (that you're currently using) will use a lot less memory than File.ReadAllLines (that I am suggesting you should use), but if that's the case then you should switch to a database, which would be much more suited to your purpose anyway. It is, however, a bit of an overkill for a toy project with 5 lines in that file.
Use StreamReader directly with the using statement, for example:
var lines = new List<string>();
using (StreamReader reader = new StreamReader(#"C:\test.txt")) {
var line = reader.ReadLine();
while (line != null) {
lines.Add(line);
line = reader.ReadLine();
}
}
By using the using statement the StreamReader instance will automatically be disposed of after it's done with it.
You can try with this:
File.WriteAllLines(
"transactions.txt",
File.ReadAllLines("transactions.txt")
.Select(x => x.Contains(ID) ? textBox2.Text : x));
It works fine, but if the file is big you have to find other solutions.
You can use the StreamReader class instead of the methods of the File class. In this way you can use, Stream.Close() and Stream.Dispose().
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()//