Как удалить все строки в файле длиной менее 6 символов?

17

У меня есть файл, содержащий около 10 миллионов строк.

Я хочу удалить все строки в файле длиной менее шести символов.

Как мне это сделать?

Скажи мне почему
источник
Разве этот вопрос больше не подходит для Stackoverflow?
user1073075
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.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')
heemayl
источник
Ура! Я надеялся получить ответ от Python =)
TellMeWhy
@DevRobot, я вижу .. затем проверьте список понимания, которое я добавил, будьте более Pythonic ..
Heemayl
1
Также @DevRobot не уверен, что python работает медленнее на больших файлах, когда используется первая опция. На самом деле я почти уверен, что python быстрее на миллионах строк, так как он читает каждую строку.
Джейкоб Влейм
1
Второй пример Python считывает весь файл в память перед выполнением соединения. Я думаю, что первый пример Python лучше в этом случае.
Holloway
Чтение по строкам обязательно медленнее, потому что файлы не имеют такой структуры. В любом случае вам нужно прочитать блок впереди и найти новую строку с ограниченными возможностями распараллеливания, а затем вернуть только частичную строку. Вам нужен круговой буфер. Вам нужно динамически распределять память, если вы не знаете, как долго могут быть строки.
The Vee
19

Это очень просто:

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

Это чрезвычайно эффективно, так как grepне будет пытаться ни анализировать больше, чем нужно, ни каким-либо образом интерпретировать символы: он просто отправляет (целую) строку в stdout (который оболочка затем перенаправляет в файл результатов), как только увидит 6 символы в этой строке ( .в контексте регулярного выражения соответствует любому 1 символу).

Таким образом, grep будет выводить только строки с 6 (или более) символами, а остальные не выводятся с помощью grep, поэтому они не преобразуются в файл результата.

Оливье Дюлак
источник
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).

(Trick для замены \nс \0найден здесь .)

Сравнение со всеми другими решениями, предложенными для этого вопроса, за исключением решения оболочки (тестовый запуск файла ~ 91 МБ с 10M строками со средней длиной 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
Поверьте мне .. Я ждал вашего awk решения ..
Heemayl
2
@heemayl И я не сразу увидел вопрос, поэтому я знал, что если бы вы оказались в сети, вы бы быстрее. Пришлось удалить мое sedрешение (такое бывает, я знаю). XD
Кос
Какой смысл posпеременной? Я получаю, он возвращает указатель на символ lineс символом новой строки, но вы, кажется, никогда не используете его. И если вы не найдете его, вы просто установите его равным \0.
user1717828
@ user1717828 Если я найду его, я заменю его на \0( strchr()возвращает нулевой указатель, если символ не найден). Точка заменяет каждую новую строку в конце каждой строки \0так, чтобы новая строка никогда не подсчитывалась strlen(): это так, что длину всегда можно сравнить с 6, независимо от потенциального отсутствия новой строки в последней строке. Я знаю, что по-другому рассматривать только последнюю строку было бы гораздо эффективнее. Я, вероятно, обновлю это позже.
Кос
1
@tripleee Идея состояла в том, чтобы добавить решение, полезное для чего-то большего, чем одноразовая работа, или для еще больших файлов, но : я проверил grepрешение на том же файле, и оно на самом деле быстрее (вероятно, потому что strlen()здесь не самая лучшая идея) , Я попытаюсь использовать getchar()цикл для проверки только первого символа N, я думаю, это должно заметно улучшить его. И да, любая линия по длине буфера просто обрезается до длины буфера.
Кос
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

Простая идея: перенаправить файл в стандартный ruby ​​и выводить строку из стандартного stdin, только если его длина больше или равна 6

Сергей Колодяжный
источник