如何使用 C 向文件插入字符#

How to insert characters to a file using C#

提问人:Gulzar Nazim 提问时间:9/19/2008 最后编辑:Gulzar Nazim 更新时间:5/17/2017 访问量:10214

问:

我有一个巨大的文件,我必须在特定位置插入某些字符。在 C# 中执行此操作的最简单方法是什么,而无需再次重写整个文件。

C# .NET 文件 随机存取

评论

0赞 Scott Marlowe 1/22/2009
问得好。我需要弄清楚如何回答你在问什么,而你的问题是谷歌返回的第一件事。

答:

3赞 Mike Thompson 9/19/2008 #1

您可能需要从插入更改的那一刻起重写文件。最好始终写入文件末尾,并使用 sort 和 grep 等工具按所需顺序获取数据。我假设您在这里谈论的是文本文件,而不是二进制文件。

评论

0赞 Gulzar Nazim 9/19/2008
我实际上是在寻找一些使用 C# 的随机访问技术,即使我必须使用不安全的代码。无论如何,感谢您的建议。
1赞 FlySwat 9/19/2008 #2

您可以使用随机访问来写入文件的特定位置,但您不能以文本格式执行此操作,您必须直接使用字节。

评论

0赞 Gulzar Nazim 9/19/2008
你能指出网络上的一些资源吗?我以为在 C# 中不可能进行随机访问文件处理。
0赞 Cristian Ciupitu 9/19/2008
我不认为他想覆盖旧字节。
0赞 Sev 9/19/2008 #3

根据项目的范围,您可能需要决定在表数据结构中插入每一行文本和文件。有点像数据库表,这样您就可以在任何给定时刻插入到特定位置,而不必每次都读入、修改和输出整个文本文件。这是因为您的数据正如您所说的那样“巨大”。您仍会重新创建该文件,但至少会以这种方式创建可伸缩的解决方案。

10赞 bineteri 9/19/2008 #4

文件系统不支持在文件中间“插入”数据。如果你真的需要一个可以以排序方式写入的文件,我建议你考虑使用嵌入式数据库。

您可能想看看 SQLiteBerkeleyDB。

再说一次,您可能正在处理文本文件或旧二进制文件。在这种情况下,您唯一的选择是重写文件,至少从插入点到末尾。

我会查看 FileStream 类以在 C# 中执行随机 I/O。

评论

0赞 FlySwat 9/19/2008
这并不完全正确。您可以使用随机访问从任何点任意读取和写入文件(以字节为单位)。但是,在插入某些内容时,您可以自行更改文件偏移量。换句话说,重新生成文件更简单。
2赞 bineteri 9/19/2008
我不同意。当然,您可以使用随机访问来查找文件中的任何点。但是,如果你在那个点上写,你将覆盖以前在该位置的内容。因此,如果您的文件中有“abced”,则查找“c”并写入“123”,则最终得到“ab123”,而不是“ab123cde”。
0赞 Captain Segfault 9/19/2008 #5

这可能是“可能的”,具体取决于文件系统如何存储文件以在中间快速插入(即添加额外的)字节。如果远程可能,则可能只能一次完成一个完整的块,并且只能通过对文件系统本身进行低级修改或使用特定于文件系统的接口来执行。

文件系统通常不是为此操作而设计的。如果你需要快速进行插入,你确实需要一个更通用的数据库。

根据您的应用程序,中间立场是将插入物组合在一起,因此您只对文件进行一次重写,而不是二十次。

2赞 Socratees 9/19/2008 #6

在不重写字符的情况下,无法将字符插入到文件中。使用 C#,可以使用任何 Stream 类来完成。如果文件很大,我建议您在 C# 代码中使用 GNU Core Utils。它们是最快的。我曾经使用核心实用程序(大小为 4GB、8GB 或更多等)处理非常大的文本文件。head、tail、split、csplit、cat、shuf、shred、uniq 等命令在文本处理方面确实有很大帮助。

例如,如果您需要将一些字符放在一个 2GB 的文件中,您可以使用 split -b BYTECOUNT,将 ouptut 放入文件中,将新文本附加到其中,然后获取其余内容并添加到其中。这应该比任何其他方式都快。

希望它有效。试一试。

1赞 Scott Marlowe 1/22/2009 #7

如果您知道要将新数据写入到的特定位置,请使用 BinaryWriter 类:

using (BinaryWriter bw = new BinaryWriter (File.Open (strFile, FileMode.Open)))
{
    string strNewData = "this is some new data";
    byte[] byteNewData = new byte[strNewData.Length];

    // copy contents of string to byte array
    for (var i = 0; i < strNewData.Length; i++)
    {
        byteNewData[i] = Convert.ToByte (strNewData[i]);
    }

    // write new data to file
    bw.Seek (15, SeekOrigin.Begin);  // seek to position 15
    bw.Write (byteNewData, 0, byteNewData.Length);
}

