代码之家  ›  专栏  ›  技术社区  ›  Sandeep Datta

在二进制流上实现readline()最有效的方法是什么?

  •  1
  • Sandeep Datta  · 技术社区  · 16 年前

    如果我错了,请随时纠正我…

    我想读一本 CSV (逗号分隔值)使用.NET文件I/O类的文件。现在的问题是,这个csv文件可能包含一些字段,其中有些字段带有软回车(即,在文本文件中用于结束行的\r\n独立标记,而不是标准标记),并且标准文本模式I/O类streamreader不遵守标准约定,将软回车视为硬回车。因此,RNS会损害CSV文件的完整性。

    现在使用BinaryReader类似乎是唯一的选择,但BinaryReader没有readline()函数,因此需要自己实现readline()。

    我当前的方法一次从流中读取一个字符,并填充一个StringBuilder,直到获得一个(忽略所有其他字符,包括\r或\n单独字符),然后返回StringBuilder的字符串表示形式(使用ToString())。

    但我想知道:这是实现readline()函数最有效的方法吗?请启发我。

    7 回复  |  直到 10 年前
        1
  •  7
  •   configurator    16 年前

    可能是的。按照顺序,它只经过每个字符一次,所以它是O(N)(其中N是流的长度),所以这不是问题。要读取单个字符,最好使用二进制阅读器。

    我要做的就是上课

    public class LineReader : IDisposable
    {
        private Stream stream;
        private BinaryReader reader;
    
        public LineReader(Stream stream) { reader = new BinaryReader(stream); }
    
        public string ReadLine()
        {
            StringBuilder result = new StringBuilder();
            char lastChar = reader.ReadChar();
            // an EndOfStreamException here would propogate to the caller
    
            try
            {
                char newChar = reader.ReadChar();
                if (lastChar == '\r' && newChar == '\n')
                    return result.ToString();
    
                result.Append(lastChar);
                lastChar = newChar;
            }
            catch (EndOfStreamException)
            {
                result.Append(lastChar);
                return result.ToString();
            }
        }
    
        public void Dispose()
        {
            reader.Close();
        }
    }
    

    或者类似的。

    (警告:本规范未经测试,按原样提供,无任何明示或暗示的保证。如果该程序被证明有缺陷或破坏了地球,您将承担所有必要的维修、修理或修正的费用。)

        2
  •  1
  •   Kevin    16 年前

    您可能希望使用ODBC/OLEDB连接来完成此操作。如果将OLEDB连接的数据源指向包含csv文件的目录,则可以像查询每个csv文件一样进行查询。
    查看http://www.connectionstrings.com/?carrier=textfile>connectionstrings.com以获取正确的连接字符串

        3
  •  1
  •   TermWay    10 年前

    这里是BinaryReader类的扩展方法:

    using System.IO;
    using System.Text;
    
    public static class BinaryReaderExtension
    {
        public static string ReadLine(this BinaryReader reader)
        {
            if (reader.IsEndOfStream())
                return null;
    
            StringBuilder result = new StringBuilder();
            char character;
            while(!reader.IsEndOfStream() && (character = reader.ReadChar()) != '\n')
                if (character != '\r' && character != '\n')
                    result.Append(character);
    
            return result.ToString();
        }
    
        public static bool IsEndOfStream(this BinaryReader reader)
        {
            return reader.BaseStream.Position == reader.BaseStream.Length; 
        }
    }
    

    我没有在所有条件下进行测试,但这个代码对我有效。

        4
  •  0
  •   Lasse V. Karlsen    16 年前

    简单地预处理文件怎么样?

    用一些独特的东西替换软马车返回。

    对于记录来说,csv文件的数据中有换行符,这是糟糕的设计。

        5
  •  0
  •   Guge    16 年前

    您可以一次读取一个更大的块,使用encoder.getString将其解锁为字符串,然后使用string.split(“\r\n”)拆分为行,甚至使用string.substring(0,string.indexof(“\r\n”)挑选出字符串的头,剩下的部分留给下一行处理。记住将下一个读取操作添加到上一个读取的最后一行。

        6
  •  0
  •   MusiGenesis    16 年前

    你的方法听起来不错。提高方法效率的一种方法可能是将每一行存储在一个常规字符串(即,不是一个StringBuilder)中,然后将整个行字符串附加到StringBuilder中。见 this article 更进一步的解释是,在这里,StringBuilder并不是最好的选择。

    不过,这可能无关紧要。

        7
  •  0
  •   oazabir    13 年前

    这里有一个编码支持的更快的替代方案。它扩展了BinaryReader,因此您可以同时使用它来读取二进制块,还可以直接在二进制流上执行类似于流阅读器的readline。

    public class LineReader : BinaryReader
    {
        private Encoding _encoding;
        private Decoder _decoder;
    
        const int bufferSize = 1024;
        private char[] _LineBuffer = new char[bufferSize];
    
        public LineReader(Stream stream, int bufferSize, Encoding encoding)
            : base(stream, encoding)
        {
            this._encoding = encoding;
            this._decoder = encoding.GetDecoder();
        }
    
        public string ReadLine()
        {
            int pos = 0;
    
            char[] buf = new char[2];
    
            StringBuilder stringBuffer = null;
            bool lineEndFound = false;
    
            while(base.Read(buf, 0, 2) > 0)
            {
                if (buf[1] == '\r')
                {
                    // grab buf[0]
                    this._LineBuffer[pos++] = buf[0];
                    // get the '\n'
                    char ch = base.ReadChar();
                    Debug.Assert(ch == '\n');
    
                    lineEndFound = true;
                }
                else if (buf[0] == '\r')
                {
                    lineEndFound = true;
                }                    
                else
                {
                    this._LineBuffer[pos] = buf[0];
                    this._LineBuffer[pos+1] = buf[1];
                    pos += 2;
    
                    if (pos >= bufferSize)
                    {
                        stringBuffer = new StringBuilder(bufferSize + 80);
                        stringBuffer.Append(this._LineBuffer, 0, bufferSize);
                        pos = 0;
                    }
                }
    
                if (lineEndFound)
                {
                    if (stringBuffer == null)
                    {
                        if (pos > 0)
                            return new string(this._LineBuffer, 0, pos);
                        else
                            return string.Empty;
                    }
                    else
                    {
                        if (pos > 0)
                            stringBuffer.Append(this._LineBuffer, 0, pos);
                        return stringBuffer.ToString();
                    }
                }
            }
    
            if (stringBuffer != null)
            {
                if (pos > 0)
                    stringBuffer.Append(this._LineBuffer, 0, pos);
                return stringBuffer.ToString();
            }
            else
            {
                if (pos > 0)
                    return new string(this._LineBuffer, 0, pos);
                else
                    return null;
            }
        }
    
    }