У меня есть много больших (~ 30 МБ за штуку) текстовых файлов с разделителями табуляции и линиями переменной ширины. Я хочу извлечь 2-е поле из n-й (здесь n = 4) и следующей за последней строки (последняя строка пуста). Я могу получить их отдельно с помощью awk:

awk 'NR==4{print $2}' filename.dat

И (я не понимаю это полностью, но)

awk '{y=x "\n" $2};END{print y}' filename.dat

Но есть ли способ собрать их в один звонок? Мое более широкое намерение состоит в том, чтобы обернуть его в скрипт Python, чтобы собрать эти значения из большого количества файлов (многие тысячи) в отдельных каталогах, и я хочу уменьшить количество системных вызовов. Огромное спасибо -

Правка . Я знаю, что могу прочитать весь файл с помощью Python, чтобы извлечь эти значения, но я подумал, что awk может быть более подходящим для этой задачи (имея в виду одно из двух значений, расположенных в конце большой файл).

1
hatmatrix 19 Авг 2010 в 05:29

4 ответа

Лучший ответ

Вот как реализовать это в Python, не читая весь файл

Чтобы получить n-ю строку, у вас нет другого выбора, кроме как прочитать файл до n-й строки, поскольку строки имеют переменную ширину.

Чтобы получить строку со второй по последнюю, угадайте , какой длины строка может быть (быть щедрой), и найдите столько байтов до конца файла.

read() с той точки, которую вы искали. Подсчитайте количество символов новой строки - вам нужно как минимум два. Если количество строк перевода меньше 2, удвойте свое предположение и попробуйте снова

Разделить данные, которые вы читаете на новые строки - строка, которую вы ищите, будет вторым последним элементом в разделении

1
John La Rooy 19 Авг 2010 в 01:57

Это мое решение в Python. Вдохновленный этим другим кодом:

def readfields(filename,nfromtop=3,nfrombottom=-2,fieldnum=1,blocksize=4096):
    f = open(filename,'r')
    out = ''
    for i,line in enumerate(f):
        if i==nfromtop:
            out += line.split('\t')[fieldnum]+'\t'
            break
    f.seek(-blocksize,2)
    out += str.split(f.read(blocksize),'\n')[nfrombottom].split('\t')[fieldnum]
    return out

Когда я его профилировал, разница была на 0,09 секунды быстрее, чем решение, вызывающее awk (awk 'NR==4{print $2};{y=x $2};END{print y}' filename.dat) с модулем подпроцесса. Не нарушитель соглашения, но когда остальная часть сценария написана на Python, кажется, что это выгодно (тем более, что у меня много этих файлов).

1
hatmatrix 20 Авг 2010 в 03:51

Вы можете передать количество строк в awk:

awk -v lines=$( wc -l < filename.dat ) -v n=4 '
    NR == n || NR == lines-1 {print $2}
' filename.dat

Обратите внимание, что в команде wc используйте перенаправление <, чтобы избежать печати имени файла.

2
glenn jackman 19 Авг 2010 в 12:50
awk 'NR==4{print $2};{y=x "\n" $2};END{print y}' filename.dat
3
Ignacio Vazquez-Abrams 19 Авг 2010 в 01:33