文字列置換のための非行指向ツール?


13

私は最近、改行文字が別の特定の文字の後に発生した場合に改行文字を削除する方法について質問しました

Unixテキスト処理ツールは非常に強力ですが、それらのほとんどすべてがテキスト行を処理します。これは、ほとんどの場合、入力が使用可能なメモリに収まる場合は問題ありません。

しかし、改行を含まない巨大なファイルのテキストシーケンスを置き換える場合はどうすればよいですか?

たとえば置き換える<foobar>\n<foobar>、入力ライン・バイ・ラインを読まず?(1行しかないため、長さは2.5G文字です)。


1
perlまたはを使用することに同意していpythonますか?
iruvar

Perlは大丈夫です。試したところgsarhome.online.no/~tjaberg)を見つけました。
MattBianco

回答:


12

この種の問題に直面したときに私に最初に起こることは、レコード区切り文字を変更することです。ほとんどのツールでは、これは\nデフォルトで設定されていますが、変更できます。例えば:

  1. Perl

    perl -0x3E -pe 's/<foobar>/\n$&/' file
    

    説明

    • -0:これは入力レコードセパレーターを16進値が与えられた文字に設定します。この場合、>16進値がに設定されています3E。一般的な形式は-0xHEX_VALUEです。これは、行を管理可能なチャンクに分割するための単なるトリックです。
    • -pe:で指定されたスクリプトを適用した後、各入力行を出力し-eます。
    • s/<foobar>/\n$&/:単純な置換。$&この場合には、マッチしたものは何でもあります<foobar>
  2. awk

    awk '{gsub(/foobar>/,"\n<foobar>");printf "%s",$0};' RS="<" file
    

    説明

    • RS="<":入力レコード区切り文字をに設定します>
    • gsub(/foobar>/,"\n<foobar>"):すべてのケースをfoobar>で置き換えます\n<foobar>。なおので、RSに設定されている<、全ての<(方法ですその入力ファイルから削除されているawk私たちが一致する必要があるので、作品)foobar>(なし<)で置き換えます\n<foobar>
    • printf "%s",$0:置換後の現在の「行」を出力します。$0は現在のレコードであるawkので、それは以前のものを保持し<ます。

これらのコマンドで作成された2.3 GBの単一行ファイルでこれらをテストしました。

for i in {1..900000}; do printf "blah blah <foobar>blah blah"; done > file
for i in {1..100}; do cat file >> file1; done
mv file1 file

両方awkperlメモリの使用無視できる量。


Tie::File perldoc.perl.org/Tie/File.htmlを試したことはありますか。Perl巨大なファイルを扱うときの最高の機能だと思います。
cuonglm 14年

@Gnouc少し遊んだよ、はい。しかし、i)OPは別の質問でPerlの嫌悪感をすでに表明しているので、シンプルにしたかったii)絶対に必要でない限り外部モジュールの使用を避ける傾向があり、iii)Tie :: Fileモジュールを使用すると構文がかなり少なくなる晴れ。
テルドン

同意する。Tie::Fileそれ以降のコアモジュールである小さなメモv5.7.3
cuonglm 14年

9

gsar (一般的な検索と置換)は、まさにこの目的に非常に役立つツールです。

この質問へのほとんどの回答は、レコードベースのツールとさまざまなトリックを使用して、それらを問​​題に適応させます。たとえば、デフォルトのレコード区切り文字を、各レコードを処理するには大きすぎないように入力で十分頻繁に発生すると想定されるものに切り替えます。

多くの場合、これは非常に細かく読みやすいものです。私は簡単に/効率的にどこでも利用可能なようなツールを使って解くことができる問題好きですawktrsedおよびBourneシェル。

バイナリ検索を実行し、任意の巨大なファイルをランダムなコンテンツで置き換えることは、これらの標準的なUNIXツールにはあま​​り適していません。

これは不正行為だと思う人もいるかもしれませんが、仕事に適切なツールを使用することが間違っている可能性はありません。この場合、それはGPL v2のgsar下でライセンスされていると呼ばれるCプログラムですので、どちらのgentooにもこの非常に便利なツールのパッケージがないことにかなり驚いていますredhatubuntuの

gsarBoyer-Moore文字列検索アルゴリズムのバイナリバリアントを使用します

使い方は簡単です:

gsar -F '-s<foobar>' '-r:x0A<foobar>'

ここで、-F「フィルター」モード、つまり、読み取りstdin書き込みstdoutます。ファイルを操作する方法もあります。-s検索文字列と-r置換を指定します。コロン表記を使用して、任意のバイト値を指定できます。

大文字と小文字を区別しないモードはサポートされていますが(-i)、アルゴリズムは検索文字列の長さを使用して検索を最適化するため、正規表現はサポートされていません。

このツールは、検索に使用することもできgrepます。gsar -bマッチした検索文字列のバイトオフセットを出力し、gsar -lもしあればマッチのファイル名と番号を印刷し、ビットの組み合わせのようgrep -lwc

このツールは、Tormod Tjaberg(初期)およびHans Peter Verne(改善)によって作成されました。


GPLの場合、ディストリビューション用にパッケージ化することを検討します:)
Rqomey 14年

1
実際、私はgentooのebuildを作成することをかなり真剣に考えています。たぶんrpmも。しかし、.debパッケージをビルドしたことがないので、誰かがそれに打ち勝つことを願っています(時間がかかるため)。
MattBianco 14年

これはあまり慰めとは思いませんが、OS Xの自作にはの公式がありgsarます。
crazysim

5

