Есть ли опция «перейти к строке» в TextReader / StreamReader? - PullRequest
8 голосов
/ 31 мая 2009

У меня огромный текстовый файл с 25k строками. Внутри этого текстового файла каждая строка начинается с "1 \ t (белья)"

Пример:

1   1   ITEM_ETC_GOLD_01    골드(소)   xxx xxx xxx_TT_DESC 0   0   3   3   5   0   180000  3   0   1   0   0   255 1   1   0   0   0   0   0   0   0   0   0   0   -1  0   -1  0   -1  0   -1  0   -1  0   0   0   0   0   0   0   100 0   0   0   xxx item\etc\drop_ch_money_small.bsr    xxx xxx xxx 0   2   0   0   1   0   0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0   0   0   0   0   0   0   0   0   0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1   표현할 골드의 양(param1이상) -1  xxx -1  xxx -1  xxx -1  xxx -1  xxx -1  xxx -1  xxx -1  xxx -1  xxx -1  xxx -1  xxx -1  xxx -1  xxx -1  xxx -1  xxx -1  xxx -1  xxx -1  xxx -1  xxx 0   0
1   2   ITEM_ETC_GOLD_02    골드(중)   xxx xxx xxx_TT_DESC 0   0   3   3   5   0   180000  3   0   1   0   0   255 1   1   0   0   0   0   0   0   0   0   0   0   -1  0   -1  0   -1  0   -1  0   -1  0   0   0   0   0   0   0   100 0   0   0   xxx item\etc\drop_ch_money_normal.bsr   xxx xxx xxx 0   2   0   0   1   0   0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0   0   0   0   0   0   0   0   0   0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1000    표현할 골드의 양(param1이상) -1  xxx -1  xxx -1  xxx -1  xxx -1  xxx -1  xxx -1  xxx -1  xxx -1  xxx -1  xxx -1  xxx -1  xxx -1  xxx -1  xxx -1  xxx -1  xxx -1  xxx -1  xxx -1  xxx 0   0
1   3   ITEM_ETC_GOLD_03    골드(대)   xxx xxx xxx_TT_DESC 0   0   3   3   5   0   180000  3   0   1   0   0   255 1   1   0   0   0   0   0   0   0   0   0   0   -1  0   -1  0   -1  0   -1  0   -1  0   0   0   0   0   0   0   100 0   0   0   xxx item\etc\drop_ch_money_large.bsr    xxx xxx xxx 0   2   0   0   1   0   0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0   0   0   0   0   0   0   0   0   0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 10000   표현할 골드의 양(param1이상) -1  xxx -1  xxx -1  xxx -1  xxx -1  xxx -1  xxx -1  xxx -1  xxx -1  xxx -1  xxx -1  xxx -1  xxx -1  xxx -1  xxx -1  xxx -1  xxx -1  xxx -1  xxx -1  xxx 0   0
1   4   ITEM_ETC_HP_POTION_01   HP 회복 약초    xxx SN_ITEM_ETC_HP_POTION_01    SN_ITEM_ETC_HP_POTION_01_TT_DESC    0   0   3   3   1   1   180000  3   0   1   1   1   255 3   1   0   0   1   0   60  0   0   0   1   21  -1  0   -1  0   -1  0   -1  0   -1  0   0   0   0   0   0   0   100 0   0   0   xxx item\etc\drop_ch_bag.bsr    item\etc\hp_potion_01.ddj   xxx xxx 50  2   0   0   1   0   0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0   0   0   0   0   0   0   0   0   0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 120 HP회복양   0   HP회복양(%)    0   MP회복양   0   MP회복양(%)    -1  xxx -1  xxx -1  xxx -1  xxx -1  xxx -1  xxx -1  xxx -1  xxx -1  xxx -1  xxx -1  xxx -1  xxx -1  xxx -1  xxx -1  xxx -1  xxx 0   0
1   5   ITEM_ETC_HP_POTION_02   HP 회복약 (소)  xxx SN_ITEM_ETC_HP_POTION_02    SN_ITEM_ETC_HP_POTION_02_TT_DESC    0   0   3   3   1   1   180000  3   0   1   1   1   255 3   1   0   0   1   0   110 0   0   0   2   39  -1  0   -1  0   -1  0   -1  0   -1  0   0   0   0   0   0   0   100 0   0   0   xxx item\etc\drop_ch_bag.bsr    item\etc\hp_potion_02.ddj   xxx xxx 50  2   0   0   2   0   0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0   0   0   0   0   0   0   0   0   0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 220 HP회복양   0   HP회복양(%)    0   MP회복양   0   MP회복양(%)    -1  xxx -1  xxx -1  xxx -1  xxx -1  xxx -1  xxx -1  xxx -1  xxx -1  xxx -1  xxx -1  xxx -1  xxx -1  xxx -1  xxx -1  xxx -1  xxx 0   0
1   6   ITEM_ETC_HP_POTION_03   HP 회복약 (중)  xxx SN_ITEM_ETC_HP_POTION_03    SN_ITEM_ETC_HP_POTION_03_TT_DESC    0   0   3   3   1   1   180000  3   0   1   1   1   255 3   1   0   0   1   0   200 0   0   0   4   70  -1  0   -1  0   -1  0   -1  0   -1  0   0   0   0   0   0   0   100 0   0   0   xxx item\etc\drop_ch_bag.bsr    item\etc\hp_potion_03.ddj   xxx xxx 50  2   0   0   3   0   0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0   0   0   0   0   0   0   0   0   0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 370 HP회복양   0   HP회복양(%)    0   MP회복양   0   MP회복양(%)    -1  xxx -1  xxx -1  xxx -1  xxx -1  xxx -1  xxx -1  xxx -1  xxx -1  xxx -1  xxx -1  xxx -1  xxx -1  xxx -1  xxx -1  xxx -1  xxx 0   0
1   7   ITEM_ETC_HP_POTION_04   HP 회복약 (대)  xxx SN_ITEM_ETC_HP_POTION_04    SN_ITEM_ETC_HP_POTION_04_TT_DESC    0   0   3   3   1   1   180000  3   0   1   1   1   255 3   1   0   0   1   0   400 0   0   0   7   140 -1  0   -1  0   -1  0   -1  0   -1  0   0   0   0   0   0   0   100 0   0   0   xxx item\etc\drop_ch_bag.bsr    item\etc\hp_potion_04.ddj   xxx xxx 50  2   0   0   4   0   0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0   0   0   0   0   0   0   0   0   0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 570 HP회복양   0   HP회복양(%)    0   MP회복양   0   MP회복양(%)    -1  xxx -1  xxx -1  xxx -1  xxx -1  xxx -1  xxx -1  xxx -1  xxx -1  xxx -1  xxx -1  xxx -1  xxx -1  xxx -1  xxx -1  xxx -1  xxx 0   0

