У мене є файл, що містить приблизно 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
Використовуючи оболонку, повільніше, не робіть цього , це лише для того, щоб показати інший метод:
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')
Краще використовувати розуміння списку, щоб бути більш пітонічним:
#!/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 Мб із 10М рядками із середньою довжиною 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 рубіну та друкувати рядок із stdin, лише якщо його довжина більша або дорівнює 6