【问题标题】:Why is my filestream not writing correctly为什么我的文件流没有正确写入
【发布时间】:2019-08-04 21:55:02
【问题描述】:

我正在尝试修改内联文件流,因为该文件可能非常大,我不想将其加载到内存中。我正在编辑的信息总是相同的长度,所以理论上我可以使用流式阅读器交换内容,但它似乎没有写入正确的位置

我创建了一段代码,使用流阅读器将逐行读取,直到找到正则表达式匹配,然后尝试将字节与编辑的行交换出来。代码如下:

private void UpdateFile(string newValue, string path, string pattern)
{
    var regex = new Regex(pattern, RegexOptions.IgnoreCase);
    int index = 0;
    string line = "";

    using (var fileStream = File.OpenRead(path))
    using (var streamReader = new StreamReader(fileStream, Encoding.Default, true, 128))
    {

        while ((line = streamReader.ReadLine()) != null)
        {
            if (regex.Match(line).Success)
            {
                break;
            }
            index += Encoding.Default.GetBytes(line).Length;
        }
    }
    if (line != null)
    {
        using (Stream stream = File.Open(path, FileMode.Open))
        {
            stream.Position = index + 1;
            var newLine = regex.Replace(line, newValue);
            var oldBytes = Encoding.Default.GetBytes(line);
            var newBytes = Encoding.Default.GetBytes("\n" + newLine);
            stream.Write(newBytes, 0, newBytes.Length);
        }
    }

}

代码几乎按预期工作,它插入了更新的行,但它总是提前一点,只是多早会根据我正在编辑的文件略有不同。我希望这与我管理流位置的方式有关,但我不知道处理这个问题的正确方法。

不幸的是,我正在处理的确切文件受 NDA 保护。

结构如下: 一个文件将包含未知数量的数据,后跟一行已知格式,例如: 说明:ABCDEF 我知道“描述:”后面的部分将始终为 6 个字符,因此我在行上进行替换以替换为例如 UVWXYZ。 问题是,例如,如果文件读取为

'... 不重要的未知数据 描述:ABCDEF 更多数据 ...'

它会像这样出现

'... 不重要的未知描述:UVWXYZDEF 更多数据 ...'

【问题讨论】:

  • 目前还不清楚您要做什么。请给我们提供文件示例,您要更改的位置以及要更改的内容。
  • 查看我的文件结构编辑
  • 如果你能分享minimal reproducible example,那就太棒了。 minimal reproducible example 必须能够被复制并粘贴到控制台应用程序中并且无需修改即可运行(这可能涉及您将示例文件附加到您的问题中 - 它不必是真实的客户文件,只是一个证明问题的人)。它必须证明您遇到的问题,并且您的问题必须清楚地详细说明 minimal reproducible example 当前正在做什么 - 以及您希望它做什么。
  • 您是否考虑过只打开文件一次(而不是两次,一次读取,一次写入)? stackoverflow.com/questions/33633344/…

标签: c# file io filestream


【解决方案1】:

我认为这里的问题是你没有考虑你得到的每一行的换行符(“\n”),因此你的索引错误地设置了你的流的位置。试试下面的代码:

private void UpdateFile(string newValue, string path, string pattern)
{
   var regex = new Regex(pattern, RegexOptions.IgnoreCase);
   int index = 0;
   string line = "";

   using (var fileStream = File.OpenRead(path))
   using (var streamReader = new StreamReader(fileStream, Encoding.Default, true, 128))
   {

       while ((line = streamReader.ReadLine()) != null)
       {
           if (regex.Match(line).Success)
           {
            break;
           }
           index += Encoding.ASCII.GetBytes(line + "\n").Length;
       }
   }
   if (line != null)
   {
       using (Stream stream = File.Open(path, FileMode.Open))
       {
           stream.Position = index;
           var newBytes = Encoding.Default.GetBytes(regex.Replace(line + "\n", newValue));
           stream.Write(newBytes, 0, newBytes.Length);
       }
   }
}

【讨论】:

  • 这太完美了!谢谢,我没有意识到 ReadLine 函数也没有返回换行符。
【解决方案2】:

在您的示例中,您“偏离”了 4 个字符。不是很常见的“因一个错误而关闭”,但很接近。但也许不同的模式最有帮助?

如今的程序很少像那样“在文件上”工作。出错的地方太多了,一直到中途断电。相反,他们:

  • 在同一位置创建一个空的新文件。通常是临时命名和隐藏的。
  • 将输出写入新文件
  • 一旦你完成并且一切都很好 - 所有缓存都被刷新并且一切都在磁盘上(由 Stream.Close() 或 Dispose() 完成) - 只需使用操作系统移动将旧文件替换为新文件操作。

优点是不可能有数据丢失。即使计算机在运行过程中断电,最重要的是临时文件也被弄乱了。您仍然有原始文件,如果您也需要,您可以删除临时文件并从头开始工作。事实上,恢复只在极少数情况下才有意义(文字处理器)

用新文件替换旧文件是通过移动顺序完成的。如果它们在同一个分区上,那实际上只是文件系统中的重命名操作。由于现代 FS 基本上被设计为顶线,强大的关系数据库因此没有危险。

您可以在从Word Porcessor of choice 到备份程序、Firefox 的下载管理器(因为您可能会覆盖之前存在的文件)甚至压缩程序的所有内容中找到该模式。每次你有一个很长的写作阶段,想把危险降到最低,那就是去模式。

而且由于您可以完全在内存中工作,而不必处理在读/写头周围移动,它也可以解决您的问题。

编辑:我从内存/文档中为它制作了一些源代码。可能包含语法错误

string sourcepath; //containts the source file path, set by other code
string temppath; //containts teh path of the tempfile. Should be in the same folder, and thus same partiion

//Open both Streams, can use a single using for this
//The supression of any Buffering on the output should  be optional and will be detrimental to performance
using(var sourceStream = File.OpenRead(sourcepath), 
  outStream = File.Create(temppath, 0, FileOptions.WriteThrough )){

    string line = "";

    //itterte over the input
    while((line = streamReader.ReadLine()) != null){
        //do processing on line here

        outStream.Write(line);
    }
}

//替换文件。很确定它会在不询问的情况下覆盖 File.Move(temppath, sourcepath);

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2022-09-29
    • 2015-01-15
    • 2015-08-30
    • 1970-01-01
    • 2020-10-07
    • 1970-01-01
    • 2019-11-09
    相关资源
    最近更新 更多