Вопрос: Как мне напрямую прочитать, например, строку 5?

Ответы [ 5 ]

10 голосов
/ 31 мая 2009

Вы можете использовать мой класс LineReader (класс MiscUtil или простая версия ) для реализации IEnumerable<string>, а затем использовать LINQ:

string line5 = new LineReader(file).Skip(4).First();

Это предполагает .NET 3.5, по общему признанию. В противном случае откройте TextReader (например, с помощью File.OpenText) и просто позвоните ReadLine() четыре раза, чтобы пропустить ненужные строки, а затем еще раз прочитайте пятую строку.

Нет способа «сократить» это, если вы точно не знаете, сколько байтов в каждой строке.

3 голосов
/ 31 мая 2009

Вы не можете перейти непосредственно к строке в текстовом файле, если каждая строка не имеет фиксированной ширины и вы используете кодировку с фиксированной шириной (то есть не UTF-8 - который является одним из наиболее распространенных в настоящее время).

Единственный способ сделать это - прочитать строки и отбросить те, которые вам не нужны.

В качестве альтернативы, вы можете поместить индекс вверху файла (или во внешний файл), который сообщает ему (например), что строка 1000 начинается со смещения байта [x], строка 2000 начинается со смещения байта [y] и т. д. Затем используйте .Position или .Seek() на FileStream, чтобы перейти к ближайшей индексированной точке и идти вперед.

Предполагая, что самый простой подход (без индекса), код в примере Джона должен работать нормально. Если вы не хотите LINQ, вы можете создать нечто подобное в .NET 2.0 + C # 2.0:

