C# streamreader Read(char[] buffer, int index, int count) 方法在 streamreader 对象 Position 大于 buffersize 后无法从文件中读取

C# streamreader Read(char[] buffer, int index, int count) method fails to read from file after streamreader object Position is greater than buffersize

更新:

我可以确认以下行为是我造成的 做一些我之前没有指定的事情,我正在用 reader charPos 属性 手动播放,因此问题可以重命名:"How to screw up your working fine Read(buffer,int,int) method" 答案是简单地手动设置reader (SR1) 位置在流 (FSr) 之外的位置 buffersize(不要与读取操作缓冲区混淆):

循环之前(在原问题的代码中)

 System.Reflection.FieldInfo charPos_private = typeof(StreamReader).GetField("charPos", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.DeclaredOnly);

并在循环内(在原始问题的代码中)

charPos_private.SetValue(SR1, string_index);

文件 reader 实际上读取到 1024,然后当文件流读取下一个 1024 个字符时它变为 0。我试图手动设置位置(因为我弄乱了一些模式)但我没有注意到它永远不会达到 1025。

然后,你就是这样把简单的事情搞砸的。 非常感谢所有评论!非常感激!我会将答案设置为包含有关如何正确执行此操作的示例的答案,如果不是我没有提到的那几行代码,我发布的代码也可以正常工作。


原题

第一次来这里,

我正在自学C#。我正在尝试使用 streamreader 从一个大的 UTF-8 Linux LF(结束于 \n)(一个 xml)逐个字符(或逐块)读取,并且我正在对它执行一些操作,然后将它逐个字符(或逐块)写入一个新文件。我有一个 streamreader 和 streamwriter.

我会文字说明,最后加点代码:

我正在查找流reader Read()Read(char[] buffer, int index, int count) 方法对大文件执行不同的操作。我知道这两个只不过是调用同一个方法的两种不同方式(我也尝试过ReadBlock)但是情况是:Read()方法自动填充StreamReader对象ByteBuffer(数组)动态地,即当 StreamReader 对象 Position 达到默认的 bufferSize 参数(通常为 1024 或 4096)时,方法 自动 开始 缓冲下一个 1024 或 4096 或任何缓冲区大小。

但是 Read(char[] buffer, int index, int count) 不会自动执行此操作 因此当 StreamReader 对象 Position 达到缓冲区大小 +1 时它会抛出异常。即在 1025 位置或 4097 位置 (char) (System.IndexOutofRangeException on System.Buffer.InternalBlockCopy(Array src, Int32 srcOffsetBytes, Array dst, Int32 dstOffsetBytes, Int32 byteCount)) 或如果我尝试使用 Peek() 查看接下来的内容(System.IndexOutofRangeException on System.IO.StreamReader.Peek())。我的测试文件是 300 MB。

*问题是:如何让 Read(char[] buffer, int index, int count) 自动重新缓冲 ByteBuffer(StreamReader:非 Public 成员 ByteBuffer)以便有效地读取大于缓冲区大小的文件?或者换句话说:我如何使用 Read(buffer_search, 0, x_number_of_chars) 实际读取大文件? *

我的意思是我不知道我是否需要通过系统反射手动修改 ByteBuffer 以及我将如何做。它应该是自动的;手动重新缓冲对于一件简单的事情来说就像太多的工作。

在代码中:(我在这里解释了一些代码)

做类似的事情:

char current_char;
using (System.IO.FileStream FSw = new FileStream(sourcePath, FileMode.Create))
{
    using (System.IO.StreamWriter SW1 = new StreamWriter(FSw, System.Text.Encoding.UTF8))
    {
        using (FileStream FSr = new FileStream(destinationPath, FileMode.Open))
        {
            using (StreamReader ofile_temp_chars = new StreamReader(fsr, System.Text.Encoding.UTF8))
            {
                while ((current_char = (char)SR1.Read()) != '\uffff')
                {
                    SW1.Write(current_char);
                }
            }
        }
    }
}

代码成功,没有问题。大文件被读入写入新文件。

但是当我尝试指定要读取的字符数时(我实际上必须读取用户定义的字符数,我只是在这里展示一些代码,只读取一个字符以简化)然后我需要使用 Read(char[] buffer, int index, int count),像这样:

char[] buffer_search = new char[1]
using (System.IO.FileStream FSw = new FileStream(fePath, FileMode.Create))
{
    using (System.IO.StreamWriter SW1 = new StreamWriter(FSw, System.Text.Encoding.UTF8))
    {
        using (FileStream FSr = new FileStream(fPath, FileMode.Open))
        {
            using (StreamReader ofile_temp_chars = new StreamReader(fsr, System.Text.Encoding.UTF8))
            {
                while (SR1.Peek() != -1)
                {
                    SR1.Read(buffer_search, 0, 1);
                    SW1.Write(buffer_search[0]);
                }
            }
        }
    }
}

当流reader 对象 Position 达到并超过缓冲区大小(即 1025、4097)等时,该代码将以异常结束((System.IndexOutofRangeException on System.IO.StreamReader.Peek() )等。 .. 显然是从它在缓冲区上的内容而不是在文件本身上偷看,并且不会自动重新缓冲导致在 ByteBuffer char[] 之外偷看。

如果我这样做:

char[] buffer_search = new char[1]
using (System.IO.FileStream FSw = new FileStream(fePath, FileMode.Create))
{
    using (System.IO.StreamWriter SW1 = new StreamWriter(FSw, System.Text.Encoding.UTF8))
    {
        using (FileStream FSr = new FileStream(fPath, FileMode.Open))
        {
            using (StreamReader SR1 = new StreamReader(fsr, System.Text.Encoding.UTF8))
            {
                while (!end_of_file)
                {
                    try { SR1.Read(buffer_search, 0, 1); }
                    catch { end_of_file = true; }
                    SW1.Write(buffer_search[0]);
                }
            }
        }
    }
}

然后我将得到一个仅包含 1024 个字符或缓冲区大小的文件。将抛出的异常(捕获)将是: System.IndexOutOfRangeException on System.Buffer.InternalBlockCopy(Array src, Int32 srcOffsetBytes, Array dst, Int32 dstOffsetBytes, Int32 byteCount) 在 System.IO.StreamReader.Read(Char[] 缓冲区,Int32 索引,Int32 计数)

所以在这两种情况下,结果是相同的,缓冲区没有从文件中获取新数据,这是由 Read() 和 ReadLine() 方法自动处理的。

像增加缓冲区大小这样的简单解决方案将不起作用,因为我的文件有数百 MB,而且我正在努力提高内存效率...(或者更简单,比如使用 Read(),因为我需要使用 Read(buffer, 0, x_number_of_chars)。这应该是一件简单的事情,但花费的时间比预期的要长。

感谢您的帮助,

真不清楚你在问什么。但是,如果你想从一个流 reader 中读取任意数量的字符并将它们写入写入器,这可行:

int bytesRead;
do
{
    bytesRead = SR1.Read(buffer_search, 0, buffer_search.Length);
    if (bytesRead > 0)
    {
        // TODO: process buffer_search in some way.
        SW1.Write(buffer_search, 0, bytesRead);
    }
} while (bytesRead > 0);

这将在需要时将新字符读入内部流写入器缓冲区。