Зберігайте лише рядки, що містять точну кількість роздільників


9

У мене величезний файл csv з 10 полями, розділеними комами. На жаль, деякі рядки неправильно сформовані і не містять рівно 10 коми (що спричиняє деякі проблеми, коли я хочу прочитати файл у R). Як я можу відфільтрувати лише рядки, які містять рівно 10 коми?


1
ваше запитання і пов'язане питання - це не одне і те ж питання. Ви запитуєте, як обробити лінії з не більшою чи меншою кількістю певної кількості збігів, тоді як для цього питання потрібно лише мінімальний кількість матчів. реальність полягає в тому, що на це питання легше відповісти - воно не вимагає сканування рядка в повному обсязі, або (принаймні, як це sedробиться тут) лише на більшу кількість збігів, ніж шукали, хоча це питання не відповідає. Ви не мали б цього закривати.
mikeserv

1
на самому справі, придивившись, Аскер там робить хоче , не більше і не менше , ніж збіги. це питання потребує нового заголовку. але grepвідповідь не відповідає на жодне питання ...
mikeserv

Відповіді:


21

Ще один POSIX:

awk -F , 'NF == 11' <file

Якщо рядок має 10 коми, то в цьому рядку буде 11 полів. Тому ми просто awkвикористовуємо ,як роздільник поля. Якщо кількість полів дорівнює 11, умова NF == 11вірна, awkтоді виконується дія за замовчуванням print $0.


5
Це власне перше, що мені прийшло в голову з цього питання. Я думав, що це надмірно, але, дивлячись на код ... це, звичайно, зрозуміліше. На користь інших: -Fвстановлює роздільник поля і NFпосилається на кількість полів у заданому рядку. Оскільки жоден блок коду {statement}не додається до умови NF == 11, дією за замовчуванням є друк рядка. (@cuonglm, сміливо додайте це пояснення, якщо вам подобається.)
Wildcard

4
+1: Дуже елегантне та читабельне рішення, яке також є загальним. Я можу, наприклад, знайти всі неправильні лінії зawk -F , 'NF != 11' <file
Мирослав Сабо

@gardenhead: Це легко отримати, як ви бачите, що в своєму коментарі сказав ОП. Я колись відповідаю зі свого мобільного, тому важко додати пояснення до деталей.
cuonglm

1
@mikeserv: Ні, вибачте, якщо я вас збентежив, це просто моя погана англійська. Ви не можете мати 11 полів з 1-9 комами.
cuonglm

1
@OlivierDulac: Він захищає вас від запуску файлу з -або назви -.
cuonglm

8

Використання egrep(або grep -Eв POSIX):

egrep "^([^,]*,){10}[^,]*$" file.csv

Це відфільтровує все, що не містить 10 коми: воно відповідає повним рядкам ( ^на початку та $в кінці), що містять рівно десять повторів ( {10}) послідовності "будь-яка кількість символів, окрім", ", за яким слідує один", "" ( ([^,]*,)), після чого знову будь-яка кількість символів, крім ',' ( [^,]*).

Ви також можете скористатися -xпараметром для скидання якорів:

grep -xE "([^,]*,){10}[^,]*" file.csv

Це менш ефективно , ніж cuonglm «и awkрішення , хоча; останнє, як правило, у моїй системі в шість разів швидше для рядків з приблизно 10 комами. Більш довгі лінії призведуть до величезних уповільнень.


5

Найпростіший grepкод, який буде працювати:

grep -xE '([^,]*,){10}[^,]*'

Пояснення:

-xгарантує, що візерунок повинен відповідати всій лінії, а не лише її частині. Це важливо, щоб ви не відповідали рядкам із більш ніж 10 комами.

-E означає "розширений регулярний вираз", що дозволяє зменшити кількість відхилень від косої риски у вашому регулярному вираженні.

Для групування використовуються дужки, а {10}згодом означає, що в парантезах повинно бути рівно десять збігів підряд.

[^,]є класом символів - наприклад, [c-f]відповідатиме будь-яким окремим символам, що є a c, a d, an eабо an f, і [^A-Z]відповідатиме будь-якому одному символу, який НЕ є великими літерами. Так [^,]відповідає будь-якому одному символу, крім коми.

Клас *символів після означає "нуль або більше з них".

Отже, частина регулярного вираження ([^,]*,)означає "Будь-який символ, крім коми, будь-яку кількість разів (включаючи нульовий раз) з комою", і {10}вказує 10 з них. Потім [^,]*підберіть решту символів без коми до кінця рядка.


