سؤال

وكيف تحليل ملف CSV باستخدام البله؟ ببساطة وضع FS="," لا يكفي، كحقل نقلت بفاصلة داخل سيتم التعامل مع حقول متعددة.

مثال باستخدام FS="," الذي لا يعمل:

ومحتويات الملف:

one,two,"three, four",five
"six, seven",eight,"nine"

والنصي البله:

BEGIN { FS="," }
{
  for (i=1; i<=NF; i++) printf "field #%d: %s\n", i, $(i)
  printf "---------------------------\n"
}

والناتج سيئة:

field #1: one
field #2: two
field #3: "three
field #4:  four"
field #5: five
---------------------------
field #1: "six
field #2:  seven"
field #3: eight
field #4: "nine"
---------------------------

والناتج المرغوب فيه:

field #1: one
field #2: two
field #3: "three, four"
field #4: five
---------------------------
field #1: "six, seven"
field #2: eight
field #3: "nine"
---------------------------
هل كانت مفيدة؟

المحلول

والجواب القصير هو "أنا لن تستخدم البله تحليل CSV CSV إذا كان يحتوي على بيانات حرج"، حيث "حرج" يعني أشياء مثل الفواصل في البيانات الميدانية CSV.

والسؤال التالي هو "ماذا معالجة البعض انت ذاهب الى ما تقوم به"، لأن ذلك سيؤثر ما هي البدائل التي تستخدمها.

وكنت على الارجح استخدام بيرل والنص :: CSV أو وحدات النص :: CSV_XS لقراءة ومعالجة البيانات. تذكر، وكتب بيرل في الأصل تأتي في إطار awk وsed القاتل - ومن هنا جاءت برامج a2p وs2p لا يزال توزيعها مع بيرل التي تحول awk وsed البرامج النصية (على التوالي) في بيرل

نصائح أخرى

والبله الإصدار 4 دليل يقول لاستخدام FPAT = "([^,]*)|(\"[^\"]+\")"

عند يعرف FPAT، فإنه تعطيل FS ويحدد المجالات التي المحتوى بدلا من فاصل.

ويمكنك استخدام الدالة المجمع بسيطة تسمى csvquote لتطهير المدخلات واستعادته بعد الانتهاء AWK معالجتها. يجب أن تعمل الأنابيب البيانات الخاصة بك من خلال ذلك في بداية ونهاية، وكل شيء طيب:

قبل:

gawk -f mypgoram.awk input.csv

وبعد:

csvquote input.csv | gawk -f mypgoram.awk | csvquote -u

https://github.com/dbro/csvquote للحصول على رمز والوثائق.

إذا يجوز، وأود أن استخدام بايثون CSV حدة، مع إيلاء خاص الانتباه إلى المستخدمة والتنسيق المعلمات المطلوبة لتحليل ملف CSV لديك.

csv2delim.awk

# csv2delim.awk converts comma delimited files with optional quotes to delim separated file
#     delim can be any character, defaults to tab
# assumes no repl characters in text, any delim in line converts to repl
#     repl can be any character, defaults to ~
# changes two consecutive quotes within quotes to '

# usage: gawk -f csv2delim.awk [-v delim=d] [-v repl=`"] input-file > output-file
#       -v delim    delimiter, defaults to tab
#       -v repl     replacement char, defaults to ~

# e.g. gawk -v delim=; -v repl=` -f csv2delim.awk test.csv > test.txt

# abe 2-28-7
# abe 8-8-8 1.0 fixed empty fields, added replacement option
# abe 8-27-8 1.1 used split
# abe 8-27-8 1.2 inline rpl and "" = '
# abe 8-27-8 1.3 revert to 1.0 as it is much faster, split most of the time
# abe 8-29-8 1.4 better message if delim present

BEGIN {
    if (delim == "") delim = "\t"
    if (repl == "") repl = "~"
    print "csv2delim.awk v.m 1.4 run at " strftime() > "/dev/stderr" ###########################################
}

