2009-02-14 24 views
10

Tôi muốn biết tôi có thể sử dụng mẫu nào trong sed để thay đổi dòng đầu tiên của các tệp lớn (~ 2 GB). Ưu tiên cho sed là chỉ vì tôi cho rằng nó phải nhanh hơn một kịch bản Python hoặc Perl.Làm cách nào để thay đổi chỉ dòng đầu tiên của tệp?

Các tập tin có cấu trúc sau:

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

và với khả năng có khoảng trống trong các định danh cho mỗi lĩnh vực, tôi cần phải thay thế tất cả các không gian bằng một dấu gạch theo cách này:

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

Bất kỳ con trỏ nào với mẫu phù hợp để sử dụng hoặc giải pháp tập lệnh khác sẽ tuyệt vời.

Trả lời

19

Để chỉnh sửa 10 dòng đầu tiên

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

Trong Perl, bạn có thể sử dụng toán tử flip-flop trong bối cảnh vô hướng:

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

Điều đó cần một 'g' ở cuối để làm cho nó thay thế tất cả các khoảng trống trong dòng, không chỉ là dấu cách đầu tiên. –

+1

perl -i -pe 's// _/g nếu 1 .. 10' ??? Wow, tôi chưa bao giờ nghe nói về cú pháp này trong "nếu 1..10". Đôi khi tôi cảm thấy hơi khó chịu với Perl. Tại sao tất cả các ngoại lệ này? Tại sao không chỉ sử dụng đơn giản nếu ($. <11)? – Frank

+0

@leon: wow, mẹo rất gọn gàng !, cảm ơn bạn. –

5

Bạn không có khả năng nhận thấy bất kỳ sự khác biệt về tốc độ giữa Perl, Python, và sed. Kịch bản của bạn sẽ dành phần lớn thời gian chờ IO.

Nếu các dòng có cùng độ dài, bạn có thể chỉnh sửa tại chỗ, nếu không, bạn sẽ phải tạo tệp mới.

Trong 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

Xin chào, bạn có thể giải thích tại sao chỉ có dòng đầu tiên được lưu trữ trong '$ first_line'? –

4

thay đổi mà bạn đề cập đến (thay thế tất cả các không gian bằng một dấu gạch dưới) không làm thay đổi chiều dài của dòng, vì vậy về mặt lý thuyết nó có thể được thực hiện inplace.

cảnh báo !: chưa được kiểm tra!

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

tôi không như vậy chắc chắn về các thông số conv=..., nhưng có vẻ như nó nên dd ghi đè lên đầu của tập tin gốc với dòng biến đổi.

xin lưu ý rằng nếu bạn muốn thực hiện bất kỳ phép chuyển đổi nào khác, điều này có thể làm thay đổi độ dài của đường, không, không làm làm điều này. bạn phải làm một bản sao đầy đủ. một cái gì đó như thế này:

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

tôi không nghĩ rằng bạn muốn sử dụng bất kỳ giải pháp nào yêu cầu dữ liệu được ghi vào một tệp mới.

Nếu bạn khá chắc chắn rằng tất cả những gì bạn cần là thay đổi dấu cách thành dấu gạch dưới trong dòng đầu tiên của tệp văn bản lớn, bạn chỉ phải đọc dòng đầu tiên, hoán đổi ký tự và viết lại tại chỗ:

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

để sử dụng nó, chỉ cần vượt qua đường dẫn đầy đủ của tập tin để cập nhật:

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

Mở cửa || chết không chính xác, nó đánh giá thành mở FH, ("+ <$ filename" || die "không thể mở $ filename: $!"); Sử dụng Ether "hoặc" hoặc đặt dấu ngoặc đơn xung quanh các thông số mở hoặc cả hai: mở (FH, "+ <$ filename") hoặc chết "không thể mở $ filename: $!"; – szabgab

+0

Đúng, cảm ơn vì đã chú ý đến lỗi. –

+0

Đó cũng sẽ là giải pháp của tôi. +1 – Axeman

-1

Đây có thể là một giải pháp:

 

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

Tie::File là một trong những mô-đun tôi sử dụng nhiều nhất và rất dễ sử dụng. Mỗi phần tử trong mảng là một dòng trong tệp. Tuy nhiên, một trong những nhược điểm là việc này sẽ tải toàn bộ tập tin trong bộ nhớ.

+0

thực sự nó sẽ không tải các tập tin nếu nó không phải, vì vậy nếu bạn chỉ thay đổi dòng đầu tiên và số lượng ký tự không thay đổi điều này không có nhiều chi phí. – szabgab

+0

Tôi nghĩ khá hiếm khi có cùng số ký tự sau khi sửa đổi dòng. – Geo

Các vấn đề liên quan