.NET流CopyTo错误?

问题描述

我有一堆CSV文件,所有文件的第一行都有标题行。 我需要将所有这些CSV文件合并到一个文件中,只复制一次标头,并将其保留为合并文件的第一行。

我编写了以下代码:

public static void Merge( string outputFile,params string[] inputFiles )
{
    if( inputFiles == null || inputFiles.Length <= 1 ) return;

    using( Stream outputStream = new FileStream( outputFile,FileMode.Append,FileAccess.Write,FileShare.None ) )
    {
        for( int i = 0; i < inputFiles.Length; i++ )
        {
            var inputFile = inputFiles[ i ];

            using( var inputStream = File.OpenRead( inputFile ) )
            using( var textReader = new StreamReader( inputStream ) )
            {
                if( i != 0 )
                    textReader.ReadLine();

                textReader.BaseStream.CopyTo( outputStream );
            }
        }
    }
}

上面的代码正确地跳过了每个文件的第一行(完全复制为输出的第一个文件除外),但是未能正确地写入每个文件的第二行(大约前半部分)每个文件的第二行都没有),然后从第三行开始按预期工作。

似乎是流位置问题或CopyTo方法中的错误。任何想法吗?

P.S:使用下面的代码可以很容易地解决问题,但是我真的很想知道上面的代码有什么问题。谢谢。

public static void Merge( string outputFile,string inputDir,string filtro )
{
    if( String.IsNullOrEmpty( filtro ) )
        filtro = "*.*";

    var inputFiles = Directory.GetFiles( inputDir,filtro );

    using( FileStream outputStream = new FileStream( outputFile,FileShare.None ) )
    {
        using( var sw = new StreamWriter( outputStream ) )
        {
            for( int i = 0; i < inputFiles.Length; i++ )
            {
                var inputFile = inputFiles[ i ];

                using( var inputStream = File.OpenRead( inputFile ) )
                using( var textReader = new StreamReader( inputStream ) )
                {
                    if( i != 0 && textReader.BaseStream.Position != textReader.BaseStream.Length )
                        textReader.ReadLine();

                    while( textReader.BaseStream.Position != textReader.BaseStream.Length )
                        sw.WriteLine( textReader.ReadLine() );
                }
            }
        }
    }
}

解决方法

问题是缓冲之一。

除非您很幸运,否则您使用StreamReader跳过1行实际上会跳过1行以上。

如果选中reference source,将会看到StreamReader使用缓冲区,并在需要时尝试填充缓冲区。因此,很有可能它所获得的不仅仅是当前行的末尾。如果文件的前几行很短,则读取的第一个缓冲区也很可能从文件开始抓取了好几行。对于参考源,默认缓冲区大小似乎是1024或4096,具体取决于您的框架类型和版本。

然后,当您绕过读取器并使用基础流时,它将位于读取器最后一次读取缓冲区之后。这就是为什么它从某行的中间开始。

现在,您可以通过多种方法来执行此操作,但是您可以将整个过程重写为延迟评估的LINQ查询,并摆脱所有代码。

public static void Merge( string outputFile,string inputDir,string filtro )
{
    if( String.IsNullOrEmpty( filtro ) )
        filtro = "*.*";

    var inputFiles = Directory.GetFiles( inputDir,filtro );
    File.AppendAllLines(outputFile,inputFiles
        .SelectMany((inputFile,index) =>
            File.ReadLines(inputFile).Skip(index == 0 ? 0 : 1)));
}

相关问答

错误1:Request method ‘DELETE‘ not supported 错误还原:...
错误1:启动docker镜像时报错:Error response from daemon:...
错误1:private field ‘xxx‘ is never assigned 按Alt...
报错如下,通过源不能下载,最后警告pip需升级版本 Requirem...