Удалить только запятые, присутствующие в числах в двойных кавычках в CSV-файле.

Удалить только запятые, присутствующие в числах в двойных кавычках в CSV-файле.

В текстовом файле я хочу удалить ,(запятые), а также "(кавычки) (только если двойные кавычки содержат числа, разделенные запятыми).

56,72,"12,34,54",x,y,"foo,a,b,bar"

Ожидаемый результат

56,72,123454,x,y,"foo,a,b,bar"

Примечание:Я показываю строку выше просто как пример. Мой текстовый файл содержит много строк, подобных приведенным выше, и числа, разделенные запятыми, присутствующие в двойных кавычках, должны различаться. То есть,

56,72,"12,34,54",x,y,"foo,a,b,bar"
56,92,"12,34",x,y,"foo,a,b,bar"
56,72,"12,34,54,78,76,54,67",x,y,"foo,a,b,bar"
56,72,x,y,"foo,a,b,bar","12,34,54"
56,72,x,y,"foo,a,b,bar","12,34,54","45,57,84,92","bar,foo"

Ожидаемый результат:

56,72,123454,x,y,"foo,a,b,bar"
56,92,1234,x,y,"foo,a,b,bar"
56,72,12345478765467,x,y,"foo,a,b,bar"
56,72,x,y,"foo,a,b,bar",123454
56,72,x,y,"foo,a,b,bar",123454,45578492,"bar,foo"

В двойных кавычках есть nряд цифр, разделенных запятыми. А также оставьте двойные кавычки, которые содержат символы, как есть.

Мне нравится sedинструмент для обработки текста. Буду рад, если вы опубликуете какое-либо sedрешение для этого.

решение1

Если с perl все в порядке, вот короткий (и, вероятно, быстрый, хотя и не обязательно простой :) ) способ сделать это:

perl -pe 's:"(\d[\d,]+)":$1=~y/,//dr:eg' file

