awk에서 필드 구분 기호로 큰따옴표 안의 쉼표를 이스케이프하세요.

awk에서 필드 구분 기호로 큰따옴표 안의 쉼표를 이스케이프하세요.

csv 파일에 두 개의 필드를 추가해야 합니다. csv 필드의 구분 기호는 comma이고 일부 필드는 큰따옴표 안에 있습니다. 문제는 큰따옴표로 묶인 필드 내에서도 쉼표를 찾을 수 있다는 것입니다. awk를 사용하여 분할하는 방법은 무엇입니까?
이 필드는 mongo 내보내기에서 가져온 것입니다. 이 필드의 위치는 변경될 수 있습니다.

입력 CSV 예시,

 DateTime,Dealers,Locations,CallEndTime,TotalDuration
"2018-12-27 12:19:14","Dealer1,Dealer2,Dealer3","Gujarat",,67,,
"2018-12-27 12:19:14","Dealer1,Dealer2","Gujarat,Vadodara",,100,

예제 출력 CSV,

 DateTime,Dealers,Locations,CallEndTime,TotalDuration
"2019-01-07 11:35:42","Dealer1,Dealer2,Dealer3","Gujarat","2019-01-07 11:36:51",69,,
"2018-12-27 12:19:14","Dealer1,Dealer2","Gujarat,Vadodara","2018-12-27 12:19:14,78",

이상한 코드:

BEGIN { FSOFS=","}
NR==1 {
        for (i=1; i<=NF; i++) {
            f[$i] = i
        }
      }
NR>1  {
        begSecs = mktime( gensub( /[":-]/, " ", "g", $(f["DateTime"]) ) )
        endSecs = begSecs + $(f["TotalDuration"])
        $(f["CallEndTime"]) = strftime("%Y-%m-%d %H:%M:%S", endSecs)
}
{print}

큰따옴표 안의 쉼표가 FS로 처리되는 것을 원하지 않습니다. FPAT를 사용하여 이 작업을 수행할 수 있다는 것을 보았지만 여기서는 다음과 같은 경우를 대비하여 사용하는 방법에 대한 단서가 없습니다.

BEGIN { FPAT = "([^,]*)|(\"[^\"]+\")"}
NR==1 {
        for (i=1; i<=NF; i++) {
            f[$i] = i
        }
      }
NR>1  {
        begSecs = mktime( gensub(/[":-]/," ","g",$(f["DateTime"])) )
        endSecs = begSecs + $(f["TotalDuration"])
        $(f["CallEndTime"]) = strftime("%Y-%m-%d %H:%M:%S", endSecs)
      }
{print}

답변1

나는 그것을 csv 파일을 구문 분석하는 데 사용하지 않을 것입니다. awk예를 들어 python csv 모듈을 사용하는 전용 도구를 사용하는 것이 더 나을 것입니다.

#!/usr/bin/env python3
import csv, shutil
from tempfile import NamedTemporaryFile
from datetime import datetime 
from datetime import timedelta

tempfile = NamedTemporaryFile(mode='w', delete=False)

with open('input.csv') as csvfile:
    reader = csv.DictReader(csvfile)
    writer = csv.DictWriter(tempfile, fieldnames=reader.fieldnames)
    writer.writeheader()

    for row in reader:
        row['CallEndTime']=datetime.strptime(row['DateTime'], '%Y-%m-%d %H:%M:%S') + timedelta(seconds=int(row['TotalDuration']))
        writer.writerow(row)

shutil.move(tempfile.name, 'output.csv')

출력.csv:

DateTime,Dealers,Locations,CallEndTime,TotalDuration
2018-12-27 12:19:14,"Dealer1,Dealer2,Dealer3",Gujarat,2018-12-27 12:20:21,67
2018-12-27 12:19:14,"Dealer1,Dealer2","Gujarat,Vadodara",2018-12-27 12:20:54,100

답변2

csvkit >= 1.0.4(현재 개발 버전)에서는 다음을 사용할 수 있습니다 csvsql.

csvsql --query '
    update input
    set CallEndTime = datetime(DateTime,"+"||TotalDuration||" seconds");
' input.csv

답변3

두 번째 예는 거의 작동합니다. ,출력 구분 기호( OFS=",")와 새로 계산된 날짜 주위에 큰따옴표가 누락되었습니다 . 이것은 작동합니다:

BEGIN { FPAT = "([^,]*)|(\"[^\"]+\")"; OFS=","}
NR==1 {
        for (i=1; i<=NF; i++) {
            f[$i] = i
        }
      }
NR>1  {
        begSecs = mktime( gensub(/[":-]/," ","g",$(f["DateTime"])) )
        endSecs = begSecs + $(f["TotalDuration"])
        $(f["CallEndTime"]) = "\"" strftime("%Y-%m-%d %H:%M:%S", endSecs) "\""
      }
{print}

주어진 예에서.

하지만 csv에는 awk가 처리할 수 있는 것보다 훨씬 더 많은 콘텐츠가 있습니다. 다른 답변에서 이미 제안했듯이 csv 형식을 올바르게 이해하는 도구를 사용하십시오.

답변4

BEGIN {
    FPAT="\"[^\"]*\"|[^,]*"
}

예를 들어 모든 값을 추출하고 구분 기호를 쉼표로 바꾸려면 다음과 같이 하십시오 --.

awk 'BEGIN { OFS = "--"; FPAT="\"[^\"]*\"|[^,]*"} NR > 1 { print $1, $2, $3, $4, $5 }' input.csv

출력 예:

"2018-12-27 12:19:14"--"Dealer1,Dealer2,Dealer3"--"Gujarat"----67
"2018-12-27 12:19:14"--"Dealer1,Dealer2"--"Gujarat,Vadodara"----100

FS필드 구분 기호를 정의합니다. 즉, 필드가 무엇인지 정의합니다.아니요.

FPAT반면에 필드가 정의됩니다..


그런데 input.csv 예제의 첫 번째 행에는 6개의 값이 있고, 두 번째 행과 헤더 행에는 5개의 열이 있어야 함을 나타냅니다.

관련 정보