Оставьте только строки, содержащие точное количество разделителей


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]будет соответствовать любому символу , который является c, d, eили f, и [^A-Z]будет соответствовать любому символу , который не является прописной буквой. Так что [^,]соответствует любому отдельному символу, кроме запятой.

Класс *символов после означает «ноль или более из них».

Таким образом, часть регулярного выражения ([^,]*,)означает «Любой символ, кроме запятой любое количество раз (включая ноль раз), за которым следует запятая», и {10}указывает 10 из них. Затем, [^,]*чтобы сопоставить остальные символы без запятой до конца строки.


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

Это сначала разветвляет любую строку с 11 или более запятыми, а затем печатает то, что остается, только те, которые соответствуют 10 запятым.

Видимо, я отвечал на это раньше ... Вот я-плагиат из вопроса, ищущего ровно 4 случая некоторого паттерна:

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

Или, по крайней мере, это обрабатывает линии, которые превышают ваш максимум 4. Очевидно, у вас также есть минимальное требование. К счастью, это так же просто:

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

... просто замените 4-е вхождение ,в строке на себя и pприкрепите свой знак к s///флагам ubstitution. Поскольку любые строки, соответствующие ,5 или более раз, уже были удалены, строки, содержащие 4 ,совпадения, содержат только 4.


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

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

Хаха, верно, это очень позитивное мышление. Во всяком случае, очень смешно с тобой общаться, а иногда ты напрягаешь мой мозг.
cuonglm

Интересно, что в этом ответе , если я заменю s/hello/world/2на s//world/2, GNU sed работает нормально. С двумя sedиз семейной реликвии, /usr/5bin/posix/sedподнять segfault, /usr/5bin/sedидет в бесконечный цикл.
cuonglm

@mikeserv, со ссылкой на наше предыдущее обсуждение sedиawk (в комментариях) - мне нравится этот ответ, и я проголосовал за него, но обратите внимание, что перевод принятого awkответа: «Распечатать строки с 11 полями», а перевод этого sedответа: « Попытайтесь удалить 11-ую запятую; в случае неудачи перейдите к следующей строке. Попытайтесь заменить 10-ю запятой на себя; в случае успеха напечатайте строку. " 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.