ฉันจะลบบรรทัดทั้งหมดในไฟล์ที่มีอักขระน้อยกว่า 6 ตัวได้อย่างไร


17

ฉันมีไฟล์ที่มีประมาณ 10 ล้านบรรทัด

ฉันต้องการลบบรรทัดทั้งหมดในไฟล์ที่มีอักขระน้อยกว่าหกตัว

ฉันจะทำสิ่งนี้ได้อย่างไร


คำถามนี้ไม่เหมาะสำหรับ Stackoverflow หรือไม่
1073075

2
@ user1073075 มันสมบูรณ์แบบในหัวข้อที่นี่
เซท

คำตอบ:


30

มีหลายวิธีในการทำเช่นนี้

การใช้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.bakfile.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')

เย้! ฉันหวังว่าจะได้รับคำตอบจากหลาม=)
TellMe ทำไม

@DevRobot ฉัน see..then ตรวจสอบความเข้าใจรายการฉันเพิ่มได้มากขึ้น Pythonic ..
heemayl

1
และ @DevRobot ก็ไม่แน่ใจว่าไพ ธ อนทำงานช้าลงในไฟล์ขนาดใหญ่เมื่อใช้ตัวเลือกแรก ที่จริงแล้วฉันค่อนข้างมั่นใจว่าหลามเร็วกว่าหลายล้านบรรทัดเนื่องจากมันอ่านต่อบรรทัด
Jacob Vlijm

1
ตัวอย่างไพ ธ อนที่สองจะอ่านไฟล์ทั้งหมดในหน่วยความจำก่อนทำการเข้าร่วม ฉันคิดว่าตัวอย่างงูหลามแรกดีกว่าในกรณีนี้
Holloway

การอ่านตามบรรทัดนั้นช้ากว่าปกติเพราะไฟล์ไม่มีโครงสร้างแบบนั้น คุณต้องอ่านบล็อกล่วงหน้าแล้วค้นหาบรรทัดใหม่ด้วยความเป็นไปได้ที่ลดลงของการทำคู่ขนานจากนั้นส่งคืนเฉพาะบางส่วนของสตริง คุณต้องมีบัฟเฟอร์แบบวงกลม คุณต้องจัดสรรหน่วยความจำแบบไดนามิกหากคุณไม่ทราบว่าสามารถใช้บรรทัดได้นานเท่าใด
The Vee

19

มันง่ายมาก:

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

สิ่งนี้มีประสิทธิภาพอย่างมากเนื่องจากgrepจะไม่พยายามแยกวิเคราะห์เกินความต้องการหรือตีความตัวอักษรไม่ว่าด้วยวิธีใด: เพียงแค่ส่งบรรทัด (ทั้งหมด) ไปยัง stdout (ซึ่งเชลล์จะเปลี่ยนเส้นทางไปที่ resultfile) ทันทีที่เห็น 6 ตัวอักษรในบรรทัดนั้น ( .ในบริบท regexp ตรงกับ 1 ตัวอักษรใด ๆ )

ดังนั้น grep จะส่งออกบรรทัดที่มีตัวอักษร 6 ตัว (หรือมากกว่า) เท่านั้นและตัวอื่น ๆ จะไม่ถูกส่งออกโดย grep ดังนั้นพวกมันจึงไม่ทำให้มันเป็นไฟล์ resultfile


14

โซลูชัน # 1: การใช้ C

วิธีที่เร็วที่สุด: รวบรวมและเรียกใช้โปรแกรม 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

โซลูชัน # 2: การใช้ AWK:

awk 'length>=6' file
  • length>=6: ถ้าlength>=6ส่งคืน TRUE ให้พิมพ์ระเบียนปัจจุบัน

โซลูชัน # 3: การใช้ Perl:

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

1
เชื่อ me..I กำลังรอให้คุณ awkแก้ปัญหา ..
heemayl

2
@ heemayl และฉันไม่เห็นคำถามทันทีดังนั้นฉันรู้ว่าถ้าคุณบังเอิญออนไลน์คุณจะเร็วขึ้น ต้องลบsedโซลูชันของฉัน(มันเกิดขึ้นฉันรู้) XD
kos

จุดของposตัวแปรคืออะไร? ฉันได้รับมันส่งกลับตัวชี้ไปที่ตัวละครlineด้วยตัวละครขึ้นบรรทัดใหม่ แต่คุณดูเหมือนจะไม่เคยใช้มัน \0และถ้าคุณพบว่ามันไม่คุณเพียงแค่ตั้งค่าเท่ากับ
user1717828

@ user1717828 หากฉันพบว่าฉันแทนที่ด้วย\0( strchr()ส่งกลับตัวชี้ NULL หากไม่พบตัวอักษร) จุดจะแทนที่แต่ละบรรทัดใหม่ในตอนท้ายของแต่ละบรรทัดด้วย\0เพื่อไม่ให้นับบรรทัดใหม่strlen(): นี่คือเพื่อให้ความยาวสามารถเปรียบเทียบได้กับ 6 เสมอโดยไม่คำนึงถึงบรรทัดใหม่ที่อาจหายไปในบรรทัดสุดท้าย ฉันรู้ว่าการรักษาที่แตกต่างกันเพียงบรรทัดสุดท้ายจะมีประสิทธิภาพมากกว่า ฉันจะอัปเดตในภายหลัง
kos

1
@tripleee ความคิดคือการเพิ่มโซลูชันที่มีประโยชน์สำหรับงานมากกว่าหนึ่งครั้งหรือสำหรับไฟล์ที่ใหญ่กว่าแต่ : ฉันทดสอบgrepโซลูชันในไฟล์เดียวกันและมันเร็วขึ้นจริง (อาจเป็นเพราะstrlen()ไม่ใช่แนวคิดที่ดีที่สุดที่นี่) . ฉันจะลองใช้การgetchar()วนซ้ำเพื่อตรวจสอบเฉพาะอักขระ N ตัวแรกแทนฉันเดาว่าควรปรับปรุงให้ดีขึ้นอย่างเห็นได้ชัด และใช่ความยาวของบัฟเฟอร์ของเส้นใด ๆ จะถูกตัดให้ยาวตามความยาวของบัฟเฟอร์
kos

2

คุณสามารถใช้ Vim ในโหมด Ex:

ex -sc 'v/\v.{6}/d' -cx file
  1. \v เปิดเวทมนต์

  2. .{6} ค้นหาบรรทัดที่มี 6 ตัวอักษรขึ้นไป

  3. v กลับเลือก

  4. d ลบ

  5. x บันทึกและปิด


1

วิธีการแก้ปัญหาทับทิม:

$ cat input.txt                                                                                                          
abcdef
abc
abcdefghijk

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

แนวคิดง่าย ๆ : เปลี่ยนเส้นทางไฟล์เป็น stdin ของ ruby ​​และพิมพ์บรรทัดจาก stdin เฉพาะเมื่อมันยาวมากกว่าหรือเท่ากับ 6

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.