Может ли awk работать с CSV-файлом, который содержит запятую внутри поля, заключенного в кавычки?

StackOverflow https://stackoverflow.com/questions/3138363

Вопрос

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

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

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

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

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

Спасибо.

Это было полезно?

Решение

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

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

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

Другие советы

Один из способов использования GNU awk а также Флисткас

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

Результат:

192

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

Вы можете помочь AWK работать с полями данных, которые содержат запятые (или новые линии), используя небольшой скрипт, который я написал под названием CSVQUOTE. Он заменяет оскорбительные запяты внутри цитируемых полей с непечатающими символами. Если вам нужно, вы можете позже восстановить эти запятые - но в этом случае вам не нужно.

Вот команда:

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

видеть https://github.com/dbro/csvquote. для кода

я использую

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

Определить поля с GAWK. Я обнаружил, что когда поле нулю, это не распознает правильное количество полей. Потому что «+» требуется как минимум 1 символ в поле. Я изменил его на:

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

и заменить "+" с участием "*". Отказ Это работает правильно.

Я также обнаружил, что руководство пользователя GNU awk также имеет эту проблему.https://www.gnu.org/software/gawk/manual/html_node/spliting-by-content.html.

Для такого простого входного файла, как этот, вы можете просто написать небольшую функцию для преобразования всех реальных FSS вне кавычек в какое-либо другое значение (я выбрал 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>

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

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

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

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

$ 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 не имеет роскоши модификации исходных данных, этот метод не будет подходящим ему.

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

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

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

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

Если вам нужны данные в этих полях, которые содержат мусор, это не для вас. ghostdog74 Предоставлял ответ, который опорожняет это поле, но поддерживает общее количество полей в конце, что является ключом к сохранению вывода данных в соответствии с. Мне не понравилось, как это решение представило новые строки. Это версия этого решения, которую я использовал. Кулак три поля никогда не имел этой проблемы в данных. Четвертое поле, содержащее имя клиента, часто делал, но мне нужно было эти данные. Оставшиеся поля, которые демонстрируют проблему, я мог бы выбросить без проблем, потому что она не нужна в моем выходе отчета. Итак, я сначала SED из мусора 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 /

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

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

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

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

Полностью подвесные шарики CSV, такие как Perl's 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 В моем ответе здесь: Parse CSV-файл с помощью Gawk

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top