Tôi có một tập tin chứa khoảng 10 triệu dòng.
Tôi muốn xóa tất cả các dòng trong tệp có ít hơn sáu ký tự.
Làm thế nào để tôi làm điều này?
Tôi có một tập tin chứa khoảng 10 triệu dòng.
Tôi muốn xóa tất cả các dòng trong tệp có ít hơn sáu ký tự.
Làm thế nào để tôi làm điều này?
Câu trả lời:
Có rất nhiều cách để làm điều này.
Sử dụng grep
:
grep -E '^.{6,}$' file.txt >out.txt
Bây giờ out.txt
sẽ chứa các dòng có sáu ký tự trở lên.
Cách ngược lại:
grep -vE '^.{,5}$' file.txt >out.txt
Sử dụng sed
, loại bỏ các dòng có độ dài từ 5 trở xuống:
sed -r '/^.{,5}$/d' file.txt
Cách ngược, in các dòng có độ dài từ sáu trở lên:
sed -nr '/^.{6,}$/p' file.txt
Bạn có thể lưu kết quả đầu ra trong một tệp khác bằng cách sử dụng >
toán tử như grep
hoặc chỉnh sửa tệp tại chỗ bằng -i
tùy chọn sed
:
sed -ri.bak '/^.{6,}$/' file.txt
Các tập tin ban đầu sẽ được sao lưu file.txt.bak
và tập tin sửa đổi sẽ được file.txt
.
Nếu bạn không muốn giữ một bản sao lưu:
sed -ri '/^.{6,}$/' file.txt
Sử dụng shell, Slower, Đừng làm điều này , đây chỉ là để hiển thị một phương thức khác:
while IFS= read -r line; do [ "${#line}" -ge 6 ] && echo "$line"; done <file.txt
Sử dụng python
, thậm chí chậm hơn grep
, sed
:
#!/usr/bin/env python2
with open('file.txt') as f:
for line in f:
if len(line.rstrip('\n')) >= 6:
print line.rstrip('\n')
Sử dụng tốt hơn việc hiểu danh sách để được Pythonic hơn:
#!/usr/bin/env python2
with open('file.txt') as f:
strip = str.rstrip
print '\n'.join([line for line in f if len(strip(line, '\n')) >= 6]).rstrip('\n')
Nó rất đơn giản:
grep ...... inputfile > resultfile #There are 6 dots
Điều này cực kỳ hiệu quả, vì grep
sẽ không cố phân tích nhiều hơn mức cần thiết, cũng như không diễn giải các ký tự theo bất kỳ cách nào: nó chỉ đơn giản gửi một dòng (toàn bộ) đến stdout (mà vỏ sau đó chuyển hướng đến resultfile) ngay khi nhìn thấy 6 ký tự trên dòng đó ( .
trong ngữ cảnh regrec phù hợp với bất kỳ 1 ký tự nào).
Vì vậy, grep sẽ chỉ xuất ra các dòng có 6 (hoặc nhiều hơn) ký tự và các dòng khác không được xuất ra bởi grep để chúng không biến nó thành kết quả.
Cách nhanh nhất: biên dịch và chạy chương trình C này:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define MAX_BUFFER_SIZE 1000000
int main(int argc, char *argv[]) {
int length;
if(argc == 3)
length = atoi(argv[2]);
else
return 1;
FILE *file = fopen(argv[1], "r");
if(file != NULL) {
char line[MAX_BUFFER_SIZE];
while(fgets(line, sizeof line, file) != NULL) {
char *pos;
if((pos = strchr(line, '\n')) != NULL)
*pos = '\0';
if(strlen(line) >= length)
printf("%s\n", line);
}
fclose(file);
}
else {
perror(argv[1]);
return 1;
}
return 0;
}
Biên dịch với gcc program.c -o program
, chạy với ./program file line_length
(where file
= path đến tệp và line_length
= độ dài dòng tối thiểu, trong trường hợp của bạn 6
; độ dài dòng tối đa được giới hạn ở các 1000000
ký tự trên mỗi dòng; bạn có thể thay đổi giá trị này bằng cách thay đổi giá trị của MAX_BUFFER_SIZE
).
(Thủ thuật để thay thế \n
bằng \0
tìm thấy ở đây .)
So sánh với tất cả các giải pháp khác được đề xuất cho câu hỏi này ngoại trừ giải pháp shell (chạy thử trên tệp ~ 91 MB với các dòng 10M với chiều dài trung bình 8 ký tự):
time ./foo file 6
real 0m1.592s
user 0m0.712s
sys 0m0.160s
time grep ...... file
real 0m1.945s
user 0m0.912s
sys 0m0.176s
time grep -E '^.{6,}$'
real 0m2.178s
user 0m1.124s
sys 0m0.152s
time awk 'length>=6' file
real 0m2.261s
user 0m1.228s
sys 0m0.160s
time perl -lne 'length>=6&&print' file
real 0m4.252s
user 0m3.220s
sys 0m0.164s
sed -r '/^.{,5}$/d' file >out
real 0m7.947s
user 0m7.064s
sys 0m0.120s
./script.py >out
real 0m8.154s
user 0m7.184s
sys 0m0.164s
awk 'length>=6' file
length>=6
: nếu length>=6
trả về TRUE, in bản ghi hiện tại.perl -lne 'length>=6&&print' file
lenght>=6
trả về TRUE, in bản ghi hiện tại.% cat file
a
bb
ccc
dddd
eeeee
ffffff
ggggggg
% ./foo file 6
ffffff
ggggggg
% awk 'length>=6' file
ffffff
ggggggg
% perl -lne 'length>=6&&print' file
ffffff
ggggggg
awk
..
sed
giải pháp của tôi (nó xảy ra, tôi biết). XD
pos
biến là gì? Tôi nhận được nó trả về một con trỏ tới ký tự line
với một ký tự dòng mới, nhưng dường như bạn không bao giờ sử dụng nó. Và nếu bạn không tìm thấy nó, bạn chỉ cần đặt nó bằng \0
.
\0
( strchr()
trả về một con trỏ NULL nếu không tìm thấy ký tự ). Điểm đang thay thế mỗi dòng mới ở cuối mỗi dòng \0
để dòng mới không bao giờ được tính bằng strlen()
: điều này sao cho chiều dài luôn có thể được so sánh với 6 bất kể dòng mới có khả năng bị thiếu ở dòng cuối cùng. Đối xử khác nhau chỉ có dòng cuối cùng sẽ hiệu quả hơn, tôi biết. Có lẽ tôi sẽ cập nhật điều này sau.
grep
giải pháp trên cùng một tệp và nó thực sự nhanh hơn (có lẽ vì đó strlen()
không phải là ý tưởng tốt nhất ở đây) . getchar()
Thay vào đó, tôi sẽ cố gắng sử dụng một vòng lặp để chỉ kiểm tra ký tự N đầu tiên, tôi đoán điều đó sẽ cải thiện rõ rệt. Và vâng, bất kỳ dòng nào trên chiều dài của bộ đệm chỉ đơn giản là được cắt theo chiều dài của bộ đệm.
Bạn có thể sử dụng Vim trong chế độ Ex:
ex -sc 'v/\v.{6}/d' -cx file
\v
bật ma thuật
.{6}
tìm dòng có 6 ký tự trở lên
v
lựa chọn đối nghịch
d
xóa bỏ
x
lưu và đóng
Giải pháp Ruby:
$ cat input.txt
abcdef
abc
abcdefghijk
$ ruby -ne 'puts $_ if $_.chomp.length() >= 6 ' < input.txt
abcdef
abcdefghijk
Ý tưởng đơn giản: chuyển hướng tệp vào stdin của ruby và chỉ in dòng từ stdin nếu chiều dài của nó lớn hơn hoặc bằng 6