5
sed -ne's/,//11;t' -e's/,/&/10p' <in >out

Спочатку розгалужується будь-який рядок з 11 або більше комами, а потім друкується лише те, що відповідає 10 комам.

Мабуть, я відповів на це раніше ... Ось мій плагіат із запитання, яке шукає саме 4 випадки певної картини:

Ви можете націлити на [num]появу шаблону за допомогою s///команди sed ubstitution, просто додавши [num]до команди. Коли ви tоцінюєте успішну заміну і не вказуєте цільову :мітку, test розгалужується зі сценарію. Це означає, що все, що вам потрібно зробити, - це перевірити s///5чи більше коми, а потім надрукувати те, що залишилося.

Або, принаймні, що обробляє лінії, які перевищують ваш максимум 4. Очевидно, ви також маєте мінімальну вимогу. На щастя, це так само просто:

sed -ne 's|,||5;t' -e 's||,|4p'

... просто замініть 4-е виникнення ,на лінії себе і прикріпіть свої прапори pдо s///прапорів ув'язнення. Оскільки будь-які рядки, які відповідають ,5 і більше разів, вже обрізані, рядки, що містять 4 ,збіги, містять лише 4.


1
@cuonglm - це те, що я мав насправді, спочатку, але люди завжди мені кажуть, що я повинен написати більш читабельний код. оскільки я можу читати речі, які інші суперечать, як нечитаючі я не впевнений, що тримати, а що впасти ... тому я поставив другу кому.
mikeserv

@cuonglm - ти можеш знущатися з мене - це не зашкодить моїм почуттям. я можу пожартувати. якби ти знущався зі мене, це було трохи смішно. все гаразд - я просто не був впевнений і хотів знати. на мою думку, люди повинні вміти сміятися над собою. у будь-якому випадку, я все ще не отримую це!
mikeserv

Ха-ха, правда, це дуже позитивне мислення. У всякому разі, поспілкуватися з тобою дуже смішно, а іноді ти підкреслюєш мій мозок.
cuonglm

Цікаво , що в цій відповіді , якщо я заміню s/hello/world/2з s//world/2, GNU СЕД працювати нормально. З двома sedз heirloom, /usr/5bin/posix/sedпідняти segfault, /usr/5bin/sedпереходить у нескінченний цикл.
cuonglm

@mikeserv, посилаючись на нашу попередню дискусію про sedтаawk (у коментарях) - мені подобається ця відповідь і я її схвалив, але зауважте, що переклад прийнятої awkвідповіді такий: "Друкувати рядки з 11 полів", а переклад цієї sedвідповіді: " Спроба видалити 11-ту косу; у разі невдачі перейдіть до наступного рядка. Спробуйте замінити десяту кому на себе; друкуйте рядок, якщо вам це вдасться. " awkВідповідь дає інструкцію до комп'ютера так, як ви б висловити їх англійською мовою. ( awkдобре для даних на місцях.)
Wildcard

4

Викидання короткого python:

#!/usr/bin/env python2
with open('file.csv') as f:
    print '\n'.join(line for line in f if line.count(',') == 10)

Це буде читати кожен рядок і перевіряти, чи кількість коми в рядку дорівнює 10 line.count(',') == 10, якщо так - надрукувати - це буде рядок.


2

І ось Perl спосіб:

perl -F, -ane 'print if $#F==10'

-nПризводить perlпрочитати його вхідний файл по рядках та виконати сценарій , даний -eв кожному рядку. У -aчерзі на автоматичне розщеплення: кожна вхідний рядок буде розділена на значення , що дається -F(тут кома) і зберігаються в вигляді масиву @F.

$#F(Або, в більш загальному випадку $#array), найвищий індекс масиву @F. Оскільки масиви починаються з 0, рядок з 11 полів матиме @Fо 10. Отже, сценарій друкує рядок, якщо в ньому є рівно 11 полів.


Ви також можете зробити, print if @F==11як масив у скалярному контексті повертає кількість елементів.
Sobrique

1

Якщо поля можуть містити коми або нові рядки, ваш код повинен розуміти csv. Приклад (з трьома стовпцями):

$ cat filter.csv
a,b,c
d,"e,f",g
1,2,3,4
one,two,"three
...continued"

$ cat filter.csv | python3 -c 'import sys, csv
> csv.writer(sys.stdout).writerows(
> row for row in csv.reader(sys.stdin) if len(row) == 3)
> '
a,b,c
d,"e,f",g
one,two,"three
...continued"

Я припускаю, що більшість рішень поки що відкине другий і четвертий ряд.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.