16

When I'm saving content of the String[] array with System.IO.File.WriteAllLines, at the end of a file is always left a blank line. For example:

System.IO.File.WriteAllLines(Application.dataPath + "/test.txt",["a", "b", "c"]);

Produces file (without underscore):

a
b
c
_

There was already such topic: Empty line in .Net File.WriteAllLines, is a bug? , but autor said that "I think there are something wrong with my data,that's my problem but not the WritAllLines" and it was closed as "too localized" (?!?).

It's a bug? How can I easily get rid of it (for now I'm just ignoring it when reading file again)?

Community
  • 1
  • 1
user1557862
  • 163
  • 1
  • 1
  • 5

9 Answers9

17

As others have pointed out, that's just how it works. Here is a method that does it without the extra newline:

public static class FileExt
{
    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]);
            }
        }
    }
}

Usage:

FileExt.WriteAllLinesBetter("test.txt", "a", "b", "c");

Writes:

aenter
benter
c
Daniel A.A. Pelsmaeker
  • 47,471
  • 20
  • 111
  • 157
9

The WriteAllLines method will write out each line in your array followed by a line break. This means that you will always get this "empty line" in your file.

The point made in the post you linked is that when running ReadAllLines that considers a line to be characters terminated by a line break. So when you use the read method on the file you've just written you should get the exact same lines back.

If you are reading the file in a different way then you will have to deal with linebreaks yourself.

Essentially what you are seeing is Expected Behaviour.

Chris
  • 27,210
  • 6
  • 71
  • 92
  • 1
    I understand that the best way to get rid of it will be just using `WriteAllText` with `String.Join` with `\r\n` as separator? – user1557862 Jul 27 '12 at 14:19
  • Look also at [this question](http://stackoverflow.com/questions/585860/string-join-vs-stringbuilder-which-is-faster) about performances of String.Join and StringBuilder – Steve Jul 27 '12 at 14:34
  • Since there is always a "last line", be it empty or not, I don't understand why the last written line must be empty. The last array element that results from ReadAllLines is a file line that doesn't end with a line break - it's just the last line. Compare files saved by WriteAllLines with files saved by XmlSerializer.Serialize and you'll see what I mean. – Tiago Freitas Leal Jun 19 '16 at 10:11
  • @TiagoFreitasLeal: Its all to do with what I said in my first paragraph. The way the code works is to write each item of your array followed by a line break. This means that the last item in your array will be followed by a line break. This means that the file ends in a line break which then means that there is an empty string on that last line. This is a necessary product of any representation of that trailing new line (if you're not sure why consider how you would represent the difference between the same file with and without that trailing new line). – Chris Jun 20 '16 at 11:46
  • Also in response to "The last array element that results from ReadAllLines is a file line that doesn't end with a line break" - this is not true. You might expect the behaviour to be that it puts a newline between each item which would result in there being no newline at the end of the file. This is not how it works though. Internally it pretty much does a `foreach` across the input and for each item it does a `WriteLine`. The `WriteLine` is why every item ends with a newline. Whatever you think the right way to do this is of course not important, this is simply the way the code works. – Chris Jun 20 '16 at 11:49
  • That's the way Microsoft did it but it doesn't mean it's well done. Did you try reading a file that doesn't end with a line break using ReadAllLines? That last line is part of the array. When reading, lines end with line breaks or the end of file. When writing, all lines must end with a line break. If you take a file that doesn't end with a line break and ReadAllLines followed by WriteAllLines the file size changes and my SVN complains. But never mind, on the next answer we have a WriteAllLinesBetter method that solves the issue. – Tiago Freitas Leal Jun 21 '16 at 16:17
  • @TiagoFreitasLeal: You may well be right and that it would have been better to not having the trailing line break but the method is only one of many to write content to a file. If you don't like their quick and dirty way of doing things its trivial to write something that does what you want. – Chris Jun 22 '16 at 08:19
9

You can also save a file with WriteAllText and join array of lines manually like:

File.WriteAllText(file, String.Join("\r\n",correctedLines));
Paweł Zarzycki
  • 306
  • 3
  • 7
4

@Virtlink solution is almost perfect. In fact there is a scenario where you will get garbage at the end of the file - when the file exist and its content is bigger than the new content. Before wrting the new file content, just reset the file lenght to zero.

    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))
        {
            stream.SetLength(0);
            using (var writer = new StreamWriter(stream))
            {
                if (lines.Length > 0)
                {
                    for (var i = 0; i < lines.Length - 1; i++)
                    {
                        writer.WriteLine(lines[i]);
                    }
                    writer.Write(lines[lines.Length - 1]);
                }
            }
        }
    }
Tiago Freitas Leal
  • 693
  • 1
  • 7
  • 13
4

There's a simpler workaround:

// 1. Convert the items on the array to single string with the separator "\n" between the items
string AllItemsInOneString= string.Join("\n", StringArrayToSave);

// 2. Save with WriteAllText instead
File.WriteAllText(FilePath, AllItemsInOneString);
Majid ALSarra
  • 394
  • 2
  • 7
3

WriteAllLines writes every single entry in your array and append a newline.
As you can see, every string is on its own line, this means that your last entry is newline terminated and you see a one more line in file. You could prove this with an hexdecimal dump of the file

Looking at the source code of WriteAllLines confirms this.
Internally, it uses the TextWriter.WriteLine(string) method.

Steve
  • 213,761
  • 22
  • 232
  • 286
0

i did the same thing just adding a line :3 , hope that helps someone

    for(int i = 0; i < Length; i++)
    {
        line = getLocation(i).X.ToString("0.0", nfi) + ',' + getLocation(i).Y.ToString("0.000", nfi) + ',' + getColorRaw(i).R.ToString("0.000", nfi) + ',' + getColorRaw(i).G.ToString("0.000", nfi) + ',' + getColorRaw(i).B.ToString("0.000", nfi);
        writer.Write(line);
        if( i < Length - 1) writer.Write("\n");
0

The easiest way for me to do it was usning AppendAllText for last line:

if ($i -ne $lines.Count - 1){
     $newLines += $lines[$i]
    } else {
     $lastLine = $lines[$i]
}

[IO.File]::WriteAllLines($file.FullName, $newLines);
[IO.File]::AppendAllText($file.FullName, $lastLine);
-1

To get rid of the trailing newline, the best solution would be, when you read it with .ReadAllLines() to actually dismiss the last element from your array of strings.

Felix Frank
  • 8,125
  • 1
  • 23
  • 30
rokyed
  • 420
  • 5
  • 9
  • 1
    That would be a partial solution in case an empty string is actually read into the receiving array structure. The question makes it appear that this is not in fact the case. – Felix Frank Jun 13 '14 at 23:13