Чтение строки в c # без усечения символа разделителя строк - PullRequest
2 голосов
/ 07 февраля 2010

У меня есть строка, которую я хочу читать построчно, но мне также нужно иметь символ разделителя строк, который, к сожалению, обрезает 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 ]

2 голосов
/ 07 февраля 2010

Вас беспокоит несоответствие между файлами (т. Е. Из 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;
            }
        }
    }
}

Это вернет вам начальную позицию каждой новой строки. Очевидно, что вы можете настроить это, чтобы сделать все, что вам нужно, то есть сделать что-то еще с реальными строками, которые вы прочитали.

Просто обратите внимание, что для создания байтового массива необходимо создать копию строки, поэтому она действительно не подходит для очень больших строк. Это немного лучше, чем подход char-by-char, но менее подвержен ошибкам, поэтому, возможно, стоит подумать, если длина строк не в мегабайтах.

0 голосов
/ 08 марта 2010
        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();
0 голосов
/ 07 февраля 2010

File.ReadAllText получит вам все содержимое файла. Ага. Все. Так что перед использованием лучше проверить этот размер файла.

EDIT:

прочитайте все это, затем создайте перечислитель, который выдает построчно.

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;

}
0 голосов
/ 07 февраля 2010

Если вам важна только позиция: ReadLine() перемещает вас на следующую строку. Если вы храните .Position потока под ним, вы можете сравнить его с .Position после следующего ReadLine(). Это длина строки, которую вы только что прочитали плюс разделитель. Длина разделителя составляет currentPosition - previousPosition - line.Length.

Таким образом, вы могли бы легко узнать, был ли это 1 или 2 байта (не зная деталей, но вы сказали, что все равно заботитесь только о позициях).

...