在 C# 中同时读取和写入文件
本文关键字:文件 读取 | 更新日期: 2023-09-27 18:35:49
我正在尝试用 C# 同时读取和写入文件。我在这里和 MSDNA 中阅读了许多线程,但似乎没有一个符合我的需求。我的文件有一系列用逗号分隔的数字。确切地说,是大量的数字。我数据中的一个示例行是这样的
-0.1171695,0.03270377,2.420116,-0.02128719,0.9612453,0.2460478,-0.1225349,-0.110185,0.07739609,2.500247,-0.2783474,-0.06909045,-0.01818598,0.9578197,-0.1089995,0.456151,2.639686,0.3486561,-0.0008622027,-0.002657401,0.9372466,-0.1170361,0.6441286,2.674476,0.08662115,0.001171953,-0.01347759,0.9961495,-0.2623751,0.3104511,2.600713,-0.002028131,0.004831213,0.9220369,-0.3870664,-0.3145202,0.123338,2.49155,0.217727,0.4528476,-0.2009471,0.8409188,-0.2771441,-0.07509593,2.299996,0.2185546,3.817581E-09,7.635163E-09,0.9758247,-0.2690773,-0.1254997,2.259336,-0.02814693,0.0009682054,-0.03436448,0.9990125,0.01672855,0.3196935,2.572941,0.001961287,0.005368799,0.9392719,0.3431264,0.08505877,0.1033191,2.450031,0.1364797,-0.3903133,0.161962,0.8959894,0.03953359,-0.08940583,2.255897,0.2523192,7.699712E-09,0,0.967644,0.01856858,-0.1507191,2.211281,0.004362902,0.0004109977,0.09378911,0.9955825,-0.1821601,-0.03123568,2.403718,4.035548E-09,-3.067017E-07,0.9231187,-0.384515,-0.2238743,-0.4083549,2.266029,-0.05534944,0.02699615,-0.3286877,0.9424288,-0.2095885,-0.7422835,2.178757,-0.06393463,-0.003723484,-0.0580211,0.9962591,-0.2166772,-0.7653325,2.087598,0.5079094,0.03407073,-0.05760901,0.8588064,-0.05478298,-0.01793054,2.37413,8.070093E-09,-3.066635E-07,0.9232336,0.3842392,0.004473582,-0.3737353,2.252681,-0.09306445,-0.04594634,0.3224528,0.9408783,0.004849254,-0.7096405,2.178587,-0.03546751,0.003154774,0.08854229,0.9954358,-0.005173458,-0.7367281,2.088935,0.5053017,0.02486493,-0.04239483,0.861542,63507242650167
它实际上在写字板中占据了24行。每一行。我想做的是转到每行的最后一行,对其进行更改,然后将其保存回文件中。我找到的所有同步写入器阅读器源代码都在使用 append。我不想在文件末尾附加。我希望从每行中获取最后一个数字,并将修改后的数字放回原处。我希望其余数据保持不变。我该怎么做?
你想要做的事情违反了Windows和POSIX中低级文件I/O API的限制;具体来说,除了重写整个文件之外,不可能在文件中间插入或删除字节。 有两种常用的"重写整个文件"的方法:
-
读取整个文件,然后将其写回新文件,其中包含所需的更改。完成后,关闭两个文件,删除旧文件,然后将新文件重命名为旧名称。 ("删除旧文件"步骤仅在Windows上是必需的,不幸的是,它引入了一个竞赛窗口,在该窗口中,同时阅读者可能会发现该文件不存在正确的名称。 这是首选技术,因为它更容易正确编码,不需要您暂时将整个文件保存在内存中,如果计算机在操作过程中崩溃,不会损坏文件,1 并保证并发读取器将看到新文件或旧文件,而不是两者的某种组合。 但是,它需要的暂存磁盘空间等于旧文件或新文件中较大的存储空间。
-
将整个文件读入内存。对文件的内存中表示形式进行更改(可以像大字符串或字符串列表一样简单)。倒带文件句柄并从头开始写回所有内容。(从技术上讲,您只需要在第一次更改之后写入所有内容,但这需要您知道第一次更改的字节偏移量,这通常比它的价值更麻烦。 如果结果比原始结果短,请使用
truncate
或等效物来切断多余的部分。 这不需要额外的暂存磁盘空间,但并发读取器可能会看到两个文件的乱码组合,如果计算机在操作过程中崩溃,则文件可能会被销毁。 您可以通过文件锁定来缓解并发读取器问题,但请注意,某些文件锁定机制是建议性的,即仅对知道锁定可能正在发生的读取器有效。
1 某些操作系统和/或文件系统要求在重命名之前调用新文件fsync
,以确保在重命名后计算机在数十分钟长的窗口中崩溃时的数据完整性。 这是这些系统中的错误。
您应该考虑内存映射文件之一:
http://blogs.msdn.com/b/salvapatuel/archive/2009/06/08/working-with-memory-mapped-files-in-net-4.aspx
或者可能是托管的 ESENT 包装器:
http://managedesent.codeplex.com/wikipage?title=PersistentDictionaryDocumentation&referringTitle=Home
应该像这样简单:
List<string> myLines = File.ReadAllLines("file.txt").ToList();
foreach (string s in myLines)
{
//whatever you're doing to each line
}
File.WriteAllLines("file.txt", myLines);
为什么不用ReadAllLines()
读取文件,用Regex.Replace()
替换,然后WriteAllLines()
?
编辑:详细地:
string[] lines = File.WreadAllLines("file.txt");
int lastnum;
for(int i=0;i<lines.Count;i++)
lines[i]=System.Text.RegularExpressions.Regex.Replace(lines[i], @",('d+)$", m =>
{
lastnum = Convert.ToInt32(m.Groups[1].Value);
// Do any operations on lastnum
return "," + lastnum.ToString();
});
File.WriteAllLines("file.txt",lines);
要读取并保存文件:
StringBuilder newTextFile = new StringBuilder();
string[] lines = File.ReadAllLines(@"1.txt");
foreach (string l in lines)
{
// logic to replace last number, saved in string newLine
// you can find the last
newTextFile.Append(newLine + "'r'n");
}
File.WriteAllText(@"1.txt", newTextFile.ToString());
要更改行(从我的头顶上,测试它!
int locationAt = line.LastIndexOf(',');
string newLine = line.Substring(0, locationAt) + newValue + line.Substring(locationAt);
或:
string[] values = line.Split(',');
values[values.Length - 1] = 'somethingelse';
string newLine = string.Join(",", values);
由于您只更改每行逗号后的最后一个数字,因此您可以做的是逐行读取并获取逗号的最后一个索引,然后获取子字符串。 您可以将所有这些最后的数字添加到列表中。 处理此列表,最后写入文件。