2010-06-29 3 views
19

Я использую awk для выполнения подсчета суммы одного столбца в файле csv. Формат данных что-то вроде:Может ли 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}' 

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

Спасибо.

ответ

-1

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

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

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

+2

Это действительно неуклюже, он терпит неудачу, если поле не является числом. @ Ответ Стива гораздо лучше. – smci

+1

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

+0

Другая проблема с этим ответом заключается в том, что если «значение» отсутствует в строке, предполагается, что «id» - это значение, если в программу awk не добавлено больше логики, чтобы сказать «если элемент в строке является числовым а не первый элемент ... » – benson

2

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

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

$ 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 не обладает роскошью для изменения исходных данных, этот метод не подходит для него.

+0

Возможно, было бы полезно, если бы вы показали, как обращаться цитируемым поле. –

+0

Спасибо, Dennis Но файл csv создается клиентом, так что я могу ничего не делать о формате файла. :( – maguschen

4

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

+0

Да, я согласен с тобой, мне просто интересно, как справиться с этой проблемой. :) – maguschen

+0

См. ответ, который я опубликовал, чтобы определить поля в целом, но для вашей конкретной проблемы ответ by @HaiVu является правильным. –

2

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

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

NF обозначает количество полей, так что $ NF является последней колонке

2

Эта статья сделала помощь я решаю эту же проблему с полем данных. Большинство 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/

+0

printf является встроенным не функцией, поэтому «(« не делают то, что вы считаете и нецелесообразным ». Кроме того, синопсисом для printf является« printf fmt, values ​​»- выполнение« значений printf »с пользовательским вводом является опасным и чтобы избежать этого. Наконец, не производите ORS с помощью printf "\ n", просто используйте print "", и пусть ORS расширяется естественным образом. –

2

Ибо, как простой входной файл, как вы можете просто написать небольшую функцию для преобразования всех реального ФПА вне кавычек на другое значение (я выбрал RS, так как разделитель записи не может быть часть записи), а затем использовать его в качестве ФС, например:

$ 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> 

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

16

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

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

Результат:

192 
+1

Подход FPAT замечательный, но он работает только тогда, когда FS является единственным символом с вы не можете отрицать RE.Это не работает, когда FS является строкой, как в этом случае, когда AFAIK это «,», поэтому, хотя он работает с отдельными входными данными образца, он идентифицирует слишком много полей, учитывая строка ввода, где поле содержит пробелы, но не включено в кавычки. –

+1

Ну, для CSV нет ни одного стандарта, поэтому YMMV, но обычно кавычки используются, когда вам нужно включить разделитель полей в поле, а не когда у вас просто пробелы. Например, MS-Excel не будет использовать кавычки при сохранении в качестве CSV fo rmat, если ячейка содержит пробелы, только если она содержит запятую. –

+1

Это замечательно, за исключением того, что вы должны иметь возможность сопоставлять полностью пустые поля: 'FPAT =" ([^,] *) | (\ "[^ \"] + \ ")"} '. В противном случае он не соответствует полям в строках например, '22 ,,," some string "' – smci

3

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

Вот команда:

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

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

1

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

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

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

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

2

Я использую

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

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

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

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

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

1

Полноценные синтаксические анализаторы 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 file using gawk

 Смежные вопросы

  • Нет связанных вопросов^_^