如何在文本文件的第一行插入行?

17

我有一个包含以下内容的测试文件:

1,2,3
2,3,4
5,6,7

我想把A,B,C插入到第一行:

这样我就得到了:

A,B,C
1,2,3
2,3,4
5,6,7

我该如何做到这一点?

6个回答

29

与之前的答案类似,但这里展示了如何在最小化内存消耗的情况下完成你想要做的事情。即使你将其打开为读/写流,你也无法“插入”数据,因此你需要阅读整个要修改的文件。

static void WriteABC(string filename)
{
    string tempfile = Path.GetTempFileName();
    using (var writer = new StreamWriter(tempfile))
    using (var reader = new StreamReader(filename))
    {
        writer.WriteLine("A,B,C");
        while (!reader.EndOfStream)
            writer.WriteLine(reader.ReadLine());
    }
    File.Copy(tempfile, filename, true);
}

2
很棒,但我想知道,在结尾处我们应该使用´File.Delete(tempfile)´还是最好让系统来清理? - Luis Ferrao
是的,最好删除文件,为什么要留下我们不再需要的东西呢? - Artemious
最好使用 File.Move。 - colin lamarre

4
我认为这个答案更简单更快速:
public static void WriteToFile(string Path, string Text)
{
    string content = File.ReadAllText(Path);
    content = Text + "\n" + content;      
    File.WriteAllText(Path, content);
}

然后你可以调用它:
WriteToFile("yourfilepath", "A,B,C");

针对较小的文件来说是不错的选择,但对于较大的文件可能会遇到内存问题。 - Ali
我们需要指定编码,否则它会读取文件,但是用不同的编码写入。 - Artemious

2
来晚了...
好问题。您可以使用这段代码:
static void WriteABC2(string filename)
{
    string tempfile = Path.GetTempFileName();
    using (var writer = new FileStream(tempfile, FileMode.Create))
    using (var reader = new FileStream(filename, FileMode.Open))
    {
        var stringBytes = Encoding.UTF8.GetBytes("A,B,C" + Environment.NewLine);
        writer.Write(stringBytes, 0, stringBytes.Length);

        reader.CopyTo(writer);
    }
    File.Copy(tempfile, filename, true);
    File.Delete(tempfile);
}

这段代码基于优秀的Jake答案,但我使用FileStream而不是StreamWriterStreamReader。当处理大文件时,使用FileStream可以带来很大的性能提升(从我的测试中甚至快了4倍)。只是为了更清晰一点,我进行了一些测试,并在下面报告了结果。

如何进行测试

我创建了7个文件。每个文件包含10^X行,它的名称是file<line count>.txt。因此,文件列表如下:

| File name       | Line count | File size |
|-----------------|------------|-----------|
| file1.txt       |          1 | 66 byte   |
| file10.txt      |         10 | 678 byte  |
| file100.txt     |        100 | 6,72 KB   |
| file1000.txt    |       1000 | 68,2 KB   |
| file10000.txt   |      10000 | 692 KB    |
| file100000.txt  |     100000 | 6,85 MB   |
| file1000000.txt |    1000000 | 69,5 MB   |

每个文件的每一行都由以下内容组成:
<line index starting from 0> - abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789

例如,file10.txt 的前两行如下:
0 - abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789
1 - abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789

我使用以下代码进行测试并获取函数执行时间:
var stopwatch = new Stopwatch();

long abcTotalDuration = 0;
long abc2TotalDuration = 0;

string fileInput = "file1.txt";
string outputFolder = "output";

for (int i = 0; i < 100; i++)
{
    string filename1 = Path.Combine(outputFolder, Guid.NewGuid().ToString());
    string filename2 = Path.Combine(outputFolder, Guid.NewGuid().ToString());

    stopwatch.Restart();
    WriteABC(fileInput, filename1);
    stopwatch.Stop();
    abcTotalDuration += stopwatch.ElapsedMilliseconds;

    stopwatch.Restart();
    WriteABC2(fileInput, filename2);
    stopwatch.Stop();
    abc2TotalDuration += stopwatch.ElapsedMilliseconds;

    File.Delete(filename1);
    File.Delete(filename2);
}

Console.WriteLine("ABC : " + abcTotalDuration.ToString());
Console.WriteLine("ABC2: " + abc2TotalDuration.ToString());

// Just to wake me up :-)
Console.Beep(800, 1000);

Console.ReadKey();

测试由在每个指定文件前加上 A,B,C(换行符) 组成。结果保存在单独的文件中(仅供测试使用)。
我针对每个输入文件(在fileInput变量中指定)启动了 5 次代码。每次启动时,执行 WriteABCWriteABC2 函数各 100 次。
以下是结果(全部以毫秒为单位): 处理 file1.txt
      | ABC | ABC2 |
      |-----|------|
      | 285 |  305 |
      | 356 |  352 |
      | 435 |  371 |
      | 355 |  313 |
      | 362 |  372 |
