正確な数の区切り文字を含む行のみを保持する


9

コンマで区切られた10個のフィールドを持つ巨大なcsvファイルがあります。残念ながら、一部の行は形式が正しくなく、正確に10個のコンマが含まれていません(ファイルをRに読み込もうとすると問題が発生します)。正確に10個のコンマを含む行のみを除外するにはどうすればよいですか?


1
あなたの質問とリンクされた質問は同じ質問ではありません。特定の一致数以下の行を処理する方法を尋ねますが、その質問には最小一致数のみが必要です。実際には、その質問の方が簡単に答えられます。1 行を完全にスキャンする必要はありません。または、(少なくとも、sedここで行うように)探しているものよりも1つだけ一致する範囲だけをスキャンする必要はありません。これを閉じるべきではありませんでした。
mikeserv 2016年

1
実際、よく見てみると、そこにいる質問者マッチ以上またはそれ以下を望んでいません。その質問には新しいタイトルが必要です。しかし、grep答えはどちらの質問にも受け入れられません...
mikeserv

回答:


21

別のPOSIXの1つ:

awk -F , 'NF == 11' <file

行に10個のコンマがある場合、この行には11個のフィールドがあります。したがって、単にフィールド区切り文字としてawk使用,します。フィールド数が11の場合、条件NF == 11はtrueでありawk、デフォルトのアクションを実行しますprint $0


5
これが、この質問で最初に頭に浮かんだことです。やり過ぎだと思ったのですが、コードを見るとわかりやすいです。他の人のために:-Fフィールドセパレーターを設定NFし、指定された行のフィールド数を参照します。{statement}条件NF == 11にコードブロックは追加されないため、デフォルトのアクションは行を印刷することです。(@cuonglm、必要に応じてこの説明を自由に組み込んでください。)
ワイルドカード

4
+1:非常にエレガントで読みやすいソリューションで、非常に一般的です。たとえば、すべての不正な行を見つけることができますawk -F , 'NF != 11' <file
Miroslav Sabo

@gardenhead:OPがコメントで言ったように、簡単に入手できます。携帯から返事をすることもあるので、詳しい説明を付けるのは難しいです。
cuonglm

1
@mikeserv:いいえ、ごめんなさい、あなたを混乱させたら、それはただ私の悪い英語です。1〜9個のカンマを含む11個のフィールドは指定できません。
cuonglm

1
@OlivierDulac:で始まるファイル-や名前が付けられたファイルから保護します-
cuonglm

8

使用egrep(またはgrep -EPOSIX):

egrep "^([^,]*,){10}[^,]*$" file.csv

これは、10個のコンマを含まないものをすべて除外します。これは、「「、」を除く任意の数の文字、その後に単一の「、」を含む」シーケンスの正確な10回の繰り返し()を含む完全な行(^最初と$最後)に一致します。{10}([^,]*,))の後に、 '、'([^,]*)を除く任意の数の文字が続きます。

-xパラメータを使用してアンカーを削除することもできます。

grep -xE "([^,]*,){10}[^,]*" file.csv

ただし、これはcuonglmawkソリューションよりも効率的ではありません。後者の場合、私のシステムでは、コンマが約10行ある場合、通常6倍速くなります。ラインが長いと、速度が大幅に低下します。


5

機能する最も単純なgrepコード:

grep -xE '([^,]*,){10}[^,]*'

説明:

-xパターンはラインの一部ではなく、ライン全体と一致する必要があります。これは重要であり、コンマが10個を超える行を照合しないでください。

-E 「拡張正規表現」を意味します。これにより、正規表現でのバックスラッシュエスケープが少なくなります。

括弧はグループ化に使用され、{10}その後は、括弧内のパターンの行に正確に10個の一致がなければならないことを意味します。

[^,]文字クラス-のインスタンスは、されて[c-f]いる任意の単一文字一致しc、A deまたはf、そして[^A-Z]大文字ではありません任意の単一文字に一致します。したがって[^,]、カンマを除く任意の1文字に一致します。

*文字クラスの後は、「これらのゼロ以上」を意味します。

したがって、regexの部分([^,]*,)は「コンマを除いた任意の文字(ゼロ回を含む)を何回でも続けてコンマを続ける」を意味し{10}、これらのうち10個を指定します。次に[^,]*、残りの非コンマ文字を行末に一致させます。


