Подтвердить что ты не робот

Может ли awk работать с CSV файлом, содержащим запятую внутри поля с цитированием?

Я использую awk для вычисления суммы одного столбца в файле csv. Формат данных выглядит примерно так:

id, name, value
1, foo, 17
2, bar, 76
3, "I am the, question", 99

Я использовал этот awk script для подсчета суммы:

awk -F, '{sum+=$3} END {print sum}'

Некоторое значение в поле имени содержит запятую, и это разбивает мой awk script. Мой вопрос: можно ли решить эту проблему? Если да, и как я могу это сделать?

Спасибо.

4b9b3361

Ответ 1

вы пишете функцию в awk, как показано ниже:

$ awk 'func isnum(x){return(x==x+0)}BEGIN{print isnum("hello"),isnum("-42")}'
0 1

вы можете включить в свою script эту функцию и проверить, является ли третье поле числовым или нет. Если не числовое, то перейдите на четвертое поле, и если четвертое поле inturn не является числовым, продолжайте пятое... до вас достигните числового значения. Вероятно, цикл поможет здесь и добавит его к сумме.

Ответ 2

Один способ использования GNU awk и FPAT

awk 'BEGIN { FPAT = "([^, ]+)|(\"[^\"]+\")" } { sum+=$3 } END { print sum }' file.txt

Результат:

192

Ответ 3

Вероятно, вам лучше сделать это в perl с Text:: CSV, так как это быстрое и надежное решение.

Ответ 4

Вы можете помочь awk работать с полями данных, которые содержат запятые (или новые строки), используя небольшой script, который я написал, называемый csvquote. Он заменяет запятые запятые внутри указанных полей непечатаемыми символами. Если вам нужно, вы можете позже восстановить эти запятые, но в этом случае вам не нужно.

Вот команда:

csvquote inputfile.csv | awk -F, '{sum+=$3} END {print sum}'

см. https://github.com/dbro/csvquote для кода

Ответ 5

Вы всегда можете решить проблему из источника. Поместите кавычки вокруг поля имени, так же как поле "Я - вопрос". Это намного проще, чем потратить на это временные методы кодирования.

Обновить (по просьбе Денниса). Простой пример

$ s='id, "name1,name2", value 1, foo, 17 2, bar, 76 3, "I am the, question", 99'

$ echo $s|awk -F'"' '{ for(i=1;i<=NF;i+=2) print $i}'
id,
, value 1, foo, 17 2, bar, 76 3,
, 99

$ echo $s|awk -F'"' '{ for(i=2;i<=NF;i+=2) print $i}'
name1,name2
I am the, question

Как вы можете видеть, установив разделитель на двойную кавычку, поля, принадлежащие "кавычки", всегда находятся на четном числе. Поскольку OP не обладает роскошью для изменения исходных данных, этот метод не подходит для него.

Ответ 6

Если вы точно знаете, что столбец "значение" всегда является последним столбцом:

awk -F, '{sum+=$NF} END {print sum}'

NF представляет количество полей, поэтому $NF является последним столбцом

Ответ 7

Эта статья помогла мне решить эту же проблему с полем данных. Большинство CSV помещают цитату вокруг полей с пробелами или запятыми внутри них. Это испортит количество полей для awk, если вы не отфильтровываете их.

Если вам нужны данные в тех полях, которые содержат мусор, это не для вас. ghostdog74 предоставил ответ, который опустошает это поле, но в конечном итоге поддерживает общее количество полей, что является ключом к сохранению вывода данных. Мне не понравилось, как это решение ввело новые строки. Это версия этого решения, которое я использовал. Эти три поля никогда не имели этой проблемы в данных. Четвертое поле, содержащее имя клиента, часто делалось, но мне нужны эти данные. Остальные поля, которые показывают проблему, я могу выбросить без проблем, потому что это не было необходимо для вывода моего отчета. Поэтому я сначала очень сильно удалил 4-му полевую мусор и удалил первые два экземпляра кавычек. Затем я применяю то, что ghostdog74 дал, чтобы удалить оставшиеся поля с запятыми внутри них - это также удаляет кавычки, но я использую printf для сохранения данных в одной записи. Я начинаю с 85 полей и в итоге получаю 85 полей во всех случаях из моих 8000+ строк беспорядочных данных. Отличный счет!

