为什么我的文件流无法正确写入

时间:2019-08-04 21:55:02

标签: c# file io filestream

我正在尝试内联修改文件流,因为文件可能很大,并且我不想将其加载到内存中。我正在编辑的信息长度总是一样的,因此从理论上讲,我可以使用流阅读器将内容换出,但似乎并没有写到正确的位置

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

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替换。 问题是,例如,如果文件读取为

'...
UNIMPORTANT UNKNOWN DATA
DESCRIPTION: ABCDEF
MORE DATA
...'

它会以类似的方式出现

'...
UNIMPORTANT UNKNOWN DDESCRIPTION: UVWXYZDEF
MORE DATA
...'

2 个答案:

答案 0 :(得分: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);
       }
   }
}

答案 1 :(得分:0)

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

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

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

优点是不可能丢失数据。即使计算机在运行中失去电源,顶部的临时文件也会被弄乱。您仍然可以得到原始文件,您也可以删除临时文件并从头开始重新启动工作。实际上,仅在极少数情况下(文字处理器)才有意义进行恢复

用移动命令完成新文件替换旧文件的操作。如果它们在同一分区上,则实际上只是在Filesytem中进行重命名操作。而且,由于现代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);

相关问题