【问题标题】:Reading a line in c# without trimming the line delimiter character在c#中读取一行而不修剪行分隔符
【发布时间】:2010-02-06 23:28:57
【问题描述】:

我有一个字符串,我想逐行读取,但我还需要有行分隔符,不幸的是 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();

【问题讨论】:

  • 我的答案可能还有另一种选择,但这取决于数据的来源:它是某种流,还是源只是一个普通字符串?
  • 我确信它们会不一致,并且我无法更改输入,这被认为是只读的(克隆字符串并更改不会这样做,因为我需要其中的字符位置原始字符串)。然而,输入是一个纯字符串。
  • 在您对下面的 Aaronaught 的评论中,您写道:“设计为与 mono 和 .net2 兼容......因此无法做出任何假设”:您是否对行尾有一些有效的假设可以让这些是您正在解析的字符串的唯一来源吗?
  • @BillW:需要处理的文件将在 Windows 和 Linux 中复制和编辑。也有可能(已经发生在我身上),即使在单个文件中,行尾也会不同。

标签: c# string newline trim


【解决方案1】:

您是否担心文件之间(即来自 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;
            }
        }
    }
}

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

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

【讨论】:

  • 这是一个与 mono 和 .net2 兼容的库的一部分。它必须是故障安全的,因此不能做出任何假设。
【解决方案2】:

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

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

【讨论】:

  • 如何从 .NET 中的 StringReader 中获取流?我在文档中没有看到相应的功能。
  • Urgs。它没有。请原谅,错过了阅读器的“字符串”部分,并假设您将流传递给 StreamReader。如果你能做到这一点,我的建议可能会奏效并做你想做的事。如果你不能这样做,那么这是无用的废话,我可以删除它。
  • 查看 Aaronaught 获取职位的方法,并查看我的建议以了解这对您有何帮助。应该(tm)做到这一点。
【解决方案3】:

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;

}

【讨论】:

  • 他说输入已经是一个字符串,所以大概它适合内存。
  • 而且我需要逐行处理,所以阅读它是不行的。
【解决方案4】:
        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();

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2020-11-21
    • 1970-01-01
    • 1970-01-01
    • 2013-09-17
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多