コンマで区切られた入力ファイル(,
)。二重引用符で囲まれた一部のフィールドには、コンマが含まれています。これはサンプル行です
123,"ABC, DEV 23",345,534.202,NAME
二重引用符内にあるすべてのコンマと二重引用符も削除する必要があります。したがって、上記の行は次のように解析されます。
123,ABC DEV 23,345,534.202,NAME
sed
を使用して次のことを試しましたが、期待した結果が得られませんでした。
sed -e 's/\(".*\),\(".*\)/\1 \2/g'
sed
、awk
、またはその他のUNIXユーティリティを使用した簡単なトリックはありますか?
引用符のバランスが取れている場合、他のすべての引用符の間のコンマを削除する必要があります。これは、次のようにawk
で表すことができます。
awk -F'"' -v OFS='' '{ for (i=2; i<=NF; i+=2) gsub(",", "", $i) } 1' infile
出力:
123,ABC DEV 23,345,534.202,NAME
説明
-F"
を指定すると、二重引用符記号で行がawkで区切られます。つまり、他のすべてのフィールドが引用符間テキストになります。 forループは、コンマ(","
)を何も使用しない(""
)に置き換えて、gsub
を実行します。最後の1
は、デフォルトのコードブロック{ print $0 }
を呼び出します。
good応答があり、sedをloopで1回だけ使用します。
_echo '123,"ABC, DEV 23",345,534,"some more, comma-separated, words",202,NAME'|
sed ':a;s/^\(\([^"]*,\?\|"[^",]*",\?\)*"[^",]*\),/\1 /;ta'
123,"ABC DEV 23",345,534,"some more comma-separated words",202,NAME
_
説明:
:a;
_は、さらに分岐するためのラベルです。s/^\(\([^"]*,\?\|"[^",]*",\?\)*"[^",]*\),/\1 /
には、3つの囲まれた部分を含めることができます[^"]*,\?\|"[^",]*",\?
_一致。その後にコマが続く場合がありますorコマなしの2つの二重引用符で囲まれた文字列で、コマが続く場合があります。ta
は、前の_:a
_コマンドに変更があった場合、_s/
_にループします。バランスの取れた引用符間の複数のコンマも処理できる一般的なソリューションでは、入れ子の置換が必要です。私はPerlでソリューションを実装します。これは、特定の入力のすべての行を処理し、引用符のペアごとにコンマのみを置換します。
Perl -pe 's/ " (.+? [^\\]) " # find all non escaped
# quoting pairs
# in a non-greedy way
/ ($ret = $1) =~ (s#,##g); # remove all commas within quotes
$ret # substitute the substitution :)
/gex'
要するに
Perl -pe 's/"(.+?[^\\])"/($ret = $1) =~ (s#,##g); $ret/ge'
処理するテキストをコマンドにパイプするか、処理するテキストファイルを最後のコマンドライン引数として指定できます。
2番目の引用は間違っています:
sed -e 's/\(".*\),\(.*"\)/\1 \2/g'
さらに、正規表現を使用すると、テキストの可能な限り長い部分に一致する傾向があります。つまり、文字列に引用符で囲まれたフィールドが複数ある場合、これは機能しません。
sed -e 's/\(\"[^",]\+\),\([^",]*\)/\1 \2/g' -e 's/\"//g'
これはこれを解決する方法でもありますが、引用符で囲まれたフィールドごとに複数のコンマを含む可能性のある入力では、sedの最初の式を単一のフィールドの最大コンマコンテンツと同じ回数、またはそれまで繰り返す必要があります。出力はまったく変更されません。
複数の式を使用してsedを実行することは、いくつかのsedプロセスを実行し、すべて "tr"を開いたパイプで実行するよりも効率的です。
ただし、入力が適切にフォーマットされていない場合、これにより望ましくない結果が生じる可能性があります。つまり、ネストされた引用符、終了していない引用符。
実行例を使用して:
echo '123,"ABC, DEV 23",345,534,"some more, comma-separated, words",202,NAME' \
| sed -e 's/\(\"[^",]\+\),\([^",]*\)/\1 \2/g' \
-e 's/\(\"[^",]\+\),\([^",]*\)/\1 \2/g' -e 's/\"//g'
出力:
123,ABC DEV 23,345,534,some more comma-separated words,202,NAME
適切なCSVパーサーで言語を使用します。例えば:
Ruby -r csv -ne '
CSV.parse($_) do |row|
newrow = CSV::Row.new [], []
row.each {|field| newrow << field.delete(",")}
puts newrow.to_csv
end
' < input_file
Pythonの使用
''.join([item if index % 2 == 0 else re.sub(',', '', item) for index, item in enumerate(row.split('"')) ])
文字列のすべての文字をループする関数を作成しました。
文字が引用である場合、チェック(b_in_qt)はtrueとマークされます。
b_in_qtがtrueの場合、すべてのコンマはスペースに置き換えられます。
次のカンマが見つかると、b_in_qtはfalseに設定されます。
FUNCTION f_replace_c (str_in VARCHAR2) RETURN VARCHAR2 IS
str_out varchar2(1000) := null;
str_chr varchar2(1) := null;
b_in_qt boolean := false;
BEGIN
FOR x IN 1..length(str_in) LOOP
str_chr := substr(str_in,x,1);
IF str_chr = '"' THEN
if b_in_qt then
b_in_qt := false;
else
b_in_qt := true;
end if;
END IF;
IF b_in_qt THEN
if str_chr = ',' then
str_chr := ' ';
end if;
END IF;
str_out := str_out || str_chr;
END LOOP;
RETURN str_out;
END;
str_in := f_replace_c ("blue","cat,dog,horse","",yellow,"green")
RESULTS
"blue","cat dog horse","",yellow,"green"