ターゲット文字列と置換文字列が同じ長さの狭いケースでは、メモリマッピングが役立ちます。これは、置換をその場で実行する必要がある場合に特に役立ちます。基本的に、ファイルをプロセスの仮想メモリにマッピングしているため、64ビットアドレッシングのアドレス空間は膨大です。ファイルは必ずしも物理メモリに一度にマッピングされるとは限らないことに注意してくださいにため、マシンで使用可能な物理メモリのサイズの数倍のファイルを処理できます。

ここに代わるPythonの例だfoobarとはXXXXXX

#! /usr/bin/python
import mmap
import contextlib   
with open('test.file', 'r+') as f:
 with contextlib.closing(mmap.mmap(f.fileno(), 0, access=mmap.ACCESS_WRITE)) as m:
   pos = 0
   pos = m.find('foobar', pos)
   while pos > 0:
    m[pos: pos+len('XXXXXX')] = 'XXXXXX'
    pos = m.find('foobar', pos)

4

これには多くのツールがあります。

ddファイルをブロックしたい場合に使用したいものです-特定のバイトのみを特定の回数だけ確実に読み取ります。ファイルストリームのブロックとブロック解除を移植可能に処理します。

tr -dc '[:graph:]' </dev/urandom | dd bs=32 count=1 cbs=8 conv=unblock,sync 2>/dev/null

###OUTPUT###

UI(#Q5\e BKX2?A:Z RAxGm:qv t!;/v!)N

またtr、ASCIIバイトを他のASCIIバイトに変換する(または、この場合、非スペース印刷可能文字ではないASCIIバイトを削除する)処理ができるため、上記を使用します。今朝、あなたが行った他の質問への答えとして、私が実際に使用したものです。

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

多くの類似したものがあります。そのリストは、あなたがよく知っているかもしれない最も低い共通分母のサブセットを提供するはずです。

しかし、2.5ギガバイトのバイナリファイルでテキスト処理を行う場合は、から始めodます。octal dumpまたは他のいくつかの形式のいずれかを提供できます。あらゆる種類のオプションを指定できますが、\Cエスケープ形式で行ごとに1バイトだけを実行します。

取得するデータは、od指定した間隔で定期的になります-以下に示すように。しかし最初に-ここにあなたの質問に対する答えがあります:

printf 'first\nnewline\ttab spacefoobar\0null' |
od -A n -t c -v -w1 |
sed 's/^ \{1,3\}//;s/\\$/&&/;/ /bd
     /\\[0nt]/!{H;$!d};{:d
    x;s/\n//g}'

上記の少しは、\newlines、\0null、\tabsを区切り、区切り文字のエスケープ文字列<spaces>を保持\Cします。使用される関数Hx機能に注意してください- sed区切り文字に遭遇するたびに、メモリバッファの内容をスワップアウトします。このようにしてsed、ファイルを確実に区切るために必要なだけの情報を保持し、バッファオーバーランに屈しません-しません。つまり、実際にその区切りに遭遇する限り。その限りsed、入力を処理しod続け、に遭遇するまで入力を提供し続けEOFます。

そのままで、出力は次のようになります。

first
\nnewline
\ttab
 spacefoobar
\0null

だから私が望むならfoobar

printf ... | od ... | sed ... | 
sed 's/foobar/\
&\
/g'

###OUTPUT###

first
\nnewline
\ttab
 space
foobar

\0null

Cエスケープを使用したい場合、それは非常に簡単です- sedすでに二重の\\バックスラッシュがその単一の入力バックスラッシュのすべてをエスケープしているので、printfexeced from xargsは仕様への出力を生成するのに問題がありません。ただし、xargs シェル引用符を使用するため、二重引用符を再度使用する必要があります。

printf 'nl\ntab\tspace foobarfoobar\0null' |
PIPELINE |
sed 's/./\\&/g' | 
xargs printf %b | 
cat -A

###OUTPUT###

nl$
tab^Ispace $
foobar$
$
foobar$
^@null%

これは、シェル変数に簡単に保存して、後で同じ方法で出力できます。最後の入力では、入力のすべての文字の前にバックスラッシュがsed挿入さ\れますが、それだけです。

そして、これがすべてsed手に入る前の様子です。

printf 'nl\ntab\tspace foobarfoobar\0null' |
od -A n -t c -v -w1

   n
   l
  \n
   t
   a
   b
  \t
   s
   p
   a
   c
   e

   f
   o
   o
   b
   a
   r
   f
   o
   o
   b
   a
   r
  \0
   n
   u
   l
   l

2

Awkは連続したレコードを操作します。レコード区切り文字として任意の文字を使用できます(多くの実装でのヌルバイトを除く)。一部の実装では、レコード区切り文字として任意の正規表現(空の文字列に一致しない)がサポートされていますが、格納される前にレコード区切り文字が各レコードの末尾から切り捨てられるため、扱いにくい場合が$0あります(GNU awkは変数RTをレコード区切り文字に設定します現在のレコードの末尾から削除されました)。デフォルトでは改行であり、入力レコードセパレータとは独立して設定されprintている出力レコードセパレータORSで出力を終了することに注意してくださいRS

awk -v RS=, 'NR==1 {printf "input up to the first comma: %s\n", $0}'

あなたは効果的に他のツール(のレコードセパレータとして別の文字を選択することができsortsedとその文字で改行を交換することにより、...) tr

tr '\n,' ',\n' |
sed 's/foo/bar/' |
sort |
tr '\n,' ',\n'

多くのGNUテキストユーティリティは、区切り文字として改行ではなくヌルバイトの使用をサポートしています。

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