巨大なファイルの改行を含む文字列を置換


16

メモリー効率のよい方法で文字列を「バイナリ」検索/置換する非行ベースのツールを知っている人はいますか?この質問もご覧ください。

+ 2GBのテキストファイルがあり、これと同じように処理したいと思います。

sed -e 's/>\n/>/g'

つまり、の後にあるすべての改行を削除したいの>ですが、他の場所ではなく、除外しtr -dます。

このコマンド(同様の質問の答えから得た)は次のように失敗しcouldn't re-allocate memoryます:

sed --unbuffered ':a;N;$!ba;s/>\n/>/g'

だから、Cに頼らずに他の方法はありますか?私はperlが嫌いですが、この場合は例外を作りたいです:-)

データ内に出現しない文字がわからないので、一時的に\n別の文字に置き換えることは可能な限り避けたいものです。

良いアイデアはありますか?


オプションを試しました--unbufferedか?
ctrl-alt-delor 14年

有無にかかわらず--unbufferedメモリ不足
マット・ビアンコ

何を$!するの?
ctrl-alt-delor 14年

最初のsedコマンドの何が問題になっていますか。2番目はパターンスペースにすべてを読み込んでいるようですが、それがそうだとは知りません$!。これには多くのメモリが必要になると思います。
ctrl-alt-delor 14年

問題は、sedがすべてを行として読み取ることです。これが、最初のコマンドがテキストを行ごとに再度出力するため、改行を削除しない理由です。2番目のコマンドは単なる回避策です。sedこの場合、適切なツールではないと思います。
マットビアンコ14年

回答:


14

これはPerlでは本当に些細なことです。あなたはそれを嫌うべきではありません!

perl -i.bak -pe 's/>\n/>/' file

説明

  • -i:ファイルを所定の場所で編集し、元のファイルのバックアップを作成しますfile.bak。バックアップが必要ない場合は、perl -i -pe代わりに使用してください。
  • -pe:入力ファイルを1行ずつ読み取り、-e。として指定されたスクリプトを適用した後、各行を出力します。
  • s/>\n/>/:置換、ちょうどのようにsed

そして、ここにawkアプローチがあります:

awk  '{if(/>$/){printf "%s",$0}else{print}}' file2 

3
+1。awkゴルフ:awk '{ORS=/>$/?"":"\n"}1'
グレンジャックマン14年

1
perlが一般的に嫌いな理由は、この答え(または実際にGnoucの答えに対するあなたのコメント)を選んだ理由と同じ理由です:読みやすさ。単純な「sedパターン」でperl -peを使用すると、複雑なsed式よりも読みやすくなります。
MattBianco 14年

3
@MattBiancoは十分に公平ですが、ご存知のとおり、これはPerlとは関係ありません。Gnoucが使用した後読みは、一部の正規表現言語(PCREを含むがこれに限定されない)の機能であり、Perlの欠点ではありません。また、':a;N;$!ba;s/>\n/>/g'あなたの質問にこのセドの怪物を取り上げた後、あなたは読みやすさについて文句を言う権利を放棄しました!:P
テルドン

よろしくお願いします!foo ? bar : bazコンストラクトで遊んでいましたが、機能させることができませんでした。
テルドン

@terdon:うん、私の間違い。消して。
cuonglm 14年

7

perl解決策:

$ perl -pe 's/(?<=>)\n//'

説明

  • s/// 文字列の置換に使用されます。
  • (?<=>) 後読みパターンです。
  • \n 改行に一致します。

パターン全体は、その>前にあるすべての改行を削除することを意味します。


2
プログラムの各部が何をするのかコメントしてください。私は常に学びたいと思っています。
MattBianco 14年

2
なぜ後読みに悩むのですか?どうしてs/>\n/>/
テルドン

1
またはs/>\K\n//動作します
グレンジャックマン14年

@terdon:ちょうど私も、削除の代わりに置き換える最初のもの
cuonglm

@glennjackman:いいね!
クオンルム

3

これはどう:

sed ':loop
  />$/ { N
    s/\n//
    b loop
  }' file

GNU sedの場合、質問に従って-u--unbuffered)オプションを追加することもできます。GNU sedは、シンプルなワンライナーとしてこれにも満足しています。

sed ':loop />$/ { N; s/\n//; b loop }' file

\nファイルがで終わる場合、最後は削除されません>\nが、とにかくおそらくそれが望ましいでしょう。
ステファンシャゼル14年

@StéphaneChazelas、クロージングを}別の表現で行う必要があるのはなぜですか?これは複数行の式として機能しませんか?
グレアム14年

1
これは、とPOSIXのSEDSに動作するb loop\n}-e 'b loop' -e '}'ではなく、などのb loop;}ないよう確実かつb loop}理由};ラベル名で有効です(彼らの権利念頭に置いて誰もがそれを使用しないだろうけれども。そして、GNUのsedはその手段はPOSIX準拠ではありません)と}分離するコマンドニーズbコマンドから。
ステファンシャゼル14年

@StéphaneChazelas、GNU sedは上記のすべてに満足してい--posixます!標準には、ブレース式について次のものもあります- The list of sed functions shall be surrounded by braces and separated by <newline>s。これは、セミコロンをブレースの外側でのみ使用する必要があるという意味ではありませんか?
グレアム14年

