Получение одной строки в огромном файле с ударом

Как я могу получить конкретную строку в текстовом файле на 3 ГБ. Все строки имеют:

  • та же длина, и
  • разграничены \n.

И я должен смочь получить любую строку по требованию.

Как это может быть сделано? Только одна строка должна быть возвращенной.

17
задан agc 24 November 2017 в 17:14
поделиться

6 ответов

Если все строки имеют одинаковую длину, лучшим способом будет использовать dd (1) и задайте ему параметр пропуска.

Пусть размер блока будет длиной каждой строки (включая новую строку), тогда вы можете сделать:

$ dd if=filename bs=<line-length> skip=<line_no - 1> count=1 2>/dev/null

Идея состоит в том, чтобы искать мимо всех предыдущих строк ( skip = ) и прочтите одну строку ( count = 1 ). Поскольку размер блока установлен равным длине строки ( bs = ), каждый блок фактически представляет собой одну строку. Перенаправьте stderr, чтобы в конце не получить надоедливую статистику.

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

21
ответ дан 30 November 2019 в 11:08
поделиться

head -10 file | tail -1 возвращает строку 10, вероятно, медленно.

from here

# print line number 52 
sed -n '52p' # method 1 
sed '52!d' # method 2 
sed '52q;d' # method 3, efficient on large files
15
ответ дан 30 November 2019 в 11:08
поделиться

Используйте q с sed , чтобы остановить поиск после того, как строка была напечатана.

sed -n '11723{p;q}' filename

Python (минимальная проверка ошибок):

#!/usr/bin/env python
import sys

# by Dennis Williamson - 2010-05-08
# for http://stackoverflow.com/questions/2794049/getting-one-line-in-a-huge-file-with-bash

# seeks the requested line in a file with a fixed line length

# Usage: ./lineseek.py LINE FILE

# Example: ./lineseek 11723 data.txt

EXIT_SUCCESS      = 0
EXIT_NOT_FOUND    = 1
EXIT_OPT_ERR      = 2
EXIT_FILE_ERR     = 3
EXIT_DATA_ERR     = 4

# could use a try block here
seekline = int(sys.argv[1])

file = sys.argv[2]

try:
    if file == '-':
        handle = sys.stdin
        size = 0
    else:
        handle = open(file,'r')
except IOError as e:
    print >> sys.stderr, ("File Open Error")
    exit(EXIT_FILE_ERR)

try:
    line = handle.readline()
    lineend = handle.tell()
    linelen = len(line)
except IOError as e:
    print >> sys.stderr, ("File I/O Error")
    exit(EXIT_FILE_ERR)

# it would be really weird if this happened
if lineend != linelen:
    print >> sys.stderr, ("Line length inconsistent")
    exit(EXIT_DATA_ERR)

handle.seek(linelen * (seekline - 1))

try:
    line = handle.readline()
except IOError as e:
    print >> sys.stderr, ("File I/O Error")
    exit(EXIT_FILE_ERR)

if len(line) != linelen:
    print >> sys.stderr, ("Line length inconsistent")
    exit(EXIT_DATA_ERR)

print(line)

Проверка аргументов должна быть намного лучше, и есть место для многих других улучшений.

2
ответ дан 30 November 2019 в 11:08
поделиться

Быстрый perl one liner тоже хорошо подойдет для этого...

$ perl -ne 'if (YOURLINENUMBER..YOURLINENUMBER) {print $_; last;}' /path/to/your/file
1
ответ дан 30 November 2019 в 11:08
поделиться

Если это не файл с фиксированной длиной записи и вы не выполняете какую-либо индексацию в начале строки, лучше всего просто использовать:

head -n N filespec | tail -1

, где N - строка номер, который вы хотите.

К сожалению, это не самый эффективный фрагмент кода для файла 3Gb, но есть способы его улучшить.

Если файл не меняется слишком часто, вы можете его проиндексировать. Под этим я подразумеваю наличие другого файла со смещениями строк в нем в виде записей фиксированной длины.

Итак, файл:

0000000000
0000000017
0000000092
0000001023

даст вам быстрый способ найти каждую строку. Просто умножьте желаемый номер строки на размер индексной записи и найдите его в индексном файле.

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

Итак, для строки 3 вы должны искать в индексном файле до 33 (длина индексной записи составляет 10 символов плюс еще один для новой строки). Чтение значения 0000000092 даст вам смещение для использования в основном файле.

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


И на основе вашего обновления:

Обновление: если это важно, все строки имеют одинаковую длину.

Имея эту дополнительную информацию, вам не нужен индекс - вы можете просто немедленно перейти к нужному месту в основном файле, умножив длину записи на длину записи (при условии, что значения соответствуют вашим типам данных) .

Что-то вроде псевдокода:

def getline(fhandle,reclen,recnum):
    seek to position reclen*recnum for file fhandle.
    read reclen characters into buffer.
    return buffer.
3
ответ дан 30 November 2019 в 11:08
поделиться

Альтернатива awk, где 3 - номер строки.

awk 'NR == 3 {print; exit}' file.txt
2
ответ дан 30 November 2019 в 11:08
поделиться
Другие вопросы по тегам:

Похожие вопросы: