2

我有一个要逐行读取的字符串,但我还需要有行分隔符,不幸的是 StringReader.ReadLine 会修剪它(与保存它的 ruby​​ 不同)。实现这一目标的最快和最稳健的方法是什么?

我一直在考虑的替代方案:

  • 每次读取输入的字符并检查行分隔符
  • 将 RegExp.Split 与积极的前瞻一起使用

或者,我只关心行分隔符,因为我需要知道字符串中的实际位置,并且分隔符可以是一个字符或一个字符长。因此,如果我能取回光标在字符串中的实际位置也会很好,但 StringReader 没有这个功能。

编辑:这是我目前的实现。通过返回一个空字符串来指定文件结束。

StringBuilder line = new StringBuilder();
int r = _input.Read();
while (r >= 0)
{
  char c = Convert.ToChar(r);
  line.Append(c);
  if (c == '\n') break;
  if (c == '\r')
  {
    int peek = _input.Peek();
    if (peek == -1) break;
    if (Convert.ToChar(peek) != '\n') break;
  }
  r = _input.Read();
}
return line.ToString();
4

4 回答 4

2

您是否担心文件之间(即来自 Unix/Mac 与 Windows)或文件内部的不一致?

如果您知道各个文件与其自身一致,那么一个非常简单的优化就是只逐个字符地读取第一行并找出分隔符是什么。然后确定任何其他线的确切位置将是简单的数学运算。

如果做不到这一点,我想我会走逐个角色的路线。正则表达式似乎太“聪明”了。这听起来像是一个复杂的功能,我认为最重要的是让它易于编写、阅读、理解,最重要的是调试。


还有另一种方法可以做到这一点,如果您的数据源是流,则效率会更高。不幸的是,正如您的评论中所引用的那样,您必须先创建一个;但是,无论如何我都会包含解决方案,它可能会给您一些启发:

public IEnumerable<int> GetLineStartIndices(string s)
{
    yield return 0;
    byte[] chars = Encoding.UTF8.GetBytes(s);
    using (MemoryStream stream = new MemoryStream(chars))
    {
        using (StreamReader reader = new StreamReader(stream, Encoding.UTF8))
        {
            while (reader.ReadLine() != null)
            {
                yield return stream.Position;
            }
        }
    }
}

这将为您返回每条新行的起始位置。显然你可以调整它来做你需要的任何其他事情,即用你阅读的实际行做其他事情。

请注意,这必须复制字符串来创建字节数组,所以它真的不适合非常大的字符串。不过,它比逐个字符的方法好一点,不易出错,因此如果字符串不是兆字节长,也许值得考虑。

于 2010-02-06T23:35:55.577 回答
0
        FileStream fs = new FileStream("E:\\hh.txt", FileMode.Open, FileAccess.Read);
        BinaryReader read = new BinaryReader(fs);
        byte[] ch = read.ReadBytes((int)fs.Length);
        byte[] che=new byte[(int)fs.Length];
        int size = (int)fs.Length,j=0;
        for ( int i =0; i <= (size-1); i++)
        {
            if (ch[i] != '|')
            {
                che[j] = ch[i];
                j++;
            }

        }
        richTextBox1.Text = Encoding.ASCII.GetString(che);
        read.Close();
        fs.Close();
于 2010-03-08T14:24:44.810 回答
0

如果您只关心位置:ReadLine()将您移至下一行。如果.Position将流的 存储在下面,则可以将其与.Position以下之后的进行比较ReadLine()。那是您刚刚读取的字符串的长度加上分隔符。分隔符的长度是currentPosition - previousPosition - line.Length

这样你就可以很容易地找出它是 1 个字节还是 2 个字节(不知道细节,但你说你只关心位置)。

于 2010-02-07T00:02:03.767 回答
0

File.ReadAllText 将为您提供所有文件内容。是的。全部。因此,您最好在使用之前检查该文件的大小。

编辑:

全部阅读,然后创建一个逐行产生的枚举器。

foreach(string line in Read("some.file"))
{ ... }


private IEnumerator Read(string file)
{
  string buffer = File.ReadAllText()
  for (int index=0;index<buffer.length;index++)
   {
      string line = ... logic to build a "line" here
      yield return line;
   }

   yield break;

}
于 2010-02-07T00:15:37.260 回答