|
|
2009/8/5(Wed) 11:35:47|NO.26752
変数に入っている文字列から、「AA」というバイナリ数値(1byte)を検索して「BB CC」という数値(2byte)に書き換え(置き換え)するプログラムを作りたいです。
例) 63→FF FFに書き換え
12 EF 90 63 03 99
↓実行
12 EF 90 FF FF 03 99
wpoke命令を使ってみたのですが、以下のように(上書きで)書き換わってしまうためうまくいきません。
例) 63→FF FFに書き換え
12 EF 90 63 03 99
↓実行
12 EF 90 FF FF 99
どうすれば良いでしょうか?よろしくお願いします。
|
|
2009/8/5(Wed) 11:55:33|NO.26753
文字列でしたら、 strmid命令で挿入箇所より前と後の文字列に分割してから、前の文字列+挿入文字列+後の文字列 とでもすればよいでしょう。
スピードが気になるなら(たぶん)より高速な memcpy 命令でもおなじことができるでしょう。
|
|
2009/8/5(Wed) 11:55:54|NO.26754
memcpy で 1byte シフトさせてから書き込むのはどうでしょう
// 63→FF FFに書き換え
// バイナリデータ作成
nBin = $12, $EF, $90, $63, $03, $99
sdim sBin, 10
repeat length(nBin) : poke sBin, cnt, nBin(cnt) : loop
// 中身を表示
repeat length(nBin)
print peek(sBin, cnt)
loop
// 書き換え
memcpy sBin, sBin, 2, 5, 4
wpoke sBin, 4, $FFFF
// 中身を表示
repeat length(nBin) + 1
print peek(sBin, cnt)
loop
|
|
2009/8/5(Wed) 12:57:12|NO.26758
SYAMさん、hspailさん回答ありがとうございます。
memcpy命令を使ってプログラムを書き直してみたところ、希望していた処理ができました。
ですが、wpoke命令のみ(上書き)のときだと1秒足らずで出来る処理がmemcpy命令を入れる事によって10秒近くかかってしまいます。
サイズにして255 KBの文字列から1000箇所ほどの書き換えの処理をしています。
バイナリ数値検索→(見つかった箇所をmemcpyで2byte分確保)→wpokeで置き換え
もう少し早く処理するようには出来ないでしょうか?
|
|
2009/8/5(Wed) 13:03:17|NO.26760
最初に検索だけを実行して最終的に必要になるバッファサイズを計算するか、または十分すぎるくらいのバッファを用意してこれを出力専用バッファにします。
で、
もとのバッファの置換対象を検索、
もとのバッファから置換対象までを出力バッファの末尾にコピー、
置換後のデータを出力バッファの末尾にコピー、
…を繰り返したらどうでしょう?
|
|
2009/8/5(Wed) 16:49:05|NO.26766
SYAMさん
ありがとうございます。その方法で試してみました。
しかし結果思うように早くなりませんでした。(memcpyで1byteシフトと同じくらい)
文字列の切り貼りをすると、ものすごく時間を食うみたいです。
strmid命令、memcpy+poke命令で試したのですが、最低でも10秒はかかってしまいます。
他にいい案はないでしょうか?
|
|
2009/8/5(Wed) 18:10:02|NO.26773
↑
ごめんなさい、遅さはmemcpyが原因ではなかったみたいです。
memcpy b,buf,i-z-1,strlen(b),z
こういう風に書いてるのですが、出力バッファbの末尾にmemcpyで追加していくために、
strlenでbのサイズを求めてるのですが、それに時間がかかるようです。
もう少しで出来そうなんですが、行き詰ってます・・・。ご教授お願いします。
|
|
2009/8/5(Wed) 18:12:45|NO.26774
1000個の置換対象を埋め込んだ 500 キロバイトちょっとのデータを置換する例です。
結果は test.txt ファイルに出力します。
置換元の文字列データの生成は文字列操作のみで行っておりこれが素晴らしく遅いですから、置換を始める前にいったん待機します。
これで置換完了までにどのくらいかかるでしょうか?
sdim inbuf, 1024000
sdim outbuf,2048000
sdim src,32
sdim dist,32
src = "3210"
dist = "FFFF"
distlen = strlen(dist)
mes "テストデータを作っています."
repeat 1000
repeat 32
inbuf += "0123456789ABCDEF\n" ; 1M強.
loop
inbuf += "3210\n"
loop
mes "テストデータは " + strlen(inbuf) + " バイトになりました."
dialog "このダイアログを閉じると置換を始めます.",0
mes "置換中です."
inptr = 0
outptr= 0
counter=0
*SEARCH
idx = instr(inbuf, inptr, src)
if (idx<0):goto *LAST
counter ++
memcpy outbuf, inbuf, idx, outptr, inptr
outptr+=idx
memcpy outbuf, dist, distlen, outptr, 0
outptr+=distlen
inptr+=idx
inptr+=strlen(src)
goto *SEARCH
*LAST
memcpy outbuf, inbuf, (strlen(inbuf)-inptr), outptr, inptr
outptr+=(strlen(inbuf)-inptr)
poke outbuf, outptr, 0
mes ""+counter+" 箇所 置換終了. "
dialog "テスト終了.\n test.txt の内容を確認してください.",0
notesel outbuf
notesave "test.txt"
end
|
|
2009/8/5(Wed) 18:25:09|NO.26775
コメント入れ忘れましたね......
inbuf = 置換元の文字列バッファ
outbuf = 置換後の文字列バッファ
src = 置換元キーワード
dest = 置換後のキーワード
です.つまり、
文字列 inbuf 内に含まれる文字列 src を文字列 dest に置換したものを 文字列 outbuf として作ります。
|
|
2009/8/5(Wed) 19:25:10|NO.26776
ちょっと気になったので書き込みます。
操作対象の中身は、”文字列データ”なのでしょうか、”バイナリデータ”
でしょうか?
strlen/strmid は”文字列”を扱うため、データの途中に "00" が有ると、
そこでデータの終わりと判断してしまいます。
私がやるとしたら、先に出ているように変数を2つ用意して、peek poke で
ちまちまコピーして、該当データが有れば変換する、を繰り返します。
”文字列”用の命令等は使いません。
|
|
2009/8/5(Wed) 19:44:57|NO.26777
「変数に入っている文字列から」…ってことだったので、
文字列として16進法か何かで表現されたデータが書き込まれているものを想定してました。
…そうでないなら、私のスクリプトは使えませんね。
しもた。先にちゃんとはっきり確認しないといけなかった...orz
|
|
2009/8/5(Wed) 19:50:36|NO.26778
SYAMさん、KAさん
ありがとうございます!!
基本的には、SYAMさんの一番最初の回答の方法で、
時間がかかる文字列命令(strlen,strmid等)を省き手動(?)の数値変数で文字数をカウントして
memcpyを使い出力用の文字列バッファに次々に追加していく方法でうまくいきました。(0.2秒くらいまで早まりました)
長い時間四苦八苦しましたが、実現できてよかったです。どうもありがとうございました。
因みに、NO.26774のSYAMさんのスクリプトでも同じくらいの時間で実行できました。
|
|
2009/8/5(Wed) 19:53:13|NO.26779
>「変数に入っている文字列から」…ってことだったので、
>文字列として16進法か何かで表現されたデータが書き込まれているものを想定してました。
その通りです。その解釈で間違いありません。説明不足でスミマセン…。
|
|
2009/8/5(Wed) 20:14:20|NO.26781
バイナリで検索する版に直してる間に回答ありましたね...
sdim inbuf, 1024000
sdim outbuf,2048000
;sdim src,32
;sdim dist,32
src = '*'
srclen = 1
dist = 0x9681
distlen = 2
mes "テストデータを作っています."
repeat 100
repeat 32
inbuf += "0123456789ABCDEF\n" ; 1M強.
loop
inbuf += "*\n"
loop
inbuflen = strlen(inbuf)
mes "テストデータは " + inbuflen + " バイトになりました."
dialog "このダイアログを閉じると置換を始めます.",0
mes "置換中です."
inptr = 0
outptr= 0
counter=0
*SEARCH
idx = 0
repeat
c = peek(inbuf,inptr+idx)
if(src=c):break
idx++
if (idx>=inbuflen):goto *LAST
loop
counter ++
memcpy outbuf, inbuf, idx, outptr, inptr
outptr+=idx
memcpy outbuf, dist, distlen, outptr, 0
outptr+=distlen
inptr+=idx
inptr+=srclen
goto *SEARCH
*LAST
memcpy outbuf, inbuf, (inbuflen-inptr), outptr, inptr
outptr+=(inbuflen-inptr)
poke outbuf, outptr, 0
mes ""+counter+" 箇所 置換終了. "
dialog "テスト終了.\n test.txt の内容を確認してください.",0
notesel outbuf
notesave "test.txt"
end
せっかくなので載せておきます。
テストデータは文字列なのでstrlenが残ってますが、実際には入力データ長が inbuflen に代入されていれば検索&置換自体はそのまま使えます。
途中に0x00が出てきても止まりません。
が。
元が「文字列」だってことが最初の質問文ではハッキリ書いてあるから、データの途中に 0x00 は現れませんね......orz
| |
|
2009/8/5(Wed) 21:53:26|NO.26786
SYAMさん
最初の質問の目的は達成したのでもう良いのですが、
使い勝手の良いスクリプトだと思うので、質問させてください。(NO.26781)
バイナリ検索で置き換え前の数値を2byte以上に設定すると検索にかからず0箇所になってしまうのですが、バグでしょうか?
頓珍漢な事言ってたら、すみません…。
例)「89」を「*」に置き換え
src = 0x3938
srclen = 2
dist = 0x9681
distlen = 2
|
|
2009/8/5(Wed) 22:08:53|NO.26787
比較してるとこが1バイトしか比較してないからじゃないかなと思います。
この比較の仕方だとsrclen=1でしか動きません。
紛らわしいことしましたごめんなさいorz
もし入力が文字列である(途中に0x00を含まない)なら、instrを使っているスクリプトがそのまま使えるはずで、そちらは置換元の文字列長に制限はありません。
|
|
2009/8/5(Wed) 22:42:52|NO.26791
SYAM さんからの返答でなくてスミマセン。
SYAM さんのスクリプトをバグと言うのは早計かと思います。
変数 srclen があるため、数 byte での検索が可能かのように見えるのは確かですが、
きちんとコードを読めば、元々 1 byte でしか検索できない事が読み取れるはずです。
<< 中略 >>
repeat
c = peek(inbuf,inptr+idx) ; 1 byte 読み取って
if(src=c):break ; その 1 byte と src を比較!!
idx++
if (idx>=inbuflen):goto *LAST
loop
<< 中略 >>
所望の動作をするスクリプトを提示してもらえたのだから、
それがどういう仕組みで、どういう処理で実現されているのか
読む位の事はしましょう。
読めば、何故 2 byte での検索が出来ないか、その理由が分かるはずです。
仮に分からなかったとしても、コードを読んで理解しようと努力する事は、
スクリプトを書いてくれた人に対する礼儀だと思いますよ。
…口やかましく言うだけで終わるのも何なので、とりあえず 4 byte 長まで
検索可能なスクリプトを提示しておきます。
上で示した部分を下のコードに置き換えてください。
repeat
c = 0
repeat srclen
c |= peek(inbuf,inptr+idx+cnt) << (8 * cnt)
loop
if(src=c):break
idx++
if (idx>=inbuflen):goto *LAST
loop
|
|