2009-02-14 101 views
10

我想知道可以在sed中使用哪種模式在第一行的大文件(〜2 GB)中進行更改。對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 

任何指向正確模式的指針或其他腳本解決方案都會很棒。

回答

19

編輯第10行

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

在Perl中,你可以使用觸發器運營商在標量上下文:

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

這就需要一個`g`來使它替換線上的所有空格,而不僅僅是第一個。 – 2009-02-14 16:33:29

+1

perl -i -pe's// _/g if 1 .. 10'???哇,我從來沒有聽說過「if 1..10」這個語法。有時候我會對Perl有些惱火。爲什麼所有這些例外?爲什麼不使用簡單的if($。<11)? – Frank 2009-02-14 17:18:24

+0

@leon:哇,非常整潔的把戲!,謝謝。 – 2009-02-14 21:52:34

5

你不可能注意到Perl,Python, 和sed之間的速度差異。您的腳本將花大部分時間等待IO。

如果行長度相同,則可以就地編輯,否則 將不得不創建新文件。

在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

嗨,你能解釋爲什麼只有第一行存儲在`$ first_line`? – 2016-04-11 03:40:06

4

你提到的變化(由下劃線替換每個空間)不會改變線的長度,所以理論上它可以就地完成。

警告!:未經測試!

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 
9

我不認爲你想使用任何需要將數據寫入新文件的解決方案。

如果您非常確定您需要的是在大文本文件的第一行中將空格更改爲下劃線,則只需讀取第一行,交換字符並將其寫回原位即可:

#!/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" 
-1

這可能是一個解決辦法:

 

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

Tie::File是我使用最多的模塊之一,使用起來非常簡單。數組中的每個元素都是文件中的一行。然而,其中一個缺點是,這會將整個文件加載到內存中。

相關問題