评论

1赞 data 9/7/2010
请注意,此代码将覆盖(!)位置 15 处的数据。
0赞 user1088520 12/9/2011 #8

您将始终必须重写插入点的剩余字节。如果此点为 0,则您将重写整个文件。如果最后一个字节前 10 个字节,则将重写最后 10 个字节。

在任何情况下,都没有直接支持“插入到文件”的功能。但是下面的代码可以准确地做到这一点。

var sw = new Stopwatch();
var ab = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ ";

// create
var fs = new FileStream(@"d:\test.txt", FileMode.OpenOrCreate, FileAccess.ReadWrite, FileShare.ReadWrite, 262144, FileOptions.None);
sw.Restart();
fs.Seek(0, SeekOrigin.Begin);
for (var i = 0; i < 40000000; i++) fs.Write(ASCIIEncoding.ASCII.GetBytes(ab), 0, ab.Length);
sw.Stop();
Console.WriteLine("{0} ms", sw.Elapsed.TotalMilliseconds);
fs.Dispose();

// insert
fs = new FileStream(@"d:\test.txt", FileMode.OpenOrCreate, FileAccess.ReadWrite, FileShare.ReadWrite, 262144, FileOptions.None);
sw.Restart();
byte[] b = new byte[262144];
long target = 10, offset = fs.Length - b.Length;
while (offset != 0)
{
    if (offset < 0)
    {
        offset = b.Length - target;
        b = new byte[offset];
    }
    fs.Position = offset; fs.Read(b, 0, b.Length);
    fs.Position = offset + target; fs.Write(b, 0, b.Length);
    offset -= b.Length;
}
fs.Position = target; fs.Write(ASCIIEncoding.ASCII.GetBytes(ab), 0, ab.Length);
sw.Stop();
Console.WriteLine("{0} ms", sw.Elapsed.TotalMilliseconds);

为了获得更好的文件 IO 性能,请使用上面代码中的“魔术双倍数字”。文件的创建使用 262144 字节 (256KB) 的缓冲区,这根本无济于事。插入的同一缓冲区执行“性能作业”,如运行代码时 StopWatch 结果所示。在我的 PC 上进行草稿测试得出以下结果:

13628.8 毫秒用于创建,3597.0971 毫秒用于插入。

请注意,插入的目标字节是 10,这意味着几乎整个文件都被重写了。

1赞 Fabrizio Stellato 4/20/2016 #9

你可以看看这个项目:Win Data Inspector

基本上,代码如下:

// this.Stream is the stream in which you insert data

{

long position = this.Stream.Position;

long length = this.Stream.Length;

MemoryStream ms = new MemoryStream();

this.Stream.Position = 0;

DIUtils.CopyStream(this.Stream, ms, position, progressCallback);

ms.Write(data, 0, data.Length);

this.Stream.Position = position;

DIUtils.CopyStream(this.Stream, ms, this.Stream.Length - position, progressCallback);

this.Stream = ms;

}

#region Delegates

public delegate void ProgressCallback(long position, long total);

#endregion

DIUtils.cs

public static void CopyStream(Stream input, Stream output, long length, DataInspector.ProgressCallback callback)
{
    long totalsize = input.Length;
    long byteswritten = 0;
    const int size = 32768;
    byte[] buffer = new byte[size];
    int read;
    int readlen = length < size ? (int)length : size;
    while (length > 0 && (read = input.Read(buffer, 0, readlen)) > 0)
    {
        output.Write(buffer, 0, read);
        byteswritten += read;
        length -= read;
        readlen = length < size ? (int)length : size;
        if (callback != null)
            callback(byteswritten, totalsize);
    }
}

评论

0赞 S.L. Barth is on codidact.com 4/21/2016
请不要复制/粘贴多个问题的相同答案。另外,在这里要小心宣传你自己的作品——我们有一条禁止公开自我推销的规则。
0赞 Martijn Pieters 4/22/2016
在这种情况下,也许您想阅读如何提供个人开源库?
0赞 user8011403 5/17/2017 #10

为什么不把指针放在文件的末尾(字面意思是,比文件的当前大小高出四个字节),然后在文件末尾写入插入数据的长度,最后是要插入的数据本身。例如,如果文件中间有一个字符串,并且要在字符串中间插入几个字符,则可以在字符串中的大约四个字符上编写指向文件末尾的指针,然后将这四个字符与首先要插入的字符一起写入末尾。这一切都与对数据进行排序有关。当然,只有当你自己编写整个文件时,你才能这样做,我的意思是你没有使用其他编解码器。