awkでフィールド区切り記号で二重引用符の中のカンマをエスケープします。

awkでフィールド区切り記号で二重引用符の中のカンマをエスケープします。

csvファイルに2つのフィールドを追加する必要があります。 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

2番目の例はほとんど機能します。,出力区切り記号(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つの値があり、2番目の行とヘッダー行には5つの列があることを示します。

関連情報