Как удалить несколько повторяющихся диапазонов строк из файла CSV? - PullRequest
2 голосов
/ 23 мая 2019

Я работаю с CSV-файлом, который является выходом из анализатора данных газового хроматографа, поэтому я могу манипулировать только тем, что предоставляется. Мне нужно удалить ненужные строки из файла CSV или оставить только необходимые строки. В текущем файле 960 строк.

Первые 8 строк в файле выглядят так

[Line 1]  Remove
[Line 2]  Remove
[Line 3]  Keep
[Line 4]  Remove
[Line 5]  Remove
[Line 6]  Remove
[Line 7]  Keep
[Line 8]  Keep

Шаблон диапазонов строк, которые я хочу сохранить / удалить, продолжается для сотен строк, поэтому вот следующие 8 строк в качестве примера.

[Line 9]   Remove
[Line 10]  Remove
[Line 11]  Keep
[Line 12]  Remove
[Line 13]  Remove
[Line 14]  Remove
[Line 15]  Keep
[Line 16]  Keep

Нет шаблонов строк, которые различают эти строки, только сами номера строк. Я хотел бы избежать необходимости вычислять диапазоны из сотен строк и помещать их все в sed, как показано в приведенном ниже сценарии, который сокращает требуемое количество строк только для первых 8 строк.

    sed '1,2d; 4,6d' test.csv >> cut_test.csv

Я надеюсь на следующее:

[Line 3]  Keep
[Line 7]  Keep
[Line 8]  Keep
[Line 11] Keep
[Line 15] Keep
[Line 16] Keep

Ответы [ 5 ]

5 голосов
/ 23 мая 2019

Если номера строк сохраняются в соответствии с точным шаблоном (повторяющимся каждые 8 ​​строк), который вы указали в своем объяснении, вы можете использовать следующую команду GNU sed:

$ sed '1~8d;2~8d;4~8d;5~8d;6~8d;' input.csv 
[Line 3]  Keep
[Line 7]  Keep
[Line 8]  Keep
[Line 11]  Keep
[Line 15]  Keep
[Line 16]  Keep

и перенаправьте его в новый файл или пользователю -i.back, чтобы изменить файл на месте.

Пояснение:

  • 1~8d выполнит команду d в 1-й строке, 9-й строке, ...
  • 2~8d выполнит команду d во 2-й строке, 10-й строке, ...

input.csv:

$ cat input.csv 
[Line 1]  Remove
[Line 2]  Remove
[Line 3]  Keep
[Line 4]  Remove
[Line 5]  Remove
[Line 6]  Remove
[Line 7]  Keep
[Line 8]  Keep
[Line 9]   Remove
[Line 10]  Remove
[Line 11]  Keep
[Line 12]  Remove
[Line 13]  Remove
[Line 14]  Remove
[Line 15]  Keep
[Line 16]  Keep

Вы даже можете упростить команду, перегруппировав все следующим образом (это близко к вашей команде):

$ sed '1~8,2~8d;4~8,6~8d;' input.csv 
[Line 3]  Keep
[Line 7]  Keep
[Line 8]  Keep
[Line 11]  Keep
[Line 15]  Keep
[Line 16]  Keep

Как упоминалось в Thor , вы можете уменьшить команду, если вместо удаления строк, которые вы хотите удалить, вы просто напечатаете строки, которые хотите сохранить:

$ sed -n '3~8p;7~8,8~8p;' input.csv
[Line 3]  Keep
[Line 7]  Keep
[Line 8]  Keep
[Line 11]  Keep
[Line 15]  Keep
[Line 16]  Keep
3 голосов
/ 23 мая 2019

Подход Python - это просто

import sys
for i,l in enumerate(sys.stdin):
  if i%8 in (2,6,7): print(l)  # 0-based
1 голос
/ 23 мая 2019

Это может сработать для вас (GNU sed):

sed -n 'n;n;p;n;n;n;n;p;n;p' file

Делает, как сказано на банке.

Лучше (уже упомянуто Тором):

sed -n '3~8p;7~8,+1p' file
1 голос
/ 23 мая 2019

Краткий ответ :

Действие по умолчанию в awk для совпадения - печать строки: awk 'NR% 8 ~ / 3 | 7 | 0 /' input.csv

Длинный ответ , вдохновленный комментариями @ kvantour

awk 'NR%8~/3|7|0/' input.csv
# or shorter (when module < 10)
awk 'NR%8~/[037]/' input.csv

Когда вам нужно по модулю> 9, вам нужно сопоставить всю строку с маркерами ^$. С модулем 25 и строками 3,7,8,11,14,22 вы можете использовать

awk 'NR%25~/^[3|7|0|11|14|22]$/' input.csv
# or shorter
awk 'NR%25~/^[037]|1[14]|22$/' input.csv

Это становится труднее читать для большего количества значений. Альтернатива

# Original case
awk 'BEGIN {a[3];a[7];a[0]} NR%8 in a' input.csv 
# 3,7,8,11,14,22
awk 'BEGIN {a[3];a[7];a[8];a[11];a[14];a[22];} NR%25 in a' input.csv 

Вытащить цифры:

# Original case
awk 'FNR==NR {a[$0];next} FNR%8 in a' <(printf "%s\n" 3 7 0) input.csv 
# 3,7,8,11,14,22
awk 'FNR==NR {a[$0];next} FNR%25 in a' <(printf "%s\n" 3 7 8 11 14 22) input.csv 
1 голос
/ 23 мая 2019

Решение sed элегантно, но, как вы также пометили Python, вот эквивалентное решение на этом языке. Он должен масштабироваться до огромных файлов, если это когда-либо понадобится, потому что он никогда не читает весь файл одновременно (что, я считаю, верно и для решения sed):

import itertools

with open('input.csv', 'r') as in_file:
    with open('output.csv', 'w') as out_file:
        out_file.writelines(entry for entry, keep in zip(in_file.readlines(), itertools.cycle([False, False, True, False, False, False, True, True])) if keep)
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...