@mikeserv、で終わる連続した行を処理するにはループが必要です>。オリジナルには1つもありませんでしたが、これはステファンによって指摘されました。
グレアム14年

1

あなたは使用することができるはずsedNコマンドが、トリックは、パターンスペースから、あなたが(別のものを追加するたびに1行を削除することになりますので、パターンスペースがなく、常に全体を読み込むしようとしているの、唯一の2連続した行が含まれていることファイル)-試してください

sed ':a;$!N;s/>\n/>/;P;D;ba'

編集:Peteris Kruminsの有名なSed One-Linersの説明読み直した後、私はより良いsed解決策があると信じています

sed -e :a -e '/>$/N; s/\n//; ta'

既に>最後に一致している場合にのみ次の行を追加し、条件に応じてループバックして、連続する一致行のケースを処理する必要があります(クルミンの39です。バックスラッシュで終了する場合は次に行を追加します「\」まさにの置換を除く>ための\文字、文字が出力に保持されて参加しているという事実を)結合として。


2
2つの連続した行がで終わっていれば、仕事をしません>(それものGNUの特定)
ステファンChazelas

1

sed最後の改行なしで出力を出力する方法を提供しません。N基本的に使用するアプローチは機能しますが、メモリに不完全な行を格納するため、行が長くなりすぎると失敗する可能性があります(sed implentationsは通常、極端に長い行を処理するように設計されていません)。

代わりにawkを使用できます。

awk '{if (/<$/) printf "%s", $0; else print}'

別のアプローチは、使用することです tr、改行文字を「退屈な」頻繁に発生する文字と交換することです。ここでスペースが機能する場合があります。データのすべての行または少なくとも大部分の行に表示される傾向がある文字を選択します。

tr ' \n' '\n ' | sed 's/> />/g' | tr '\n ' ' \n'

どちらの方法も、他の回答で効果を高めるためにここですでに実証されています。そして、彼のアプローチsedは2.5ギガバイトのバッファなしでは機能しません。
mikeserv 14年

誰かがawkについて言及しましたか?ああ、私はそれを逃しました、私は何らかの理由でperdonがterdonの答えに気づいただけでした。このtrアプローチについては誰も言及していませんでした— mikeserv、あなたは別の(有効だが一般的ではない)アプローチを投稿しましたtr
ジル 'SO-悪であるのをやめる' 14年

有効であるが、あまり一般的ではないように聞こえますが、youveはそれを機能するターゲットソリューションと呼んでいます。そのようなことは役に立たないと主張するのは難しいと思います。私のソリューションとあなたのより一般的な製品との最大の違いは、私のものが特に問題を解決するのに対して、一般的にはそうすることです。それは価値があるかもしれません-そして私は私の投票を逆にするかもしれません-しかし、それらの間の7時間の厄介な問題と他人を模倣するあなたの答えの繰り返しのテーマもあります。これを説明できますか?
mikeserv 14年



-1

これを行うには多くの方法があり、ここでのほとんどは本当に良いですが、これは私のお気に入りだと思います:

tr '>\n' '\n>' | sed 's/^>*//;H;/./!d;x;y/\n>/>\n/'

あるいは:

tr '>\n' '\n>' | sed 's/^>*//' | tr '\n>' '>\n'

仕事に対する最初の答えがまったく得られません。私は2番目の優雅さを賞賛しますが、を削除する必要があると思います*。現在の方法では、で終わる行に続く空白行は削除されます>。… うーん。質問を振り返ってみると、少しあいまいなことがわかります。質問は、「>…の後に発生するすべての改行を削除したい」と述べていますが、それ>\n\n\n\n\nfooをに変更する必要があることを意味すると解釈し\n\n\n\nfooますfooが、望ましい出力になると思い ます。
スコット14年

@Scott-次のバリエーションでテストしました。- 最初の答えprintf '>\n>\n\n>>\n>\n>>>\n>\nf\n\nff\n>\n' | tr '>\n' '\n>' | sed 's/^>*//;H;/./!d;x;y/\n>/>\n/'が得られ>>>>>>>>>>f\n\nff\n\nます。私はそれを修正したいので、私はあなたがそれを破るために何をしているのか興味があります。2番目の点については、曖昧であることには同意しません。OPは削除するように依頼されません全てが > 先行する\n ewlineを、代わりに削除するために、すべての \n ewlines 以下>
mikeserv 14年

1
はい。ただし、有効な解釈は、では>\n\n\n\n\n、最初の改行のみが>;の後です。他のすべては他の改行に従っています。OPの「これが機能する場合にのみこれが欲しい」という提案はそうsed -e 's/>\n/>/g'ではないことに注意してくださいsed -e 's/>\n*/>/g'
スコット14年

1
@Scott-提案は機能せず、機能しませんでした。私は、コードを完全に理解していない人のコード提案が、その人も使用している平易な言語としての解釈ポイントとして有効であると考えることができるとは思わない。それに、出力-それは、実際に働いていた場合-のs/>\n/>/上には、>\n\n\n\n\nまだそのものになるだろうs/>\n/>/う編集。
mikeserv 14年
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.