状態:csvファイルにはさまざまな日付形式の日付列があり、これを明示的な日付形式(例:+ "%m-%d-%Y")に変換したいと思います。
ファイルデータの例:ファイル名 = Date_Test_new.csv
3/29/2019, Test, "I am new to, Unix", 04-05-2023
03/29/19, Test, "I am new to, Unix", 04-5-2023
Apr-29-2019, Test, "I am new to, Unix", "Apr-01-2019"
3/29/2019, Test, "I am new to, Unix", "Apr-01-2019"
**Source Date formats**
DD-MMM-YY -> 08-Sep-23
DD-MMM-YYYY -> 08-Sep-2023
MM/DD/YY -> 09/08/23
MM/DD/YYYY -> 09/08/2023
MM-DD-YYYY -> 09-08-2023
YYYYMMDD -> 20230908
DDMMMYY -> 08Sep23
希望の出力
03-29-2019,Test, "I am new to, Unix", 04-05-2023
03-29-2019, Test, "I am new to, Unix", 04-05-2023
04-29-2019, Test, "I am new to, Unix", 04-01-2019
03-29-2019, Test, "I am new to, Unix", 04-01-2019
試しましたが、構文エラーが発生します。
awk -F ',' '$1="date -d "$1" +"%m-%d-%Y" " ' Date_Test_new.csv > New_Output.csv
つまり、ファイル内の日付形式の全列を別の日付形式に変換するにはどうすればよいですか?
どんな助けでも私にとっては素晴らしい学習経験になります。ありがとう
答え1
実際のデータが有効なCSV形式、つまりフィールドで区切られたコンマの後にスペースがないとし、GNU awkを使用し、3rg argを使用して次のようにしますFPAT
。gensub()
match()
$ cat tst.awk
BEGIN {
split("Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec",tmp)
for (i in tmp) {
mths[tmp[i]] = i
}
FPAT = "[^,]*|(\"([^\"]|\"\")*\")"
OFS = ","
}
{
for ( i=1; i<=NF; i++ ) {
val = gensub(/^"|"$/,"","g",$i)
day = mth = yr = 0
if ( match(val,/^([0-9]{1,2})-([[:alpha:]]{3})-([0-9]{2}|[0-9]{4})$/,d) ) {
# D-MMM-YY or DD-MMM-YY or ...YYYY
day = d[1]
mth = mths[d[2]]
yr = d[3]
}
else if ( match(val,/^([[:alpha:]]{3})-([0-9]{1,2})-([0-9]{2}|[0-9]{4})$/,d) ) {
# MMM-D-YY or MMM-DD-YY or ...YYYY
day = d[2]
mth = mths[d[1]]
yr = d[3]
}
else if ( match(val,/^([0-9]{1,2})\/([0-9]{1,2})\/([0-9]{2}|[0-9]{4})$/,d) ) {
# M/D/YY or M/DD/YY or MM/D/YY or MM/DD/YY or ...YYYY
day = d[2]
mth = d[1]
yr = d[3]
}
else if ( match(val,/^([0-9]{1,2})-([0-9]{1,2})-([0-9]{2}|[0-9]{4})$/,d) ) {
# M-D-YY or M-DD-YY or MM-D-YY or MM-DD-YY or ...YYYY
day = d[2]
mth = d[1]
yr = d[3]
}
else if ( match(val,/^([0-9]{2})([[:alpha:]]{3})([0-9]{2})$/,d) ) {
# DDMMMYY
day = d[1]
mth = mths[d[2]]
yr = d[1]
}
else if ( match(val,/^([0-9]{4})([0-9]{2})([0-9]{2})$/,d) ) {
# YYYYMMDD
day = d[3]
mth = d[2]
yr = d[1]
}
if ( length(yr) == 2 ) {
yr = "20" yr
}
day += 0
mth += 0
yr += 0
if ( (1 <= day) && (day <= 31) &&
(1 <= mth) && (mth <= 12) &&
(1 <= yr) && (yr <= 9999) ) {
$i = sprintf("%04d-%02d-%02d", yr, mth, day)
}
}
print
}
$ awk -f tst.awk Date_Test_new.csv
2019-03-29,Test,"I am new to, Unix",2023-04-05
2019-03-29,Test,"I am new to, Unix",2023-04-05
2019-04-29,Test,"I am new to, Unix",2019-04-01
2019-03-29,Test,"I am new to, Unix",2019-04-01
あなたがリストしたすべての日付形式などをカバーしましたが、else if ( match(...) ) { ... }
解析できる他の形式のブロックを追加するだけです。
match()
いくつかの正規表現を軽減して、より似たような呼び出しをマージするか、よりうまく機能する場合は、いくつかの正規表現をより厳密にすることもできます。
sprintf()
希望の出力形式に変更してください。しかし、引き続き使用することをお勧めします。ISO 8601日付形式、、YYYY-MM-DD
私はこの日付の後続処理を容易にするためにこれを使用します。
必要に応じて、小切手の有効日を自由に追加または変更できます。私たちはGNU awkを使用しているので、必要に応じて各ブロックの入力形式を覚えてから、下部でmktime()
新しく作成された日付をエポック以降の秒に変換し、strftime()
その秒を元の形式に変換して確認できます。元の日付と同じ場合は、正確な一致と変換が行われていることを確認してください。練習として残してください... :-)。
フィールドを区切った後にデータにスペースがある場合,
:
$ cat file
3/29/2019, Test, "I am new to, Unix", 04-05-2023
03/29/19, Test, "I am new to, Unix", 04-5-2023
Apr-29-2019, Test, "I am new to, Unix", "Apr-01-2019"
3/29/2019, Test, "I am new to, Unix", "Apr-01-2019"
以下を使用して有効なCSVに変換できます。
$ awk 'BEGIN{FS=OFS="\""} {for (i=1; i<=NF; i+=2) gsub(/, /,",",$i)} 1' file
3/29/2019,Test,"I am new to, Unix",04-05-2023
03/29/19,Test,"I am new to, Unix",04-5-2023
Apr-29-2019,Test,"I am new to, Unix","Apr-01-2019"
3/29/2019,Test,"I am new to, Unix","Apr-01-2019"
上記のすべての内容は、フィールドに改行文字を含めることはできないと仮定しています。フィールドに改行が含まれている場合は、さらに作業が必要です。 awkを使用してCSVを処理する方法の詳細については、以下を参照してください。awkを使用してcsvを効率的に解析する最も強力な方法は何ですか。