Флаг eоператора s:::(который является просто другим способом записи s///) заставляет замену рассматриваться как выражение, которое вычисляется каждый раз. Это выражение берет $1захват из регулярного выражения (в котором уже отсутствуют кавычки) и переводит ( y///, что также может быть записано как tr///) его, удаляя ( /d) все запятые. rФлаг yнеобходим для того, чтобы получить значение, которое будет переведенной строкой, а не количеством переводов.

Для тех, кто чувствует себя каким-то образом запятнанным Perl, вот эквивалент Python. Python на самом деле не является инструментом оболочки в одну строку, но иногда его можно уговорить сотрудничать. Следующий код можно записать в одну строку (в отличие от forциклов, которые не могут быть написаны), но горизонтальная прокрутка делает его (еще более) нечитаемым:

python -c '
import re;
import sys;
r=re.compile("\"(\d+(,\d+)*)\"");
all(not sys.stdout.write(r.sub(lambda m:m.group(1).replace(",",""),l))
    for l in sys.stdin)
' < file

решение2

Это (адаптировано изздесь) должен сделать то, что вам нужно, хотя вариант @rici на Perl гораздо проще:

$ sed -r ':a;s/(("[0-9,]*",?)*"[0-9,]*),/\1/;ta; s/""/","/g; 
          s/"([0-9]*)",?/\1,/g ' file
56,72,123454,x,y,"foo,a,b,bar"
56,92,1234,x,y,"foo,a,b,bar"
56,72,12345478765467,x,y,"foo,a,b,bar"
56,72,x,y,"foo,a,b,bar",123454,
56,72,x,y,"foo,a,b,bar",123454,45578492,"bar,foo"

Объяснение

  • :a: определить метку с именем a.
  • s/(("[0-9,]*",?)*"[0-9,]*),/\1/: Это нужно разобрать
    • Прежде всего, используя эту конструкцию: (foo(bar)), \1будет foobarи \2будет bar.
    • "[0-9,]*",?: соответствует 0 или более 0-9или ,, за которыми следует 0 или 1 ,.
    • ("[0-9,]*",?)*: соответствует 0 или более из указанных выше.
    • "[0-9,]*: соответствует 0 или более символов 0-9или , ,которые идут сразу после"
  • ta;: вернитесь к метке aи запустите сноваеслизамена прошла успешно.
  • s/""/","/g;: постобработка. Заменить ""на ",".
  • s/"([0-9]*)",?/\1,/g: удалите все кавычки вокруг чисел.

Это будет легче понять на другом примере:

$ echo '"1,2,3,4"' | sed -nr ':a;s/(("[0-9,]*",?)*"[0-9,]*),/\1/;p;ta;'
"1,2,34"
"1,234"
"1234"
"1234"

Таким образом, хотя вы можете найти число, которое находится сразу после кавычек, за которым следует запятая и еще одно число, соедините два числа вместе и повторяйте процесс до тех пор, пока это не станет возможным.

На этом этапе я считаю полезным привести цитату из info sedраздела, описывающего расширенные функции, такие как метка, использованная выше (спасибо за находку @Braiam):

В большинстве случаев использование этих команд указывает на то, что вам, вероятно, лучше программировать на чем-то вроде `awk' или Perl.

решение3

Для CSV-данных я бы использовал язык с настоящим CSV-парсером. Например, с Ruby:

ruby -rcsv -pe '
  row = CSV::parse_line($_).map {|e| e.delete!(",") if e =~ /^[\d,]+$/; e} 
  $_  = CSV::generate_line(row)
' <<END
56,72,"12,34,54",x,y,"foo,a,b,bar"
56,92,"12,34",x,y,"foo,a,b,bar"
56,72,"12,34,54,78,76,54,67",x,y,"foo,a,b,bar"
56,72,x,y,"foo,a,b,bar","12,34,54"
56,72,x,y,"foo,a,b,bar","12,34,54","45,57,84,92","bar,foo"
END
56,72,123454,x,y,"foo,a,b,bar"
56,92,1234,x,y,"foo,a,b,bar"
56,72,12345478765467,x,y,"foo,a,b,bar"
56,72,x,y,"foo,a,b,bar",123454
56,72,x,y,"foo,a,b,bar",123454,45578492,"bar,foo"

решение4

С использованиемРаку(ранее известный как Perl_6)

~$ raku -pe 's:g/ \" ~ \" (\d+) ** 2..* % "," /{$0.join}/;'  file

Пример ввода:

56,72,"12,34,54",x,y,"foo,a,b,bar"
56,92,"12,34",x,y,"foo,a,b,bar"
56,72,"12,34,54,78,76,54,67",x,y,"foo,a,b,bar"
56,72,x,y,"foo,a,b,bar","12,34,54"
56,72,x,y,"foo,a,b,bar","12,34,54","45,57,84,92","bar,foo"

Пример вывода:

56,72,123454,x,y,"foo,a,b,bar"
56,92,1234,x,y,"foo,a,b,bar"
56,72,12345478765467,x,y,"foo,a,b,bar"
56,72,x,y,"foo,a,b,bar",123454
56,72,x,y,"foo,a,b,bar",123454,45578492,"bar,foo"

Raku — это язык программирования в семействе Perl с рядом мощных функций регулярных выражений. См. URL ниже для общего обзора этого ответа:

https://unix.stackexchange.com/a/722570/227738

В коде выше цифры распознаются, а встроенные запятые удаляются. Регулярное выражение использует тот факт, чтовложенные структурыможно обозначить с помощью новой нотации Раку ~ тильда (вложенной), которая \" ~ \" [\d+]означает «одна или несколько цифр, заключенных в двойные кавычки».

Кроме того,повторяющиеся структурыможет быть обозначено новым модифицированным квантификатором Раку %для повторяющихся структур. Обозначение [\d+] ** 2..* % "," означает "одну или несколько цифр, разделенных запятыми, ,причем этот шаблон повторяется ** 2..*два или более раз. [Если в синтаксисе есть конечный разделитель (например, запятая), используйте %%вместо %в синтаксисе].

Это только начало. CSV-файлы с альтернативными разделителями, встроенными переносами строк, встроенными запятыми, потенциально пустыми полями и т. д. действительно должны обрабатываться настоящим CSV-парсером, таким как Text::CSVмодуль Raku. Подробности см. по ссылкам ниже.

https://docs.raku.org/language/regexes
https://raku.land/github:Tux/Text::CSV
https://raku.org

Связанный контент