У меня есть файл, содержащий около 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 (который оболочка затем перенаправляет в файл результатов), как только увидит 6 символы в этой строке ( .
в контексте регулярного выражения соответствует любому 1 символу).
Таким образом, grep будет выводить только строки с 6 (или более) символами, а остальные не выводятся с помощью grep, поэтому они не преобразуются в файл результата.
Самый быстрый способ: скомпилировать и запустить эту программу на 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
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()
возвращает нулевой указатель, если символ не найден). Точка заменяет каждую новую строку в конце каждой строки \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
Простая идея: перенаправить файл в стандартный ruby и выводить строку из стандартного stdin, только если его длина больше или равна 6