{
    #if ($0 ~ repl) {
    #   print "Replacement character " repl " is on line " FNR ":" lineIn ";" > "/dev/stderr"
    #}
    if ($0 ~ delim) {
        print "Temp delimiter character " delim " is on line " FNR ":" lineIn ";" > "/dev/stderr"
        print "    replaced by " repl > "/dev/stderr"
    }
    gsub(delim, repl)

    $0 = gensub(/([^,])\"\"/, "\\1'", "g")
#   $0 = gensub(/\"\"([^,])/, "'\\1", "g")  # not needed above covers all cases

    out = ""
    #for (i = 1;  i <= length($0);  i++)
    n = length($0)
    for (i = 1;  i <= n;  i++)
        if ((ch = substr($0, i, 1)) == "\"")
            inString = (inString) ? 0 : 1 # toggle inString
        else
            out = out ((ch == "," && ! inString) ? delim : ch)
    print out
}

END {
    print NR " records processed from " FILENAME " at " strftime() > "/dev/stderr"
}

test.csv

"first","second","third"
"fir,st","second","third"
"first","sec""ond","third"
" first ",sec   ond,"third"
"first" , "second","th  ird"
"first","sec;ond","third"
"first","second","th;ird"
1,2,3
,2,3
1,2,
,2,
1,,2
1,"2",3
"1",2,"3"
"1",,"3"
1,"",3
"","",""
"","""aiyn","oh"""
"""","""",""""
11,2~2,3

test.bat

rem test csv2delim
rem default is: -v delim={tab} -v repl=~
gawk                      -f csv2delim.awk test.csv > test.txt
gawk -v delim=;           -f csv2delim.awk test.csv > testd.txt
gawk -v delim=; -v repl=` -f csv2delim.awk test.csv > testdr.txt
gawk            -v repl=` -f csv2delim.awk test.csv > testr.txt

ولست متأكدا بالضبط ما إذا كان هذا هو الطريق الصحيح لفعل الأشياء. وأود أن العمل بدلا من ذلك على ملف CSV الذي إما كل القيم هي ما نقلت أو لا شيء. راجع للشغل، AWK يسمح regexes أن تكون فواصل الميدان. معرفة ما اذا كان يمكن أن يكون مفيدا.

وإليك ما خطرت لي. فإن أي تعليقات و / أو حلول أفضل موضع تقدير.

BEGIN { FS="," }
{
  for (i=1; i<=NF; i++) {
    f[++n] = $i
    if (substr(f[n],1,1)=="\"") {
      while (substr(f[n], length(f[n]))!="\"" || substr(f[n], length(f[n])-1, 1)=="\\") {
        f[n] = sprintf("%s,%s", f[n], $(++i))
      }
    }
  }
  for (i=1; i<=n; i++) printf "field #%d: %s\n", i, f[i]
  print "----------------------------------\n"
}

والفكرة الأساسية هي أن حلقة I عبر الحقول، وأي مجال الذي يبدأ مع اقتباس لكن لا تنتهي مع اقتباس يحصل الحقل التالي إلحاق إليه.

وبيرل لديها وحدة النص :: CSV_XS وهو للتعامل مع غرابة نقلت-فاصلة بنيت لهذا الغرض.
بالتناوب محاولة وحدة النص :: CSV.

وperl -MText::CSV_XS -ne 'BEGIN{$csv=Text::CSV_XS->new()} if($csv->parse($_)){@f=$csv->fields();for $n (0..$#f) {print "field #$n: $f[$n]\n"};print "---\n"}' file.csv

وتنتج هذا الإخراج:

field #0: one
field #1: two
field #2: three, four
field #3: five
---
field #0: six, seven
field #1: eight
field #2: nine
---

وهنا نسخة للقراءة الإنسان.
احفظ parsecsv، شمود + س، وتشغيل بأنها "parsecsv file.csv"

#!/usr/bin/perl
use warnings;
use strict;
use Text::CSV_XS;
my $csv = Text::CSV_XS->new();
open(my $data, '<', $ARGV[0]) or die "Could not open '$ARGV[0]' $!\n";
while (my $line = <$data>) {
    if ($csv->parse($line)) {
        my @f = $csv->fields();
        for my $n (0..$#f) {
            print "field #$n: $f[$n]\n";
        }
        print "---\n";
    }
}

وقد تحتاج للإشارة إلى إصدار مختلف من بيرل على جهازك، لأن النص :: قد لا يتم تثبيت وحدة CSV_XS على نسخة افتراضية من بيرل.

Can't locate Text/CSV_XS.pm in @INC (@INC contains: /home/gnu/lib/perl5/5.6.1/i686-linux /home/gnu/lib/perl5/5.6.1 /home/gnu/lib/perl5/site_perl/5.6.1/i686-linux /home/gnu/lib/perl5/site_perl/5.6.1 /home/gnu/lib/perl5/site_perl .).
BEGIN failed--compilation aborted.

وإذا كان أي من الإصدارات الخاصة بك من بيرل يكون النص :: CSV_XS تثبيت، ستحتاج إلى:
sudo apt-get install cpanminus
sudo cpanm Text::CSV_XS

مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top