Как я могу получить конкретную строку в текстовом файле на 3 ГБ. Все строки имеют:
\n
. И я должен смочь получить любую строку по требованию.
Как это может быть сделано? Только одна строка должна быть возвращенной.
Если все строки имеют одинаковую длину, лучшим способом будет использовать dd (1)
и задайте ему параметр пропуска.
Пусть размер блока будет длиной каждой строки (включая новую строку), тогда вы можете сделать:
$ dd if=filename bs=<line-length> skip=<line_no - 1> count=1 2>/dev/null
Идея состоит в том, чтобы искать мимо всех предыдущих строк ( skip =
) и прочтите одну строку ( count = 1
). Поскольку размер блока установлен равным длине строки ( bs =
), каждый блок фактически представляет собой одну строку. Перенаправьте stderr, чтобы в конце не получить надоедливую статистику.
Это должно быть намного эффективнее, чем потоковая передача строк перед той, которую вы хотите, с помощью программы, которая прочитает все строки и затем выбросит их, поскольку dd
будет искать нужную позицию в файле. и читать только одну строку данных из файла.
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
Используйте 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)
Проверка аргументов должна быть намного лучше, и есть место для многих других улучшений.
Быстрый perl one liner тоже хорошо подойдет для этого...
$ perl -ne 'if (YOURLINENUMBER..YOURLINENUMBER) {print $_; last;}' /path/to/your/file
Если это не файл с фиксированной длиной записи и вы не выполняете какую-либо индексацию в начале строки, лучше всего просто использовать:
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.
Альтернатива awk, где 3 - номер строки.
awk 'NR == 3 {print; exit}' file.txt