grep -i $1 $dbfile | sed 's/\, Inc.//;s/, LLC.//;s/, LLC//;s/, Ltd.//;s/\"//;s/\"//' | awk -F'"' '{ for(i=1;i<=NF;i+=2) printf ($i);printf ("\n")}' > $tmpfile

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

awk -F'"' '{ for(i=1;i<=NF;i+=2) printf ($i);printf ("\n")}

Спасибо за ghostdog74 за отличное решение!

NetsGuy256/

Ответ 8

В качестве простого входного файла вы можете просто написать небольшую функцию для преобразования всех реальных FS за пределами кавычек в какое-то другое значение (я выбрал RS, так как разделитель записи не может быть частью записи), а затем используйте это как FS, например:

$ cat decsv.awk
BEGIN{ fs=FS; FS=RS }

{
   decsv()

   for (i=1;i<=NF;i++) {
       printf "Record %d, Field %d is <%s>\n" ,NR,i,$i
   }
   print ""
}

function decsv(         curr,head,tail)
{
   tail = $0
   while ( match(tail,/"[^"]+"/) ) {
       head = substr(tail, 1, RSTART-1);
       gsub(fs,RS,head)
       curr = curr head substr(tail, RSTART, RLENGTH)
       tail = substr(tail, RSTART + RLENGTH)
   }
   gsub(fs,RS,tail)
   $0 = curr tail
}

$ cat file
id, name, value
1, foo, 17
2, bar, 76
3, "I am the, question", 99

$ awk -F", " -f decsv.awk file
Record 1, Field 1 is <id>
Record 1, Field 2 is <name>
Record 1, Field 3 is <value>

Record 2, Field 1 is <1>
Record 2, Field 2 is <foo>
Record 2, Field 3 is <17>

Record 3, Field 1 is <2>
Record 3, Field 2 is <bar>
Record 3, Field 3 is <76>

Record 4, Field 1 is <3>
Record 4, Field 2 is <"I am the, question">
Record 4, Field 3 is <99>

Это только усложняется, когда вам приходится иметь дело со встроенными новыми строками и встроенными экранированными кавычками в кавычках, и даже тогда это не слишком сложно, и все это было сделано до...

Ответ 9

Я использую

`FPAT="([^,]+)|(\"[^\"]+\")" `

чтобы определить поля с gawk. Я обнаружил, что при нулевом поле это не распознает правильное количество полей. Поскольку "+" требует по крайней мере 1 символ в поле. Я изменил его на:

`FPAT="([^,]*)|(\"[^\"]*\")"`

и замените "+" на "*". Он работает правильно.

Я также обнаружил, что в GNU Awk User Guide также есть эта проблема. https://www.gnu.org/software/gawk/manual/html_node/Splitting-By-Content.html

Ответ 10

FPAT - изящное решение, потому что оно может обрабатывать страшные запятые в задаче кавычек, но суммировать столбец чисел в последнем столбце, независимо от числа предыдущих разделителей, $NF хорошо работает:

awk -F"," '{sum+=$NF} END {print sum}'

Чтобы получить доступ к столбцу от второго до последнего, вы должны использовать это:

awk -F"," '{sum+=$(NF-1)} END {print sum}'

Ответ 11

Полноценные синтаксические анализаторы CSV, такие как Perl Text::CSV_XS, предназначены для обработки такого рода странности.

perl -MText::CSV_XS -lne 'BEGIN{$csv=Text::CSV_XS->new({allow_whitespace => 1})} if($csv->parse($_)){@f=$csv->fields();$sum+=$f[2]} END{print $sum}' file

allow_whitespace необходимо, так как входные данные имеют пробелы, окружающие разделители запятой. Очень старые версии Text::CSV_XS могут не поддерживать эту опцию.

В моем ответе я дал больше объяснений Text::CSV_XS: проанализировать файл csv с помощью gawk