Как я могу внести изменения только в первую строку файла? - PullRequest
10 голосов
/ 14 февраля 2009

Я хотел бы знать, какой шаблон можно использовать в sed для внесения изменений в первую строку огромных файлов (~ 2 ГБ). Предпочтение отдается sed только потому, что я предполагаю, что он должен быть быстрее, чем скрипт Python или Perl.

Файлы имеют следующую структуру:

field 1, field 2, ... field n
data

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

**BEFORE** 
the first name,the second name,the first surname,a nickname, ...
data

**AFTER**
the_first_name,the_second_name,the_first_surname,a_nickname, ...
data

Было бы неплохо использовать любые указатели на правильный шаблон или другое решение для сценариев.

Ответы [ 5 ]

22 голосов
/ 14 февраля 2009

Для редактирования первых 10 строк

sed -i -e '1,10s/ /_/g'

В Perl вы можете использовать оператор триггера в скалярном контексте:

perl -i -pe 's/ /_/g if 1 .. 10'
10 голосов
/ 14 февраля 2009

Не думаю, что вы хотите использовать какое-либо решение, требующее записи данных в новый файл.

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

#!/usr/bin/env perl
use strict;

my $filename = shift;
open (FH, "+< $filename") || die "can't open $filename: $!";
my $line = <FH>;
$line =~ s/ /_/g;
seek FH, 0, 0; # go back to the start of the file
printf FH $line;
close FH;

Чтобы использовать его, просто введите полный путь к файлу для обновления:

# fixheader "/path/to/myfile.txt"
5 голосов
/ 14 февраля 2009

Вы вряд ли заметите разницу в скорости между Perl, Python, и сед. Ваш сценарий будет проводить большую часть времени в ожидании ввода-вывода.

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

В Perl:

#!/usr/bin/env perl
use strict;

my $filename = shift;
open my $in_fh, '<', $filename
  or die "Cannot open $filename for reading: $!";
my $first_line = <$in_fh>;

open my $out_fh, '>', "$filename.tmp"
  or die "Cannot open $filename.tmp for writing: $!";

$first_line =~ s/some translation/goes here/;

print {$out_fh} $first_line;
print {$out_fh} $_ while <$in_fh>; # sysread/syswrite is probably better

close $in_fh;
close $out_fh;

# overwrite original with modified copy
rename "$filename.tmp", $filename
  or warn "Failed to move $filename.tmp to $filename: $!";
4 голосов
/ 14 февраля 2009

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

предупреждение !: не проверено!

head -n 1 yourfile | sed -e 's/ /_/g' > tmpfile
dd conv=nocreat,notrunc if=tmpfile of=yourfile

Я не очень уверен насчет параметров conv=..., но, похоже, он должен заставить dd перезаписать начало исходного файла преобразованной строкой.

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

head -n 1 yourfile | sed -e 's/ /_/g' > tmpfile
tail -n + 2 | cat tmpfile - > transformedfile
0 голосов
/ 14 февраля 2009

Это может быть решением:


use Tie::File;
tie my @array,"Tie::File","path_to_file";
$array[0] = "new text";
untie @array;

Tie :: File - это один из модулей, который я использую чаще всего, и он очень прост в использовании. Каждый элемент в массиве - это строка в файле. Один из недостатков, однако, заключается в том, что он загружает весь файл в память.

...