5
sed -ne's/,//11;t' -e's/,/&/10p' <in >out

まず、11個以上のコンマを含む行を分岐し、次に、10個のコンマと一致するものだけを残して出力します。

どうやら私は以前にこれに答えたようです... いくつかのパターンの正確な4つの発生を探す質問からの盗用です

コマンドにを追加するだけ[num]で、sed s///ubstitutionコマンドでパターンの出現をターゲットにでき[num]ます。t置換の成功を予測し、ターゲット:ラベルを指定しない場合、testはスクリプトから分岐します。つまり、必要なのは、1 s///5つ以上のカンマをテストして、残っているものを出力することだけです。

または、少なくとも、最大4を超える行を処理します。どうやら、最小要件もあるようです。幸いなことに、それは同じくらい簡単です。

sed -ne 's|,||5;t' -e 's||,|4p'

... ,行の4番目のオカレンスをそれ自体で置き換えps///ubstitutionフラグにリントを追加します。,5回以上一致する行はすでに整理されているため、4つの,一致を含む行には4つしか含まれていません


1
@cuonglm-最初は私が実際に持っていたものですが、人々はいつもより読みやすいコードを書くべきだと私に言っています。他の人が論争しているものを読めないものとして読むことができるので、何を保持し何をドロップするのかわからない...?だから私は2番目のコンマを置きます。
mikeserv 2016年

@cuonglm-あなたは私をからかうことができます-それは私の気持ちを傷つけることはありません。冗談が取れます。あなたが私をからかっているなら、それは少しおかしいです。大丈夫-わからなかったし、知りたかった。私の意見では、人々は自分で笑うことができるはずです。とにかく、私はまだそれを取得しません!
mikeserv 2016年

はは、そうですね、とても前向きな考え方です。とにかく、おしゃべりをするのはとてもおもしろくて、時々、あなたは私の脳にストレス与えます。
cuonglm

この回答で興味深いことに、私がに置き換えるs/hello/world/2s//world/2、GNU sedは正常に機能します。sed家宝から2つ、/usr/5bin/posix/sedsegfaultを発生させ、/usr/5bin/sed無限ループに入ります。
cuonglm

@mikeserv、および(コメント内の)以前のディスカッションをsedawk参照して- 私はこの回答を気に入って賛成しましたが、受け入れられたawk回答の翻訳は「11フィールドの行を印刷する」であり、このsed回答の翻訳は: 11番目のコンマを削除してみてください。失敗した場合は次の行にスキップしてください。10番目のコンマをそれ自体に置き換えてみてください。成功した場合は行を印刷してください。」awkその答えは、コンピュータへの命令にあなたが英語でそれらを表現するのと同じ方法を提供します。(awkフィールドベースのデータに適しています。)
ワイルドカード'14年

4

少し投げるpython

#!/usr/bin/env python2
with open('file.csv') as f:
    print '\n'.join(line for line in f if line.count(',') == 10)

これは、各行を読み取り、その行のコンマの数が10 line.count(',') == 10に等しいかどうかを確認します。そうであれば、その行を印刷します。


2

そして、これがPerlの方法です:

perl -F, -ane 'print if $#F==10'

-n原因perlによって与えられたスクリプトラインで、入力ファイルの行を読み込んで実行するために-e各行のを。-a自動分割をオン:各入力線は、で与えられる値に分割される-F(ここで、カンマ)とアレイとして保存しました@F

$#F(または、より一般的に$#array)、アレイの最も高い指標です@F。配列はから始まるため0、11フィールドの行にはのが@Fあり10ます。したがって、スクリプトは、フィールドがちょうど11の場合、行を出力します。


print if @F==11スカラーコンテキストの配列が要素の数を返すようにすることもできます。
Sobrique

1

フィールドにカンマまたは改行を含めることができる場合、コードはcsv を理解する必要があります。例(3つの列):

$ cat filter.csv
a,b,c
d,"e,f",g
1,2,3,4
one,two,"three
...continued"

$ cat filter.csv | python3 -c 'import sys, csv
> csv.writer(sys.stdout).writerows(
> row for row in csv.reader(sys.stdin) if len(row) == 3)
> '
a,b,c
d,"e,f",g
one,two,"three
...continued"

これまでのほとんどの解決策では、2行目と4行目が破棄されると思います。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.