CSV 파일의 열을 시각적으로 정렬

CSV 파일의 열을 시각적으로 정렬

sed또는를 사용하여 awkCSV 파일의 열을 시각적으로 정렬할 수 있습니까 ?

예를 들어:

예를 들어:

a,b,c,some stuff,"some, other, stuff",d,2023-03-10 18:37:00
y,x,z,t,cool,thing,2022-04-12 21:44:00

도착하다:

a, b, c, some stuff,"some, other, stuff",     d, 2023-03-10 18:37:00<EOL>
x, y, z,          t,                cool, thing, 2022-04-12 21:44:00<EOL>

쉼표가 포함된 텍스트가 포함된 큰따옴표 필드가 있습니다.

시도해 column봤지만 bsdmainutils그랬어요확실히이 데이터는 처리할 수 없습니다.

답변1

이 유형의 CSV 파일:

a, b, c, some stuff,"some, other, stuff",     d, 2023-03-10 18:37:00<EOL>
x, y, z,          t,                cool, thing, 2022-04-12 21:44:00<EOL>

아니요진짜필드를 수정하고 있으므로 더 이상 동일한 데이터 파일을 사용하지 않습니다. 구문 분석 시 이제 위의 너비로 인해 원본 콘텐츠가 구문 분석됩니다 "t"(비표준 구분 기호를 구문 분석하기 위해 정규 표현식을 사용하지 않는 한)." t""some stuff",[variable space]

모든 필드에 따옴표를 적용하면 이러한 새 필드를 더 명확하게 표시하는 csv 파일을 얻을 수 있습니다. 이를 수행하는 Ruby 방법은 다음과 같습니다.

ruby -r csv -e '
cols={}
data=CSV.parse($<.read)
data.transpose.each_with_index{|sa,i| 
    cols[i]=sa.max_by{|e| e.length}; cols[i]=cols[i].length 
}
puts CSV.generate(force_quotes:true){|csv|
    data.each{|row|
        csv<<row.map.with_index{|e, i| e.rjust(cols[i] ) }
    }
}
' file

인쇄:

"a","b","c","some stuff","some, other, stuff","    d","2023-03-10 18:37:00"
"y","x","z","         t","              cool","thing","2022-04-12 21:44:00"

또는 인용된 필드와 인용되지 않은 필드를 정말로 원하는 경우 다음을 수행할 수 있습니다.

ruby -r csv -e '
lcl_csv_opt={:row_sep=>nil}
data=CSV.parse($<.read)
cols=data.transpose.map.with_index{|sa,i| 
    x=sa.max_by{|e| [e].to_csv(**lcl_csv_opt).length}
    [i,"#{[x].to_csv(**lcl_csv_opt)}"]
}.to_h
puts CSV.generate(){|csv|
    data.each{|row|
        csv<<row.map.with_index{|e, i| 
            [e].to_csv(**lcl_csv_opt)==cols[i] ? e : e.rjust(cols[i].length ) 
        }
    }
}
' file

인쇄:

a,b,c,some stuff,"some, other, stuff",    d,2023-03-10 18:37:00
y,x,z,         t,                cool,thing,2022-04-12 21:44:00

또한 필드에서 성가신 이스케이프 따옴표를 처리합니다. 반면:

$ cat file
a,b,c,some stuff,"some, other, stuff",d,2023-03-10 18:37:00
y,x,z,t,cool,"""thing"", quoted",2022-04-12 21:44:00

두 번째 버전은 다음을 인쇄합니다.

a,b,c,some stuff,"some, other, stuff",                  d,2023-03-10 18:37:00
y,x,z,         t,                cool,"""thing"", quoted",2022-04-12 21:44:00

답변2

쉼표가 포함된 텍스트가 포함된 큰따옴표 필드가 있습니다.

그렇다면 간단한 텍스트 구문 분석은 잊어버리세요. 복잡한 CSV를 구문 분석하고 예쁘게 인쇄할 수 있는 것을 얻으세요.

밀러선택하는 도구입니다. 출력 형식으로 "예쁜 인쇄"를 지정할 수 있습니다.

mlr --icsv --opprint cat example.csv

Python의 내장 csv모듈을 사용할 수도 있습니다.

import csv

rows = []
maxwidths = []
with open("foo.csv") as csvfile:
    reader = csv.reader(csvfile, delimiter=",", quotechar='"')
    for row in reader:
        for column_idx, entry in enumerate(row):
            if column_idx >= len(maxwidths):
                maxwidths += [len(entry)]
            else:
                maxwidths[column_idx] = max(maxwidths[column_idx], len(entry))
        rows += [row]

