Làm cách nào để xóa tất cả các dòng trong một tệp có ít hơn 6 ký tự?


17

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?


Không phải câu hỏi này phù hợp hơn với Stackoverflow sao?
dùng1073075

2
@ user1073075 nó hoàn toàn về chủ đề ở đây.
Seth

Câu trả lời:


30

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.txtsẽ 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ư grephoặc chỉnh sửa tệp tại chỗ bằng -itùy chọn sed:

sed -ri.bak '/^.{6,}$/' file.txt 

Các tập tin ban đầu sẽ được sao lưu file.txt.bakvà 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')

Yay! Tôi đã hy vọng cho một câu trả lời python =)
TellMeWhy

@DevRobot Tôi thấy..thì kiểm tra danh sách hiểu tôi đã thêm, hãy thêm Pythonic ..
heemayl

1
Ngoài ra @DevRobot không chắc chắn python chậm hơn trên các tệp lớn, khi tùy chọn đầu tiên được sử dụng. Trên thực tế tôi khá chắc chắn python nhanh hơn trên hàng triệu dòng, vì nó đọc trên mỗi dòng.
Jacob Vlijm

1
Ví dụ python thứ hai đọc toàn bộ tệp vào bộ nhớ trước khi thực hiện nối. Tôi nghĩ rằng ví dụ python đầu tiên là tốt hơn trong trường hợp này.
Holloway

Đọc theo dòng nhất thiết phải chậm hơn vì các tệp không có cấu trúc như vậy. Bạn vẫn cần đọc một khối phía trước và tìm kiếm một dòng mới với khả năng song song giảm, sau đó chỉ trả về chuỗi một phần. Bạn cần một bộ đệm tròn. Bạn cần phân bổ bộ nhớ linh hoạt nếu bạn không biết các dòng có thể dài bao nhiêu.
Vee

19

Nó rất đơn giản:

grep ...... inputfile > resultfile   #There are 6 dots

Điều này cực kỳ hiệu quả, vì grepsẽ 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ả.


14

Giải pháp số 1: sử dụng C

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 1000000ký 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ế \nbằng \0tì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

Giải pháp số 2: sử dụng AWK:

awk 'length>=6' file
  • length>=6: nếu length>=6trả về TRUE, in bản ghi hiện tại.

Giải pháp số 3: sử dụng Perl:

perl -lne 'length>=6&&print' file
  • Nếu lenght>=6trả 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

1
Hãy tin tôi..Tôi đang chờ đợi giải pháp của bạn awk ..
heemayl

2
@heemayl Và tôi đã không thấy câu hỏi ngay lập tức, vì vậy tôi biết rằng nếu bạn tình cờ trực tuyến, bạn sẽ nhanh hơn. Phải xóa sedgiải pháp của tôi (nó xảy ra, tôi biết). XD
kos

Điểm của posbiến là gì? Tôi nhận được nó trả về một con trỏ tới ký tự linevớ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.
dùng1717828

@ user1717828 Nếu tôi tìm thấy nó, tôi thay thế nó bằng \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.
kos

1
@tripleee Ý tưởng là thêm một giải pháp hữu ích cho công việc một lần, hoặc cho các tệp lớn hơn, nhưng : Tôi đã thử nghiệm grepgiả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.
kos

2

Bạn có thể sử dụng Vim trong chế độ Ex:

ex -sc 'v/\v.{6}/d' -cx file
  1. \v bật ma thuật

  2. .{6} tìm dòng có 6 ký tự trở lên

  3. v lựa chọn đối nghịch

  4. d xóa bỏ

  5. x lưu và đóng


1

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

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.