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?
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?
Jawaban:
Ada banyak cara untuk melakukan ini.
Menggunakan grep
:
grep -E '^.{6,}$' file.txt >out.txt
Sekarang out.txt
akan 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 grep
atau mengedit file di tempat menggunakan -i
opsi sed
:
sed -ri.bak '/^.{6,}$/' file.txt
File asli akan dicadangkan file.txt.bak
dan 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')
Ini sangat sederhana:
grep ...... inputfile > resultfile #There are 6 dots
Ini sangat efisien, karena grep
tidak 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.
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 1000000
karakter per baris; Anda dapat mengubahnya dengan mengubah nilai MAX_BUFFER_SIZE
).
(Trik untuk mengganti \n
dengan yang \0
ditemukan 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
awk 'length>=6' file
length>=6
: jika length>=6
mengembalikan TRUE, mencetak catatan saat ini.perl -lne 'length>=6&&print' file
lenght>=6
mengembalikan 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
awk
..
sed
solusi saya (itu terjadi, saya tahu). XD
pos
variabel? Saya mendapatkannya mengembalikan pointer ke karakter line
dengan karakter baris baru, tetapi Anda sepertinya tidak pernah menggunakannya. Dan jika Anda tidak menemukannya, Anda hanya mengaturnya sama dengan \0
.
\0
( strchr()
mengembalikan pointer NULL jika karakter tidak ditemukan). Intinya adalah mengganti setiap baris baru di akhir setiap baris dengan \0
sehingga 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.
grep
solusi 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.
Anda dapat menggunakan Vim dalam mode Ex:
ex -sc 'v/\v.{6}/d' -cx file
\v
nyalakan sihir
.{6}
temukan garis dengan 6 karakter atau lebih
v
pilihan sebaliknya
d
menghapus
x
Simpan dan tutup
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