for row in rows:
    print(", ".join([f"{col:<{width}}" for col, width in zip(row, maxwidths)]))

답변3

FPATGNU awk 및 2단계 방법 사용 :

$ cat tst.awk
BEGIN {
    FPAT = "([^,]*)|(\"([^\"]|\"\")*\")"
    OFS = ", "
}
NR==FNR {
    for ( i=1; i<=NF; i++ ) {
        wid = length($i)
        wids[i] = ( wid > wids[i] ? wid : wids[i] )
    }
    next
}
{
    for ( i=1; i<=NF; i++ ) {
        printf "%*s%s", wids[i], $i, (i<NF ? OFS : ORS)
    }
}

$ awk -f tst.awk file file
a, b, c, some stuff, "some, other, stuff",     d, 2023-03-10 18:37:00
y, x, z,          t,                 cool, thing, 2022-04-12 21:44:00

또는 awk와 동일한 접근 방식을 사용하여 루프 호출을 사용하여 각 레코드를 필드로 분할 match()하고 FPAT해당 필드를 배열에 저장하는 코드를 직접 작성할 수 있습니다. 위의 gawk가 일반 필드의 일부로 이 작업을 수행하는 대신 나뉘다:

$ cat tst.awk
BEGIN {
    FPAT = "([^,]*)|(\"([^\"]|\"\")*\")"
    OFS = ", "
}
{
    nf = 0
    rec = $0
    while ( (rec != "") && match(rec,FPAT) ) {
        flds[++nf] = substr(rec,RSTART,RLENGTH)
        rec = substr(rec,RSTART+RLENGTH+1)
    }
}
NR==FNR {
    for ( i=1; i<=nf; i++ ) {
        wid = length(flds[i])
        wids[i] = ( wid > wids[i] ? wid : wids[i] )
    }
    next
}
{
    for ( i=1; i<=nf; i++ ) {
        printf "%*s%s", wids[i], flds[i], (i<nf ? OFS : ORS)
    }
}

$ awk -f tst.awk file file
a, b, c, some stuff, "some, other, stuff",     d, 2023-03-10 18:37:00
y, x, z,          t,                 cool, thing, 2022-04-12 21:44:00

입력을 두 번 읽는 대신 전체 입력을 메모리에 저장하고 END 섹션에 모두 인쇄하도록 선택할 수 있습니다. 이 방법의 장점은 파이프의 입력을 처리할 수 있다는 점이고, 단점은 입력 파일이 너무 커서 메모리에 넣을 수 없습니다. 이것은 GNU awk 버전입니다:

$ cat tst.awk
BEGIN {
    FPAT = "([^,]*)|(\"([^\"]|\"\")*\")"
    OFS = ", "
}
{
    for ( i=1; i<=NF; i++ ) {
        flds[NR,i] = $i
        wid = length($i)
        wids[i] = ( wid > wids[i] ? wid : wids[i] )
    }
}
END {
    for ( rowNr=1; rowNr<=NR; rowNr++ ) {
        for ( i=1; i<=NF; i++ ) {
            printf "%*s%s", wids[i], flds[rowNr,i], (i<NF ? OFS : ORS)
        }
    }
}

$ awk -f tst.awk file
a, b, c, some stuff, "some, other, stuff",     d, 2023-03-10 18:37:00
y, x, z,          t,                 cool, thing, 2022-04-12 21:44:00

그리고 모든 awk 버전:

$ cat tst.awk
BEGIN {
    FPAT = "([^,]*)|(\"([^\"]|\"\")*\")"
    OFS = ", "
}
{
    nf = 0
    rec = $0
    while ( (rec != "") && match(rec,FPAT) ) {
        fld = substr(rec,RSTART,RLENGTH)
        flds[NR,++nf] = fld
        wid = length(fld)
        wids[nf] = ( wid > wids[nf] ? wid : wids[nf] )
        rec = substr(rec,RSTART+RLENGTH+1)
    }
}
END {
    for ( rowNr=1; rowNr<=NR; rowNr++ ) {
        for ( i=1; i<=nf; i++ ) {
            printf "%*s%s", wids[i], flds[rowNr,i], (i<nf ? OFS : ORS)
        }
    }
}

$ awk -f tst.awk file
a, b, c, some stuff, "some, other, stuff",     d, 2023-03-10 18:37:00
y, x, z,          t,                 cool, thing, 2022-04-12 21:44:00

관련 정보