So teilen Sie ein Feld in einer CSV-Datei und duplizieren die Felder in der Zeile in eine neue Zeile

So teilen Sie ein Feld in einer CSV-Datei und duplizieren die Felder in der Zeile in eine neue Zeile

Ich habe ein Ziel, das CSV-Dateien verwendet, und das 6. Feld enthält Wörter, aber die maximale Zeichenlänge beträgt 16. Wenn die Feldlänge 16 Zeichen überschreitet, möchte ich die Zeile duplizieren und aufteilen, ohne die Wörter zu trennen.

Aktuelle Datei

"5","4","3","2","1","XYZ ABCD E"
"1","2","3","4","5","AB CDE F GHI JK LMNOP Q RS TUV W XYZ 12 3456 7890"
"9","8","7","6","5","LMN O PQ R"

Gewünschte Ausgabe

"5","4","3","2","1","XYZ ABCD E"
"1","2","3","4","5","AB CDE F GHI JK"
"1","2","3","4","5","LMNOP Q RS TUV W"
"1","2","3","4","5","XYZ 12 3456 7890"
"9","8","7","6","5","LMN O PQ R"

Antwort1

Mit GNU Awk ( gawk) durch foldeineGetline/Variable/Coprozess

gawk -F, '
  BEGIN{
    OFS=FS; 
    cmd="fold -sw 16";
  }

  # if total length (16 + 2 for quotes) is within limit, print as-is
  length($NF) <= 18 {print; next}

  # else
  {
    # trim the quotes, then fold
    print substr($NF,2,length($NF)-2) |& cmd; 
    close(cmd,"to"); 
    NF--; 
    while((cmd |& getline var) > 0){

      # (optional) trim trailing whitespace
      sub(/[ \t]+$/,"",var);

      print $0, "\"" var "\"" ;
    }
    close(cmd,"from");
  }
' file.csv

Dadurch subwerden nachstehende Leerzeichen aus der foldOperation entfernt.

Beachten Sie, dass Sie, um die angezeigte genaue Ausgabe zu erhalten, bei 16 Zeichen plus dem (anschließend entfernten) Leerzeichen am Ende einen Zeilenumbruch verwenden müssten fold -sw17. Dadurch besteht jedoch die Möglichkeit, dass die 16-Zeichen-Begrenzung in der letzten Zeile der gefalteten Ausgabe überschritten wird.

Antwort2

Ich habe ein ziemlich lahmes Awk-Skript erstellt, das die Anführungszeichen beibehält. Hier ist es:

{
    for ( i=0; i<= length($6); i+=16 )
    {
        if ( i+17 < length($6) )
        {
            if ( i == 0 )
                printf ("%s,%s,%s,%s,%s,%s\"\n", $1, $2, $3, $4, $5, substr($6,i,16))
            else
                printf ("%s,%s,%s,%s,%s,\"%s\"\n", $1, $2, $3, $4, $5, substr($6,i+1,16))
        }
        else
        {
            if ( i == 0 )
                printf ("%s,%s,%s,%s,%s,%s\n", $1, $2, $3, $4, $5, substr($6,i,16))
            else
                printf ("%s,%s,%s,%s,%s,\"%s\n", $1, $2, $3, $4, $5, substr($6,i+1,16))
        }
    }
}

Die Ausgabe ist:

$ awk -F, -f awks csvfields
"5","4","3","2","1","XYZ ABCD E"
"1","2","3","4","5","AB CDE F GHI JK"
"1","2","3","4","5"," LMNOP Q RS TUV "
"1","2","3","4","5","W XYZ 12 3456 78"
"1","2","3","4","5","90"
"9","8","7","6","5","LMN O PQ R"
$

Das einzige Problem besteht darin, dass ein Leerzeichen an der Grenze erhalten bleibt, im Gegensatz zum Beispiel, wo es entfernt wurde.

Antwort3

Habe es mit dem unten stehenden Code versucht und es hat auch gut funktioniert

 k=16;for ((j=1;j<=50;j++)); do  awk -v j="$j" -v k="$k" -F "," '{if(length($NF) > 16){print $1,$2,$3,$4,$5,substr($NF,j,k)}else {print $0}}' filename; j=$(($j+16)); done|sort | uniq

