web-dev-qa-db-ja.com

カンマ区切りファイルでのみ、引用符の間のカンマを削除します

コンマで区切られた入力ファイル(,)。二重引用符で囲まれた一部のフィールドには、コンマが含まれています。これはサンプル行です

123,"ABC, DEV 23",345,534.202,NAME

二重引用符内にあるすべてのコンマと二重引用符も削除する必要があります。したがって、上記の行は次のように解析されます。

123,ABC DEV 23,345,534.202,NAME

sedを使用して次のことを試しましたが、期待した結果が得られませんでした。

sed -e 's/\(".*\),\(".*\)/\1 \2/g'

sedawk、またはその他のUNIXユーティリティを使用した簡単なトリックはありますか?

23
mtk

引用符のバランスが取れている場合、他のすべての引用符の間のコンマを削除する必要があります。これは、次のように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 }を呼び出します。

32
Thor

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つの囲まれた部分を含めることができます
    • 最初の2番目:二重引用符を含まない文字列の_[^"]*,\?\|"[^",]*",\?_一致。その後にコマが続く場合がありますorコマなしの2つの二重引用符で囲まれた文字列で、コマが続く場合があります。
    • 最初のREパートよりも前に説明したパート2の繰り返しで構成され、その後に1つの二重引用符といくつかの文字が続きますが、二重引用符はありません。昏睡状態でもありません。
    • コマが続く最初のREパート。
    • 注:行の残りの部分に触れる必要はありません
  • taは、前の_:a_コマンドに変更があった場合、_s/_にループします。
7
F. Hauri

バランスの取れた引用符間の複数のコンマも処理できる一般的なソリューションでは、入れ子の置換が必要です。私は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'

処理するテキストをコマンドにパイプするか、処理するテキストファイルを最後のコマンドライン引数として指定できます。

5
user1146332

2番目の引用は間違っています:

sed -e 's/\(".*\),\(.*"\)/\1 \2/g'

さらに、正規表現を使用すると、テキストの可能な限り長い部分に一致する傾向があります。つまり、文字列に引用符で囲まれたフィールドが複数ある場合、これは機能しません。

Sedで複数の引用符付きフィールドを処理する方法

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
3
Didi Kohen

適切な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
3
glenn jackman

Pythonの使用

''.join([item if index % 2 == 0 else re.sub(',', '', item) for index, item in enumerate(row.split('"')) ])
0
LoMaPh

文字列のすべての文字をループする関数を作成しました。
文字が引用である場合、チェック(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"
0
user143598