// to read multiple lines in a block
public static IEnumerable<string> ReadLines(
        string path, int lineIndex, int count) {
    if (string.IsNullOrEmpty(path)) throw new ArgumentNullException("path");
    if (lineIndex < 0) throw new ArgumentOutOfRangeException("lineIndex");
    if (count < 0) throw new ArgumentOutOfRangeException("count");
    using (StreamReader reader = File.OpenText(path)) {
        string line;
        while (count > 0 && (line = reader.ReadLine()) != null) {
            if (lineIndex > 0) {
                lineIndex--; // skip
                continue;
            }
            count--;
            yield return line;
        }
    }
}
// to read a single line
public static string ReadLine(string path, int lineIndex) {
    foreach (string line in ReadLines(path, lineIndex, 1)) {
        return line;
    }
    throw new IndexOutOfRangeException();
}

Если вам нужно проверить значения строки (а не просто индекс строки), то это тоже достаточно просто; просто настройте блок итератора.

3 голосов
/ 31 мая 2009

Если вы имеете дело с форматом данных фиксированной ширины (т.е. вы знаете, что все строки имеют одинаковую длину), вы можете умножить длину на желаемый номер строки и использовать Stream.Seek, чтобы найти начальную точку n-я строка.

Если строки не имеют фиксированной длины, вам нужно найти правильное количество разрывов строк, пока вы не окажетесь в начале нужной строки. Это было бы проще всего сделать с StreamReader.ReadLine. (Вы можете создать метод расширения, чтобы сделать файл en IEnumerable , как предлагает Джон Скит, - это даст вам более приятный синтаксис, но под капотом вы будете использовать ReadLine).

Если производительность является проблемой, может быть (немного) более эффективно сканировать байтовые последовательности в файле вручную, используя метод Stream.Read. Я не проверял это; но StreamReader, очевидно, нужно проделать некоторую работу для создания строки из последовательности байтов - если вам не нужны первые строки, эту работу можно сохранить, поэтому теоретически вы должны иметь возможность сделать метод сканирования, который работает лучше , Однако для вас это будет намного больше.

1 голос
/ 31 мая 2009

Если вы собираетесь искать в файле много разных строк (но не все), то вы можете получить некоторую выгоду от построения индекса по ходу работы. Используйте любое из предложений, которые уже здесь, но по мере того, как вы продвигаетесь, создайте массив смещений байтов для любых строк, которые вы уже нашли, чтобы вы могли избежать повторного сканирования файла с самого начала каждый раз. 1001 *

ДОПОЛНЕНИЕ:
Есть еще один способ сделать это быстро, если вам нужна только случайная «случайная» строка, но за счет более сложного поиска (если ответ Джона достаточно быстр, я бы определенно придерживался этого для простоты).

Вы могли бы выполнить «бинарный поиск», просто начав поискать на полпути вниз по файлу последовательности «1», и первое обнаруженное совпадение даст вам представление о том, какой номер строки вы нашли; затем в зависимости от того, где искомая линия относится к найденному числу, которое вы рекурсивно разделяете.

Для дополнительной производительности вы также можете сделать предположение, что строки примерно одинаковой длины, и алгоритм "угадывает" приблизительное положение искомой строки относительно общего количества строк в файле, а затем выполнить этот поиск оттуда и далее. Если вы не хотите делать предположения о длине файла, вы можете даже сделать его самонаправленным, просто сначала разделив его пополам, и используя номер строки, который он находит первым в качестве приблизительного значения количества строк в файле в целом.

Определенно нетривиально реализовать, но если у вас много произвольного доступа к файлам с большим количеством строк, это может окупиться приростом производительности.

0 голосов
/ 31 мая 2009

Если вам нужно перейти к строке 24 000 с помощью функции, которая выполняет ReadLine () в фоновом режиме, это будет немного медленно.

Если номер строки велик, вы можете сделать какое-то обоснованное предположение о том, где в файле может быть строка, и начать читать оттуда. Таким образом, чтобы добраться до строки 24 567, вам не нужно сначала читать 24 566 строк. Вы можете перейти к где-то посередине, чтобы узнать, на какой линии вы находитесь, основываясь на числе после / t, а затем отсчитывать оттуда.

Некоторое время назад я работал с разработчиком, который должен был создавать БД перед СУБД, где это было распространено. Его решение вашей проблемы было похоже на то, о чем я только что написал, но в его случае он держал карту в отдельном файле. Карта может сопоставить каждую сотую строку с ее местоположением в документе. Такая карта может быть загружена очень быстро, и это может увеличить время чтения. В то время его система была очень быстрой и эффективной для чтения только данных, но не очень хорошо для чтения / записи данных. (каждый раз, когда вы меняете линии, вы должны менять всю карту, это не очень эффективно)

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...