Ausgabe

"5","4","3","2","1","XYZ ABCD E"
"1","2","3","4","5","AB CDE F GHI JK"
"1","2","3","4","5","LMNOP Q RS TUV W"
"1","2","3","4","5","XYZ 12 3456 7890"
"9","8","7","6","5","LMN O PQ R"

Antwort4

Ein reiner SHELL-Ansatz (getestet auf Bash und Ksh93). Mir gefällt der foldAnsatz jedoch, da er ein vorhandenes Tool verwendet.

# read from stdin, output to stdout
# Note no Shebang line at top so it made it easier for to try bash/ksh as interpreters

OIFS="$IFS"
IFS=,
while read f1 f2 f3 f4 f5 f6; do
    f6=${f6#\"}
    f6=${f6%\"}             # strip DQs
    if ((${#f6}<17)); then  # no action
            IFS="$OIFS"
            echo "$f1,$f2,$f3,$f4,$f5,\"$f6\""
            IFS=","
            continue
    else
            IFS="$OIFS"
            while ((${#f6}>17)); do
                    n6=${f6:0:16}
                    f6=${f6#$n6}
                    n6=${n6# }
                    n6=${n6% }
                    echo "$f1,$f2,$f3,$f4,$f5,\"$n6\""
            done
            echo "$f1,$f2,$f3,$f4,$f5,\"${f6# }\""
    fi
    IFS=","
done
IFS="$OIFS"
exit

Ergebnisse:

"5","4","3","2","1","XYZ ABCD E"
"1","2","3","4","5","AB CDE F GHI JK"
"1","2","3","4","5","LMNOP Q RS TUV W"
"1","2","3","4","5","XYZ 12 3456 7890"
"9","8","7","6","5","LMN O PQ R"

Um das Worttrennungsproblem zu beheben, ohne foldo. Ä. zu verwenden, sollte der folgende Code die oben angezeigte auskommentierte Zeile ersetzen. Ersetzen Sie außerdem die zweite echoBefehlszeile durch:

                    c6="$f6"
                    n6=""
                    while (((${#n6}+${#nw})<=16)); do
                            n6=$n6${c6%% *}\
                            n6=${n6# }
                            eval c6=\${c6\#${c6%% *} }
                            nw=${c6%% *}
                    done
                    #n6=${f6:0:16} ### replace by above

und ersetzen

            echo "$f1,$f2,$f3,$f4,$f5,\"${f6# }\""

mit

            ((${#f6}>0)) && echo "$f1,$f2,$f3,$f4,$f5,\"${f6# }\""

um das Auftreten von Nullresten im Feld 6 zu vermeiden.

Als Testdatei wurde folgende verwendet:

"5","4","3","2","1","XYZ ABCD E"
"1","2","3","4","5","AB CDE F GHI JK LMNOP Q RS TUV W XYZ 12 3456 7890"
"9","8","7","6","5","LMN O PQ R"
"1","2","3","4","5","A BB CCC DDD EEEE FFFFF GGGGGG HHHHHHH"

mit Ergebnissen:

"5","4","3","2","1","XYZ ABCD E"
"1","2","3","4","5","AB CDE F GHI JK"
"1","2","3","4","5","LMNOP Q RS TUV W"
"1","2","3","4","5","XYZ 12 3456 7890"
"9","8","7","6","5","LMN O PQ R"
"1","2","3","4","5","A BB CCC DDD"
"1","2","3","4","5","EEEE FFFFF"
"1","2","3","4","5","GGGGGG HHHHHHH"

Die Verwendung der vorhandenen Tools foldist jedoch viel einfacher und folgt der UNIX-Philosophie – auf vorhandenen einfachen Tools aufbauen. Wenn Sie jedoch Shell-Programmierung mögen, ist das Obige eine Möglichkeit, eine Lösung zu finden. Wenn jemand Erklärungen zum Code benötigt, kontaktieren Sie mich bitte.

verwandte Informationen