Bagaimana cara menghapus semua baris dalam file yang kurang dari 6 karakter?


17

Saya memiliki file yang berisi sekitar 10 juta baris.

Saya ingin menghapus semua baris dalam file yang kurang dari enam karakter.

Bagaimana saya melakukan ini?


Bukankah pertanyaan ini lebih cocok untuk Stackoverflow?
user1073075

2
@ user1073075 sangat sesuai dengan topik di sini.
Seth

Jawaban:


30

Ada banyak cara untuk melakukan ini.

Menggunakan grep:

grep -E '^.{6,}$' file.txt >out.txt

Sekarang out.txtakan berisi garis yang memiliki enam karakter atau lebih.

Cara sebaliknya:

grep -vE '^.{,5}$' file.txt >out.txt

Menggunakan sed, menghapus garis dengan panjang 5 atau kurang:

sed -r '/^.{,5}$/d' file.txt

Cara sebaliknya, mencetak garis dengan panjang enam atau lebih:

sed -nr '/^.{6,}$/p' file.txt 

Anda dapat menyimpan output di file yang berbeda menggunakan >operator suka grepatau mengedit file di tempat menggunakan -iopsi sed:

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

File asli akan dicadangkan file.txt.bakdan file yang diubah akan file.txt.

Jika Anda tidak ingin menyimpan cadangan:

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

Menggunakan shell, Slower, Don't do this , ini hanya demi menunjukkan metode lain:

while IFS= read -r line; do [ "${#line}" -ge 6 ] && echo "$line"; done <file.txt

Menggunakan python, bahkan lebih lambat dari 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')

Lebih baik gunakan daftar pemahaman agar lebih Pythonic:

#!/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! Saya berharap untuk jawaban python =)
TellMeWhy

@DevRobot saya mengerti..kemudian memeriksa daftar pemahaman yang saya tambahkan, menjadi lebih Pythonic ..
heemayl

1
@DevRobot juga tidak begitu yakin python lebih lambat pada file besar, ketika opsi pertama digunakan. Sebenarnya saya cukup yakin python lebih cepat di jutaan baris, karena berbunyi per baris.
Jacob Vlijm

1
Contoh python kedua membaca seluruh file ke dalam memori sebelum melakukan join. Saya pikir contoh python pertama lebih baik dalam hal ini.
Holloway

Membaca dengan baris tentu lebih lambat karena file tidak terstruktur seperti itu. Anda harus tetap membaca blok di depan dan mencari baris baru dengan kemungkinan paralelisasi yang berkurang, lalu hanya mengembalikan sebagian string. Anda membutuhkan penyangga bundar. Anda perlu mengalokasikan memori secara dinamis jika Anda tidak tahu berapa lama garisnya.
The Vee

19

Ini sangat sederhana:

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

Ini sangat efisien, karena greptidak akan mencoba mem-parsing lebih dari yang dibutuhkannya, atau menafsirkan karakter dengan cara apa pun: ia hanya mengirim garis (keseluruhan) ke stdout (yang kemudian diarahkan oleh shell ke file yang dihasilkan) segera setelah ia melihat 6 karakter pada baris itu ( .dalam konteks regexp cocok dengan 1 karakter apa pun).

Jadi grep hanya akan menghasilkan baris yang memiliki 6 (atau lebih) karakter, dan yang lainnya tidak dihasilkan oleh grep sehingga mereka tidak membuatnya menjadi filefile.


14

Solusi # 1: menggunakan C

Cara tercepat: kompilasi dan jalankan program C ini:

#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;
}

Kompilasi dengan gcc program.c -o program, jalankan dengan ./program file line_length(di mana file= path ke file dan line_length= panjang garis minimum, dalam kasus Anda 6; panjang garis maksimum dibatasi untuk 1000000karakter per baris; Anda dapat mengubahnya dengan mengubah nilai MAX_BUFFER_SIZE).

(Trik untuk mengganti \ndengan yang \0ditemukan di sini .)

Perbandingan dengan semua solusi lain yang diajukan untuk pertanyaan ini kecuali solusi shell (uji coba pada file ~ 91MB dengan garis 10M dengan panjang rata-rata 8 karakter):

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

Solusi # 2: menggunakan AWK:

awk 'length>=6' file
  • length>=6: jika length>=6mengembalikan TRUE, mencetak catatan saat ini.

Solusi # 3: menggunakan Perl:

perl -lne 'length>=6&&print' file
  • Jika lenght>=6mengembalikan TRUE, cetak catatan saat ini.

% 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
Percayalah ... saya sedang menunggu solusi Anda awk ..
heemayl

2
@heemayl Dan saya tidak langsung melihat pertanyaannya, jadi saya tahu bahwa jika Anda online, Anda akan lebih cepat. Harus menghapus sedsolusi saya (itu terjadi, saya tahu). XD
kos

Apa gunanya posvariabel? Saya mendapatkannya mengembalikan pointer ke karakter linedengan karakter baris baru, tetapi Anda sepertinya tidak pernah menggunakannya. Dan jika Anda tidak menemukannya, Anda hanya mengaturnya sama dengan \0.
user1717828

@ user1717828 Jika saya menemukannya saya menggantinya dengan \0( strchr()mengembalikan pointer NULL jika karakter tidak ditemukan). Intinya adalah mengganti setiap baris baru di akhir setiap baris dengan \0sehingga baris baru tidak pernah dihitung oleh strlen(): ini adalah agar panjangnya selalu dapat dibandingkan dengan 6 terlepas dari potensi baris baru yang hilang pada baris terakhir. Mengobati secara berbeda hanya baris terakhir yang akan jauh lebih efisien, saya tahu. Saya mungkin akan memperbaruinya nanti.
kos

1
@ tripleee Idenya adalah untuk menambahkan solusi yang berguna untuk pekerjaan lebih dari satu kali, atau untuk file yang lebih besar, tetapi : Saya menguji grepsolusi pada file yang sama dan sebenarnya lebih cepat (mungkin karena strlen()bukan ide terbaik di sini) . Saya akan mencoba menggunakan getchar()loop untuk memeriksa hanya karakter N pertama sebagai gantinya, saya kira itu harus meningkatkan itu terlihat. Dan ya, setiap garis di atas panjang buffer hanya dipotong sesuai panjang buffer.
kos

2

Anda dapat menggunakan Vim dalam mode Ex:

ex -sc 'v/\v.{6}/d' -cx file
  1. \v nyalakan sihir

  2. .{6} temukan garis dengan 6 karakter atau lebih

  3. v pilihan sebaliknya

  4. d menghapus

  5. x Simpan dan tutup


1

Solusi Ruby:

$ cat input.txt                                                                                                          
abcdef
abc
abcdefghijk

$ ruby -ne 'puts $_ if $_.chomp.length() >= 6 ' < input.txt                                                              
abcdef
abcdefghijk

Ide sederhana: redirect file ke stdin ruby, dan cetak baris dari stdin hanya jika panjangnya lebih besar atau sama dengan 6

Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.