2009-02-14 12 views
10

Mi piacerebbe sapere quale pattern posso usare in sed per apportare modifiche nella prima riga di file enormi (~ 2 GB). La preferenza per sed è solo perché presumo che debba essere più veloce di uno script Python o Perl.Come posso apportare modifiche solo alla prima riga di un file?

I file hanno la seguente struttura:

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

e, data la probabilità di avere spazi nel identificativo per ogni campo, ho bisogno di sostituire ogni spazio da un trattino in questo modo:

**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 

Eventuali puntatori al modello corretto da utilizzare o un'altra soluzione di scripting sarebbe ottima.

risposta

19

Per modificare le prime 10 righe

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

In Perl, è possibile utilizzare l'operatore di flip-flop in un contesto scalare:

perl -i -pe 's/ /_/g if 1 .. 10' 
+0

Questo richiede un 'g' alla fine per farlo sostituire tutti gli spazi della linea, non solo il primo. –

+1

perl -i -pe 's// _/g se 1 .. 10' ??? Wow, non ho mai sentito parlare di questa sintassi in "if 1..10". A volte mi arrabbio un po 'con Perl. Perché tutte queste eccezioni? Perché non usare semplicemente un if ($. <11)? – Frank

+0

@leon: wow, trucco molto carino !, grazie. –

5

È improbabile notare alcuna differenza di velocità tra Perl, Python, e sed. Il tuo script passerà la maggior parte del tempo in attesa di IO.

Se le linee hanno la stessa lunghezza, è possibile modificare sul posto, altrimenti sarà necessario creare un nuovo file.

In 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: $!"; 
+0

Ciao, puoi spiegare perché solo la prima riga è memorizzata in '$ first_line'? –

4

il cambiamento si parla (in sostituzione di ogni spazio da un trattino basso) non cambia la lunghezza della linea, quindi in teoria si potrebbe fare inplace.

avviso !: non testato!

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

io non sono così sicuro circa i parametri conv=..., ma sembra che dovrebbe rendere dd sovrascrivere l'inizio del file originale con la linea trasformata.

Si prega di notare che se si vuole fare qualsiasi altra trasformazione, che potrebbe modificare la lunghezza della linea, no, non fare fare questo. dovresti fare una copia completa. qualcosa di simile:

head -n 1 yourfile | sed -e 's/ /_/g' > tmpfile 
tail -n + 2 | cat tmpfile - > transformedfile 
9

non lo faccio pensa di voler utilizzare qualsiasi soluzione che richieda la scrittura dei dati in un nuovo file.

Se sei abbastanza sicuro che tutto ciò che ti serve è di cambiare gli spazi in caratteri di sottolineatura nella prima riga dei file di testo di grandi dimensioni, devi solo leggere la prima riga, scambiare i caratteri e scriverli di nuovo sul posto:

#!/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; 

per usarlo, basta passare il percorso completo del file da aggiornare:

# fixheader "/path/to/myfile.txt" 
+0

Quello aperto || die non è corretto, valuta open FH, ("+ <$ filename" || die "non può aprire $ filename: $!"); Ether utilizza "or" o mette parentesi attorno ai parametri di aperto o entrambi: open (FH, "+ <$ filename") o die "impossibile aprire $ filename: $!"; – szabgab

+0

Vero, grazie per aver notato il bug. –

+0

Anche quella sarebbe stata la mia soluzione. +1 – Axeman

-1

Questa potrebbe essere una soluzione:

 

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

Tie::File è uno dei moduli che uso di più, ed è molto semplice da usare. Ogni elemento nell'array è una linea nel file. Uno dei lati negativi, tuttavia, sarebbe che questo carica l'intero file in memoria.

+0

in realtà non caricherà il file se non è necessario, quindi se cambi solo la prima riga e il numero di caratteri non cambia questo non ha molto di un sovraccarico. – szabgab

+0

Penso che sia piuttosto raro avere lo stesso numero di caratteri dopo una modifica di linea. – Geo

Problemi correlati