ฉันมีไฟล์ที่มีประมาณ 10 ล้านบรรทัด
ฉันต้องการลบบรรทัดทั้งหมดในไฟล์ที่มีอักขระน้อยกว่าหกตัว
ฉันจะทำสิ่งนี้ได้อย่างไร
ฉันมีไฟล์ที่มีประมาณ 10 ล้านบรรทัด
ฉันต้องการลบบรรทัดทั้งหมดในไฟล์ที่มีอักขระน้อยกว่าหกตัว
ฉันจะทำสิ่งนี้ได้อย่างไร
คำตอบ:
มีหลายวิธีในการทำเช่นนี้
การใช้grep
:
grep -E '^.{6,}$' file.txt >out.txt
ตอนนี้out.txt
จะมีบรรทัดที่มีอักขระอย่างน้อยหกตัว
วิธีย้อนกลับ:
grep -vE '^.{,5}$' file.txt >out.txt
การใช้การsed
ลบบรรทัดที่มีความยาว 5 หรือน้อยกว่า:
sed -r '/^.{,5}$/d' file.txt
ย้อนกลับพิมพ์เส้นที่มีความยาวหกหรือมากกว่า:
sed -nr '/^.{6,}$/p' file.txt
คุณสามารถบันทึกผลลัพธ์ในไฟล์อื่นโดยใช้>
ตัวดำเนินการเช่นgrep
หรือแก้ไขไฟล์แบบแทนที่โดยใช้-i
ตัวเลือกของsed
:
sed -ri.bak '/^.{6,}$/' file.txt
ไฟล์ต้นฉบับจะได้รับการสนับสนุนขึ้นเป็นและแฟ้มที่แก้ไขจะเป็นfile.txt.bak
file.txt
หากคุณไม่ต้องการสำรองข้อมูล:
sed -ri '/^.{6,}$/' file.txt
ใช้ shell, Slower, อย่าทำสิ่งนี้นี่เป็นเพียงการแสดงวิธีอื่น:
while IFS= read -r line; do [ "${#line}" -ge 6 ] && echo "$line"; done <file.txt
ใช้python
แม้จะช้ากว่า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')
การใช้รายการความเข้าใจที่ดีขึ้นจะเป็นแบบ 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')
มันง่ายมาก:
grep ...... inputfile > resultfile #There are 6 dots
สิ่งนี้มีประสิทธิภาพอย่างมากเนื่องจากgrep
จะไม่พยายามแยกวิเคราะห์เกินความต้องการหรือตีความตัวอักษรไม่ว่าด้วยวิธีใด: เพียงแค่ส่งบรรทัด (ทั้งหมด) ไปยัง stdout (ซึ่งเชลล์จะเปลี่ยนเส้นทางไปที่ resultfile) ทันทีที่เห็น 6 ตัวอักษรในบรรทัดนั้น ( .
ในบริบท regexp ตรงกับ 1 ตัวอักษรใด ๆ )
ดังนั้น grep จะส่งออกบรรทัดที่มีตัวอักษร 6 ตัว (หรือมากกว่า) เท่านั้นและตัวอื่น ๆ จะไม่ถูกส่งออกโดย grep ดังนั้นพวกมันจึงไม่ทำให้มันเป็นไฟล์ resultfile
วิธีที่เร็วที่สุด: รวบรวมและเรียกใช้โปรแกรม C นี้:
#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;
}
คอมไพล์ที่มีgcc program.c -o program
การเรียกใช้ด้วย./program file line_length
(ที่file
= เส้นทางไปยังแฟ้มและline_length
= ความยาวสายขั้นต่ำในกรณีของคุณ6
นั้นความยาวสายสูงสุดถูก จำกัด ไว้ที่1000000
ตัวอักษรต่อบรรทัดคุณสามารถเปลี่ยนแปลงได้โดยการเปลี่ยนค่าของMAX_BUFFER_SIZE
)
(เคล็ดลับเพื่อแทนที่\n
ด้วย\0
พบได้ที่นี่ )
เปรียบเทียบกับโซลูชันอื่น ๆ ทั้งหมดที่เสนอให้กับคำถามนี้ยกเว้นโซลูชันเชลล์ (ทดสอบการทำงานกับไฟล์ ~ 91MB ด้วย 10M line ที่มีความยาวเฉลี่ย 8 ตัวอักษร):
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
: ถ้าlength>=6
ส่งคืน TRUE ให้พิมพ์ระเบียนปัจจุบันperl -lne 'length>=6&&print' file
lenght>=6
ส่งคืน TRUE ให้พิมพ์ระเบียนปัจจุบัน% 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
โซลูชันของฉัน(มันเกิดขึ้นฉันรู้) XD
pos
ตัวแปรคืออะไร? ฉันได้รับมันส่งกลับตัวชี้ไปที่ตัวละครline
ด้วยตัวละครขึ้นบรรทัดใหม่ แต่คุณดูเหมือนจะไม่เคยใช้มัน \0
และถ้าคุณพบว่ามันไม่คุณเพียงแค่ตั้งค่าเท่ากับ
\0
( strchr()
ส่งกลับตัวชี้ NULL หากไม่พบตัวอักษร) จุดจะแทนที่แต่ละบรรทัดใหม่ในตอนท้ายของแต่ละบรรทัดด้วย\0
เพื่อไม่ให้นับบรรทัดใหม่strlen()
: นี่คือเพื่อให้ความยาวสามารถเปรียบเทียบได้กับ 6 เสมอโดยไม่คำนึงถึงบรรทัดใหม่ที่อาจหายไปในบรรทัดสุดท้าย ฉันรู้ว่าการรักษาที่แตกต่างกันเพียงบรรทัดสุดท้ายจะมีประสิทธิภาพมากกว่า ฉันจะอัปเดตในภายหลัง
grep
โซลูชันในไฟล์เดียวกันและมันเร็วขึ้นจริง (อาจเป็นเพราะstrlen()
ไม่ใช่แนวคิดที่ดีที่สุดที่นี่) . ฉันจะลองใช้การgetchar()
วนซ้ำเพื่อตรวจสอบเฉพาะอักขระ N ตัวแรกแทนฉันเดาว่าควรปรับปรุงให้ดีขึ้นอย่างเห็นได้ชัด และใช่ความยาวของบัฟเฟอร์ของเส้นใด ๆ จะถูกตัดให้ยาวตามความยาวของบัฟเฟอร์
คุณสามารถใช้ Vim ในโหมด Ex:
ex -sc 'v/\v.{6}/d' -cx file
\v
เปิดเวทมนต์
.{6}
ค้นหาบรรทัดที่มี 6 ตัวอักษรขึ้นไป
v
กลับเลือก
d
ลบ
x
บันทึกและปิด
วิธีการแก้ปัญหาทับทิม:
$ cat input.txt
abcdef
abc
abcdefghijk
$ ruby -ne 'puts $_ if $_.chomp.length() >= 6 ' < input.txt
abcdef
abcdefghijk
แนวคิดง่าย ๆ : เปลี่ยนเส้นทางไฟล์เป็น stdin ของ ruby และพิมพ์บรรทัดจาก stdin เฉพาะเมื่อมันยาวมากกว่าหรือเท่ากับ 6