CSV에서 필드를 분할하고 행의 필드를 새 행에 복제하는 방법

CSV에서 필드를 분할하고 행의 필드를 새 행에 복제하는 방법

CSV 파일을 사용하는 대상이 있고 6번째 필드에 단어가 포함되어 있지만 최대 문자 길이는 16입니다. 필드 길이가 16자를 초과하는 경우 줄을 복제하여 단어를 깨지 않고 나누기를 원합니다.

현재 파일

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

원하는 출력

"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

GNU Awk( )를 사용 하여 gawk실행foldGetline/변수/코프로세스

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

sub작업 에서 후행 공백을 제거 합니다 fold.

fold -sw17표시된 정확한 출력을 얻으려면 16자에 (나중에 제거된) 후행 공백을 더해 구분하는 데 사용해야 합니다 . 그러나 그렇게 하면 접힌 출력의 마지막 줄에서 16자 제한을 초과할 가능성이 발생합니다.

답변2

나는 큰따옴표를 유지하는 꽤 형편없는 awk 스크립트를 만들었습니다. 여기 온다:

{
    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))
        }
    }
}

출력은 다음과 같습니다

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

유일한 문제는 경계에 공간이 있으면 이를 제거한 예와 달리 그대로 유지된다는 점이다.

답변3

아래 코드로 시도했는데 잘 작동했습니다.

 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

산출

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

답변4

SHELL 전용 접근 방식(Bash 및 Ksh93에서 테스트됨) 그러나 나는 fold현존하는 도구를 사용하는 접근 방식을 좋아합니다 .

# 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

결과:

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

또는 유사한 단어를 사용하지 않고 단어 분리 문제를 해결하려면 fold위의 아래에 표시된 주석 처리된 줄을 다음 코드로 대체해야 합니다. 또한 두 번째 echo명령줄을 다음으로 바꿉니다.

                    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

교체하고

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

~와 함께

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

null 필드 6 나머지가 발생하지 않도록 합니다.

다음 테스트 파일이 사용되었습니다.

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

결과:

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

그러나 기존 도구를 사용하는 것은 fold훨씬 쉽고 UNIX 철학을 따릅니다. 즉, 기존의 간단한 도구를 기반으로 구축됩니다. 그러나 Shell 프로그래밍을 좋아한다면 위의 내용이 솔루션을 얻는 한 가지 방법입니다. 코드에 대한 설명이 필요한 사람이 있으면 저에게 연락하세요.

관련 정보