在不修剪行分隔符的情况下读取c#中的行

在不修剪行分隔符的情况下读取c#中的行,c#,string,newline,trim,C#,String,Newline,Trim,我有一个字符串,我想逐行读取,但我还需要一个行分隔符字符,StringReader.ReadLine不幸地修剪了它(与ruby中保留它的地方不同)。实现这一目标的最快和最可靠的方法是什么 我一直在考虑的备选方案: 逐个字符读取输入并每次检查行分隔符 使用带有正向前瞻的RegExp.Split 或者,我只关心行分隔符,因为我需要知道字符串中的实际位置,并且分隔符可以是一个或多个字符长。因此,如果我能够返回光标在字符串中的实际位置也很好,但是StringReader没有这个功能 编辑:这是我当前

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

您是否担心文件之间(即来自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;
            }
        }
    }
}
public IEnumerable GetLineStartIndices(字符串s)
{
收益率为0;
byte[]chars=Encoding.UTF8.GetBytes(s);
使用(MemoryStream流=新的MemoryStream(chars))
{
使用(StreamReader=newstreamreader(stream,Encoding.UTF8))
{
while(reader.ReadLine()!=null)
{
收益率回归流位置;
}
}
}
}
这将返回每条新线的起始位置。显然,你可以调整它来做任何你需要的事情,也就是说,用你读的实际行做一些其他的事情


请注意,这必须复制字符串以创建字节数组,因此它实际上不适合非常大的字符串。不过,它比逐字符的方法要好一点,不太容易出现错误,因此如果字符串不是兆字节长,那么可能值得考虑。

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


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

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;

}
foreach(读取中的字符串行(“some.file”))
{ ... }
私有IEnumerator读取(字符串文件)
{
字符串缓冲区=File.ReadAllText()
对于(int index=0;index
FileStream fs=newfilestream(“E:\\hh.txt”,FileMode.Open,FileAccess.Read));
BinaryReader read=新的BinaryReader(fs);
byte[]ch=read.ReadBytes((int)fs.Length);
字节[]che=新字节[(int)fs.Length];
int size=(int)fs.Length,j=0;

对于(int i=0;i)我的答案可能还有另一种选择,但这取决于数据来自何处:它是某种流,还是源只是一个普通字符串?我确信它们会不一致,并且我无法更改输入,这被认为是只读的(克隆字符串并进行更改是不行的,因为我需要原始字符串中的字符位置)。但是,输入是一个普通字符串。在下面对Aaronaught的评论中,您写道:“设计为与mono和.net2兼容……因此无法做出任何假设”:如果行尾是您正在分析的字符串的唯一来源,那么您是否可以对行尾进行一些有效的假设?@BillW:需要处理的文件将被复制到Windows和Linux中,并使用Windows和Linux进行编辑。这也是可能的(我已经遇到过这种情况),即使在单个文件中,行尾也会有所不同。这是一个库的一部分,旨在与mono和.net2兼容。它必须是故障安全的,因此不能进行任何假设。如何从.NET中的StringReader中获取流?我在documentation.Urgs中没有看到适用于此的函数。它没有。对不起,错过了“字符串”作为读者的一部分,我假设你会将一条流传递给一个流阅读器。如果你能做到这一点,我的建议可能会起作用,并做你想做的事情。如果你不能做到这一点,那么这是无用的废话,我可以删除它。请参阅Aaronaught了解获得职位的方法,并查看我的建议,以了解这对你有何帮助。应该(tm)做这个把戏。他说输入已经是一个字符串了,所以它可能适合内存。我需要一行一行地处理它,所以读取它是不可能的。
        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();