【问题标题】:C# Streamreader - Break on {CR}{LF} onlyC# Streamreader - 仅在 {CR}{LF} 上中断
【发布时间】:2016-07-12 15:19:07
【问题描述】:

我正在尝试在执行复杂的 SSIS 插入包之前计算文本文件中的行数(以与控制文件进行比较)。

目前我正在使用 StreamReader,它正在中断一行,并将 {LF} 嵌入到新行中,而 SSIS 正在使用 {CR}{LF}(正确),因此计数没有统计。

有谁知道这样做的替代方法,我可以仅根据 {CR}{LF} 换行符来计算文件中的行数?

提前致谢

【问题讨论】:

  • 您可以自己读取文件并根据需要拆分;只需读取字节,当您遇到 {CR}{LF} 时,只需开始新行。
  • ^- 这就是 StreamReader 在幕后工作的方式。那么它在 CR、LF 和 CRLF 上分裂
  • 也许这有帮助.. 快速的自定义流阅读器也摆脱了其他换行问题(见帖子)stackoverflow.com/questions/17994130/…
  • @Clint:除非您确定文件编码是单字节字符集,否则读取字节并不是一件特别安全的事情。事实上,如果文件是 16 位 Unicode,则该 CR/LF 对被编码为 4 个字节:0x00、0x0D、0x00、0x0A。现在,如果您将其作为文本打开(带有StreamReader)并读取字符。 . .

标签: c# .net text-files streamreader flat-file


【解决方案1】:

这是一个扩展方法,它只读取带有行分隔符 {Cr}{Lf} 而不是 {LF} 的行。你可以数一数。

var count=  new StreamReader(@"D:\Test.txt").ReadLinesCrLf().Count()

但也可以使用它来读取文件,有时很有用,因为正常的 StreamReader.ReadLine 在 {Cr}{Lf} 和 {LF} 上都会中断。可以在任何 TextReader 上使用并且可以流式传输(文件大小不是问题)。

    public static IEnumerable<string> ReadLinesCrLf(this TextReader reader, int bufferSize = 4096)
    {
        StringBuilder lineBuffer = null;

        //read buffer            
        char[] buffer = new char[bufferSize];
        int charsRead;

        var previousIsLf = false;

        while ((charsRead = reader.Read(buffer, 0, bufferSize)) != 0)
        {
            int bufferIndex = 0;
            int writeIdx = 0;
            do
            {
                var currentChar = buffer[bufferIndex];
                switch (currentChar)
                {
                    case '\n':
                        if (previousIsLf)
                        {
                            if (lineBuffer == null)
                            {
                                //return from current buffer writeIdx could be higher than 0 when multiple rows are in the buffer                                            
                                yield return new string(buffer, writeIdx, bufferIndex - writeIdx - 1);
                                //shift write index to next character that will be read
                                writeIdx = bufferIndex + 1;
                            }
                            else
                            {
                                Debug.Assert(writeIdx == 0, $"Write index should be 0, when linebuffer != null");
                                lineBuffer.Append(buffer, writeIdx, bufferIndex - writeIdx);
                                Debug.Assert(lineBuffer.ToString().Last() == '\r',$"Last character in linebuffer should be a carriage return now");
                                lineBuffer.Length--;
                                //shift write index to next character that will be read
                                writeIdx = bufferIndex + 1;
                                yield return lineBuffer.ToString();
                                lineBuffer = null;
                            }
                        }

                        previousIsLf = false;
                        break;
                    case '\r':
                        previousIsLf = true;
                        break;
                    default:
                        previousIsLf = false;
                        break;

                }


                bufferIndex++;
            } while (bufferIndex < charsRead);
            if (writeIdx < bufferIndex)
            {
                if (lineBuffer == null) lineBuffer = new StringBuilder();
                lineBuffer.Append(buffer, writeIdx, bufferIndex - writeIdx);
            }
        }
        //return last row
        if (lineBuffer != null && lineBuffer.Length > 0) yield return lineBuffer.ToString();
    }

【讨论】:

    【解决方案2】:

    {CR}{LF} 是所需的。真的不能说哪个是对的。

    因为 ReadLine 去掉了你不知道的行尾

    使用 StreamReader.Read Method () 并查找 13,然后是 10
    它返回 Int

    【讨论】:

      【解决方案3】:

      遍历文件并计算 CRLF 的数量。

      非常简单的实现:

      public int CountLines(Stream stream, Encoding encoding)
      {
          int cur, prev = -1, lines = 0;
          using (var sr = new StreamReader(stream, encoding, false, 4096, true))
          {
              while ((cur = sr.Read()) != -1)
              {
                  if (prev == '\r' && cur == '\n')
                      lines++;
      
                  prev = cur;
              }
          }
      
          //Empty stream will result in 0 lines, any content would result in at least one line
          if (prev != -1)
              lines++;
      
          return lines;
      }
      

      示例用法:

      using(var s = File.OpenRead(@"<your_file_path>"))
          Console.WriteLine("Found {0} lines", CountLines(s, Encoding.Default));
      

      其实是在字符串任务中查找子字符串。可以使用更通用的算法。

      【讨论】:

      • 希望你不会遇到一个字符的最后一个字节等于 CR,而下一个字符的第一个字节是 LF 的 Unicode 文件。你真的应该在你的回答中注意到,这个解决方案不能保证适用于任何 Unicode 编码。
      • @JimMischel 感谢您指出这一点。原始答案在任何多字节编码上都会失败。修好了。
      【解决方案4】:

      这是一种非常懒惰的方式...这会将整个文件读入内存。

      var cnt = File.ReadAllText("yourfile.txt")
                    .Split(new[] { "\r\n" }, StringSplitOptions.None)
                    .Length;
      

      【讨论】:

      • 由于大小的原因,一次性读取整个文件不是一种选择,但对于较小的文件来说是一个不错的解决方案。
      猜你喜欢
      • 1970-01-01
      • 2014-08-06
      • 2011-03-07
      • 1970-01-01
      • 2010-12-04
      • 1970-01-01
      • 1970-01-01
      • 2013-06-26
      • 1970-01-01
      相关资源
      最近更新 更多