How to insert characters to a file using C#

2020-01-26 10:43发布

I have a huge file, where I have to insert certain characters at a specific location. What is the easiest way to do that in C# without rewriting the whole file again.

10条回答
Summer. ? 凉城
2楼-- · 2020-01-26 10:55

Why don't you put a pointer to the end of the file (literally, four bytes above the current size of the file) and then, on the end of file write the length of inserted data, and finally the data you want to insert itself. For example, if you have a string in the middle of the file, and you want to insert few characters in the middle of the string, you can write a pointer to the end of file over some four characters in the string, and then write that four characters to the end together with the characters you firstly wanted to insert. It's all about ordering data. Of course, you can do this only if you are writing the whole file by yourself, I mean you are not using other codecs.

查看更多
做个烂人
3楼-- · 2020-01-26 10:58

Filesystems do not support "inserting" data in the middle of a file. If you really have a need for a file that can be written to in a sorted kind of way, I suggest you look into using an embedded database.

You might want to take a look at SQLite or BerkeleyDB.

Then again, you might be working with a text file or a legacy binary file. In that case your only option is to rewrite the file, at least from the insertion point up to the end.

I would look at the FileStream class to do random I/O in C#.

查看更多
smile是对你的礼貌
4楼-- · 2020-01-26 10:58

You will always have to rewrite the remaining bytes from the insertion point. If this point is at 0, then you will rewrite the whole file. If it is 10 bytes before the last byte, then you will rewrite the last 10 bytes.

In any case there is no function to directly support "insert to file". But the following code can do it accurately.

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);

To gain better performance for file IO, play with "magic two powered numbers" like in the code above. The creation of the file uses a buffer of 262144 bytes (256KB) that does not help at all. The same buffer for the insertion does the "performance job" as you can see by the StopWatch results if you run the code. A draft test on my PC gave the following results:

13628.8 ms for creation and 3597.0971 ms for insertion.

Note that the target byte for insertion is 10, meaning that almost the whole file was rewritten.

查看更多
闹够了就滚
5楼-- · 2020-01-26 11:04

You will probably need to rewrite the file from the point you insert the changes to the end. You might be best always writing to the end of the file and use tools such as sort and grep to get the data out in the desired order. I am assuming you are talking about a text file here, not a binary file.

查看更多
三岁会撩人
6楼-- · 2020-01-26 11:06

There is no way to insert characters in to a file without rewriting them. With C# it can be done with any Stream classes. If the files are huge, I would recommend you to use GNU Core Utils inside C# code. They are the fastest. I used to handle very large text files with the core utils ( of sizes 4GB, 8GB or more etc ). Commands like head, tail, split, csplit, cat, shuf, shred, uniq really help a lot in text manipulation.

For example if you need to put some chars in a 2GB file, you can use split -b BYTECOUNT, put the ouptut in to a file, append the new text to it, and get the rest of the content and add to it. This should supposedly be faster than any other way.

Hope it works. Give it a try.

查看更多
Luminary・发光体
7楼-- · 2020-01-26 11:07

You may take a look at this project: Win Data Inspector

Basically, the code is the following:

// 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);
    }
}
查看更多
登录 后发表回答