为什么我的文件流没有正确写入
Why is my filestream not writing correctly
我正在尝试内联修改文件流,因为文件可能非常大,我不想将它加载到内存中。我正在编辑的信息总是相同的长度,所以理论上我可以使用流 reader 交换内容,但它似乎没有写入正确的位置
我创建了一段代码,使用流 reader 将逐行读取,直到找到正则表达式匹配,然后尝试将字节换出已编辑的行。代码如下:
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
我知道 "Description: " 后面的部分总是 6 个字符,所以我在该行上进行了替换,例如用 UVWXYZ 替换。
问题是,例如,如果文件读取为
'...
UNIMPORTANT UNKNOWN DATA
DESCRIPTION: ABCDEF
MORE DATA
...'
结果会像
'...
UNIMPORTANT UNKNOWN DDESCRIPTION: UVWXYZDEF
MORE DATA
...'
在您的示例中,您是 "off" 4 个字符。不是很常见 "off by one error",但很接近。但也许不同的模式最有帮助?
如今的程序很少"on the file"那样工作。出错的地方太多了,一直到中途断电。相反,他们:
- 在同一位置创建一个空的新文件。通常临时命名和隐藏。
- 将输出写入新文件
- 完成后一切正常 - 所有缓存都被刷新并且所有内容都在磁盘上(由 Stream.Close() 或 Dispose() 完成) - 只需用新文件替换旧文件使用 OS 移动操作。
优点是不会丢失数据。即使计算机在操作过程中断电,最重要的是临时文件也会乱七八糟。您仍然拥有原始文件,您可以删除临时文件并在需要时从头开始重新开始工作。实际上,恢复仅在极少数情况下才有意义(文字处理器)
新文件替换旧文件是通过移动命令完成的。如果它们在同一个分区上,那实际上只是文件系统中的重命名操作。由于现代 FS 基本上设计得像一个顶线,健壮的关系数据库在这方面没有危险。
您可以在从 Word Porcessor of choice 到备份程序、Firefox 下载管理器(因为您可能会覆盖之前存在的文件)甚至压缩程序的所有内容中找到该模式。每次写作阶段很长,想把危险降到最低,那就是去pattern。
并且由于您可以完全在内存中工作而无需处理 read/write 头部的移动,因此它也可以解决您的问题。
编辑:我从 memory/documentation 为它制作了一些源代码。可能包含语法错误
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(临时路径,源路径);
我认为这里的问题是您没有考虑每行的换行符 ("\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);
}
}
}
我正在尝试内联修改文件流,因为文件可能非常大,我不想将它加载到内存中。我正在编辑的信息总是相同的长度,所以理论上我可以使用流 reader 交换内容,但它似乎没有写入正确的位置
我创建了一段代码,使用流 reader 将逐行读取,直到找到正则表达式匹配,然后尝试将字节换出已编辑的行。代码如下:
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 我知道 "Description: " 后面的部分总是 6 个字符,所以我在该行上进行了替换,例如用 UVWXYZ 替换。 问题是,例如,如果文件读取为
'... UNIMPORTANT UNKNOWN DATA DESCRIPTION: ABCDEF MORE DATA ...'
结果会像
'... UNIMPORTANT UNKNOWN DDESCRIPTION: UVWXYZDEF MORE DATA ...'
在您的示例中,您是 "off" 4 个字符。不是很常见 "off by one error",但很接近。但也许不同的模式最有帮助?
如今的程序很少"on the file"那样工作。出错的地方太多了,一直到中途断电。相反,他们:
- 在同一位置创建一个空的新文件。通常临时命名和隐藏。
- 将输出写入新文件
- 完成后一切正常 - 所有缓存都被刷新并且所有内容都在磁盘上(由 Stream.Close() 或 Dispose() 完成) - 只需用新文件替换旧文件使用 OS 移动操作。
优点是不会丢失数据。即使计算机在操作过程中断电,最重要的是临时文件也会乱七八糟。您仍然拥有原始文件,您可以删除临时文件并在需要时从头开始重新开始工作。实际上,恢复仅在极少数情况下才有意义(文字处理器)
新文件替换旧文件是通过移动命令完成的。如果它们在同一个分区上,那实际上只是文件系统中的重命名操作。由于现代 FS 基本上设计得像一个顶线,健壮的关系数据库在这方面没有危险。
您可以在从 Word Porcessor of choice 到备份程序、Firefox 下载管理器(因为您可能会覆盖之前存在的文件)甚至压缩程序的所有内容中找到该模式。每次写作阶段很长,想把危险降到最低,那就是去pattern。
并且由于您可以完全在内存中工作而无需处理 read/write 头部的移动,因此它也可以解决您的问题。
编辑:我从 memory/documentation 为它制作了一些源代码。可能包含语法错误
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(临时路径,源路径);
我认为这里的问题是您没有考虑每行的换行符 ("\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);
}
}
}