|-----|-----|------|
  AVG | 359 |  343 |
|-----|-----|------|

处理 file10.txt 文件。
      | ABC | ABC2 |
      |-----|------|
      | 256 |  251 |
      | 273 |  323 |
      | 355 |  347 |
      | 350 |  314 |
      | 315 |  286 |
|-----|-----|------|
  AVG | 310 |  304 |
|-----|-----|------|

处理文件 file100.txt
      | ABC | ABC2 |
      |-----|------|
      | 247 |  253 |
      | 239 |  246 |
      | 357 |  353 |
      | 387 |  334 |
      | 333 |  329 |
|-----|-----|------|
  AVG | 313 |  303 |
|-----|-----|------|

处理文件 file1000.txt
      | ABC  | ABC2  |
      |------|-------|
      |  977 |   924 |
      |  784 |   738 |
      |  818 |   764 |
      | 1142 |  1101 |
      |  975 |   903 |
|-----|------|-------|
  AVG |  939 |   886 |
|-----|------|-------|

处理文件 file10000.txt 中。
      | ABC  | ABC2  |
      |------|-------|
      | 1150 |   748 |
      | 1147 |   811 |
      | 1069 |   654 |
      | 1181 |   799 |
      | 1234 |   805 |
|-----|------|-------|
  AVG | 1156 |   763 |
|-----|------|-------|

处理文件 file100000.txt
      | ABC  | ABC2  |
      |------|-------|
      | 5485 |  1769 |
      | 5268 |  1528 |
      | 5296 |  1555 |
      | 5308 |  1529 |
      | 5289 |  1553 |
|-----|------|-------|
  AVG | 5329 |  1587 |
|-----|------|-------|

处理文件 file1000000.txt
      | ABC   | ABC2   |
      |-------|--------|
      | 49034 |  12633 |
      | 52116 |  12484 |
      | 51643 |  12109 |
      | 52022 |  12359 |
      | 53145 |  12716 |
|-----|-------|--------|
  AVG | 51592 |  12460 |
|-----|-------|--------|

差别从file10000.txt开始变得明显。

2
这是一个链接,解释了如何在C#中使用TextReader(StreamReader)和TextWriter(StreamWriter)类。

http://www.csharpfriends.com/Articles/getArticle.aspx?articleID=132

尽管这是特定于C#的,但在许多语言中,方法可能相对相同。
(注意:有几种方法可以做到这一点,这只是一个快速想到的想法之一。)
基本上,您可以将文本文件的内容读入字符串中,找到该字符串的开头,写入您的信息(A、B、C以及回车或换行符),然后将该信息重新写出到文本文件中,覆盖原始内容并保存。

5
如果你的文件大小为10MB或100MB,你会将其全部读入字符串中(内存中)吗?这并不是很高效。 - Ronald Wildenberg
1
+1 - 诱惑是认为你可以在文件开头“插入”一行,而其余部分保持不变。事实并非如此,所以你指出了这一点是正确的。 - Mark Brittingham
Joshua提到了StreamReader和StreamWriter。如果Gold都使用了,那么可以逐行进行操作,虽然不是很高效,但不用担心OutOfMemoryException的问题。 - Steve Danner
答案是:“你可以将文本文件的内容读入字符串中”。除非您确定该文件永远不会超过某个大小(取决于您的环境),否则这只是一个坏主意。 - Ronald Wildenberg

1

在文本文件中无法“插入”任何内容。

您将需要:

  • 复制内容
  • 在正确的时间插入额外数据
  • 关闭文件
  • 删除原始文件并将新文件重命名为旧文件名

@rwwilden:你似乎在提倡不可能的事情。你测试过这个理论吗?看看你回答的评论。 - Dan Tao
@rwwilden:实际上这是真的,你只能“在结尾处插入”,也就是追加,否则就会覆盖现有文件的一部分。 - Binary Worrier
抱歉造成困扰,我的答案只是覆盖了文件的一部分,并没有插入任何内容。 - Ronald Wildenberg

0

@Jake:StringBuilder更好用,而且更简单

public static void WriteToFile(string path, string text)
{
        StringBuilder stringBuilder = new StringBuilder();

        stringBuilder.AppendLine("a,b,c");
        stringBuilder.Append(File.ReadAllText(path)).AppendLine();
        File.WriteAllText(path, stringBuilder.ToString()); 
}

编辑: 但是 StringBuilder 有一个缓冲区限制:最大容量为 2147483647


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接