大きな(> 1 GB)csvファイルには、次のようなものがあります。
"34432", "name", "0", "very long description"
しかし、代わりに私は持ってみたい
34432, "name", 0, "very long description".
sed
を見ていましたが、このタスクは私の範囲外です。
これを達成する方法はありますか?
Perlの使用:
Perl -ne 's/"(\d+)"/$1/g; print' file.csv > new_file.txt
すべての作業はs/"(\d+)"/$1/g
によって行われます。ここで
s/patternA/patternB/
は、patternA
をpatternB
に置き換えるために使用されます\d+
の1つ以上の数字を探します。\d+
)を囲む括弧は、数字をキャプチャし、Perlの特殊変数$1
で置換パターンとして再利用するために使用されます。この場合に機能するGNU sed正規表現は
sed -r 's/"([0-9]+)"/\1/g'
純粋なsedの場合、グループ化括弧と+
修飾子をエスケープする必要があります
sed 's/"\([0-9]\+\)"/\1/g'
Sedの一部のバージョンでは、その場で置換を実行できます。
sed -ri 's/"([0-9]+)"/\1/g' file.csv
文字範囲[[:digit:]]
の代わりにPOSIXクラス[0-9]
を使用することもできます
問題の説明はあまり明確ではありません。 1番目と3番目のフィールドのみの二重引用符を削除することを想定しています。もしそうなら、これらのいずれかが動作するはずです:
sed
sed -r 's/^"([^"]+)"(\s*,\s*[^,]+)\s*,\s*"([^"]+)"/\1\2, \3/' file.csv
-r
により拡張正規表現が有効になり、括弧を使用してパターンをエスケープせずにキャプチャできます。したがって、行の先頭に引用符(^"
)が続き、その後に1つ以上の引用符以外の文字([^"]+
)が続き、最後の引用符に0個以上のスペースが続きます。コンマ、次に0個以上のスペース(\s*,\s*
)、次のコンマまでのコンマ以外の部分(これは2番目のフィールドを定義します)。最後に、0個以上のスペース、カンマを探し、それを最初のキャプチャパターン(\1
)、次に2番目(\2
)、コンマ、スペース、3番目に置き換えます。
Perl
Perl -pe 's/^"([^"]+)"(\s*,\s*[^,]+)\s*,\s*"([^"]+)"/$1$2, $3/; ' file.csv
-p
は、-e
によって渡されたスクリプトを適用した後、すべての行を印刷することを意味します。スクリプト自体は、基本的に上記のsed
と同じ正規表現です。ここでのみ、キャプチャされるパターンは$1
です。
awk
awk -F, -v OFS="," '{gsub("\"","",$1)0gsub("\"","",$3);}1;' file.csv
-F
は、フィールドセパレーターを,
に設定します。 OFS
は出力フィールド区切り文字であり、,
にも設定されているため、行が正しく印刷されます。 gsub
は置換を行い、1番目("
)および3番目のフィールド($1
)で実行するため、すべての$3
を何も置き換えません。これらのフィールドから引用符のみを削除します。 1;
は、「行を印刷する」ための単なるawk
の省略形です。
以下の小さなスクリプトは、ファイルのコマンドライン引数を取り、そのファイルの各行を反復処理し、,
を区切り文字として使用して各行をアイテムのリストに分割します。各エントリは引用符で囲まれず、数値文字列であるかどうかがチェックされます。文字列が数値の場合、引用符は付けられません。
#!/usr/bin/env python
import sys
with open(sys.argv[1]) as fp:
for line in fp:
new_vals = []
vals = line.strip().split(',')
for val in vals:
val = val.strip().rstrip().replace('"','')
if not val.isdigit():
val = '"' + val + '"'
new_vals.append(val)
print(",".join(new_vals))
テスト走行:
$ cat input.txt
"34432", "name", "0", "very long description"
"1234", "othe name" , "42", "another description"
$ ./unquote_integers.py input.txt
34432,"name",0,"very long description"
1234,"othe name",42,"another description"
追加のメモ:
コメントで尋ねられたのは、なぜアイテムが数値文字列であるかどうかを評価する前に、スクリプトが各アイテムを囲む二重引用符を削除する理由です。その主な理由は、二重引用符を含めると、"123"
のような項目がFalse
に評価されるため、つまり非数値になるためです。事実上、二重引用符内の内容を何らかの方法で評価する必要があります。現在、各値のリストスライスを取得することで、これにアプローチする別の方法があります。ただし、最初から.replace()
を使用するよりも優れています。それはコードを短くしますが、少なくともこの場合、スクリプトの短さは無関係です-私たちの目標は、コードゴルフではなく、コードを機能させることです。
リストスライスを使用した代替ソリューションは次のとおりです。
#!/usr/bin/env python
import sys
with open(sys.argv[1]) as fp:
for line in fp:
new_vals = []
vals = line.strip().split(',')
for val in vals:
val = val.strip().rstrip() #remove extra spaces
val = val.replace('"','') if val[1:-1].isdigit() else val
new_vals.append(val)
print(",".join(new_vals))