HSPポータル
サイトマップ お問い合わせ


HSPTV!掲示板


未解決 解決 停止 削除要請

2009
0805
t.m.rbyte数の違う値のバイナリ書き換え16解決


t.m.r

リンク

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


どうすれば良いでしょうか?よろしくお願いします。



この記事に返信する


SYAM

リンク

2009/8/5(Wed) 11:55:33|NO.26753

文字列でしたら、 strmid命令で挿入箇所より前と後の文字列に分割してから、前の文字列+挿入文字列+後の文字列 とでもすればよいでしょう。
スピードが気になるなら(たぶん)より高速な memcpy 命令でもおなじことができるでしょう。



hspail

リンク

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



t.m.r

リンク

2009/8/5(Wed) 12:57:12|NO.26758

SYAMさん、hspailさん回答ありがとうございます。
memcpy命令を使ってプログラムを書き直してみたところ、希望していた処理ができました。

ですが、wpoke命令のみ(上書き)のときだと1秒足らずで出来る処理がmemcpy命令を入れる事によって10秒近くかかってしまいます。

サイズにして255 KBの文字列から1000箇所ほどの書き換えの処理をしています。
バイナリ数値検索→(見つかった箇所をmemcpyで2byte分確保)→wpokeで置き換え

もう少し早く処理するようには出来ないでしょうか?



SYAM

リンク

2009/8/5(Wed) 13:03:17|NO.26760

最初に検索だけを実行して最終的に必要になるバッファサイズを計算するか、または十分すぎるくらいのバッファを用意してこれを出力専用バッファにします。
で、

もとのバッファの置換対象を検索、
もとのバッファから置換対象までを出力バッファの末尾にコピー、
置換後のデータを出力バッファの末尾にコピー、

…を繰り返したらどうでしょう?



t.m.r

リンク

2009/8/5(Wed) 16:49:05|NO.26766

SYAMさん
ありがとうございます。その方法で試してみました。
しかし結果思うように早くなりませんでした。(memcpyで1byteシフトと同じくらい)
文字列の切り貼りをすると、ものすごく時間を食うみたいです。
strmid命令、memcpy+poke命令で試したのですが、最低でも10秒はかかってしまいます。
他にいい案はないでしょうか?



t.m.r

リンク

2009/8/5(Wed) 18:10:02|NO.26773


ごめんなさい、遅さはmemcpyが原因ではなかったみたいです。
memcpy b,buf,i-z-1,strlen(b),z
こういう風に書いてるのですが、出力バッファbの末尾にmemcpyで追加していくために、
strlenでbのサイズを求めてるのですが、それに時間がかかるようです。
もう少しで出来そうなんですが、行き詰ってます・・・。ご教授お願いします。



SYAM

リンク

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




SYAM

リンク

2009/8/5(Wed) 18:25:09|NO.26775

コメント入れ忘れましたね......

inbuf = 置換元の文字列バッファ
outbuf = 置換後の文字列バッファ
src = 置換元キーワード
dest = 置換後のキーワード

です.つまり、
文字列 inbuf 内に含まれる文字列 src を文字列 dest に置換したものを 文字列 outbuf として作ります。



KA

リンク

2009/8/5(Wed) 19:25:10|NO.26776

ちょっと気になったので書き込みます。

操作対象の中身は、”文字列データ”なのでしょうか、”バイナリデータ”
でしょうか?

strlen/strmid は”文字列”を扱うため、データの途中に "00" が有ると、
そこでデータの終わりと判断してしまいます。

私がやるとしたら、先に出ているように変数を2つ用意して、peek poke で
ちまちまコピーして、該当データが有れば変換する、を繰り返します。
”文字列”用の命令等は使いません。



SYAM

リンク

2009/8/5(Wed) 19:44:57|NO.26777

「変数に入っている文字列から」…ってことだったので、
文字列として16進法か何かで表現されたデータが書き込まれているものを想定してました。
…そうでないなら、私のスクリプトは使えませんね。

しもた。先にちゃんとはっきり確認しないといけなかった...orz



t.m.r

リンク

2009/8/5(Wed) 19:50:36|NO.26778

SYAMさん、KAさん
ありがとうございます!!

基本的には、SYAMさんの一番最初の回答の方法で、
時間がかかる文字列命令(strlen,strmid等)を省き手動(?)の数値変数で文字数をカウントして
memcpyを使い出力用の文字列バッファに次々に追加していく方法でうまくいきました。(0.2秒くらいまで早まりました)
長い時間四苦八苦しましたが、実現できてよかったです。どうもありがとうございました。

因みに、NO.26774のSYAMさんのスクリプトでも同じくらいの時間で実行できました。



t.m.r

リンク

2009/8/5(Wed) 19:53:13|NO.26779

>「変数に入っている文字列から」…ってことだったので、
>文字列として16進法か何かで表現されたデータが書き込まれているものを想定してました。

その通りです。その解釈で間違いありません。説明不足でスミマセン…。



SYAM

リンク

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



t.m.r

リンク

2009/8/5(Wed) 21:53:26|NO.26786

SYAMさん
最初の質問の目的は達成したのでもう良いのですが、
使い勝手の良いスクリプトだと思うので、質問させてください。(NO.26781)
バイナリ検索で置き換え前の数値を2byte以上に設定すると検索にかからず0箇所になってしまうのですが、バグでしょうか?
頓珍漢な事言ってたら、すみません…。

例)「89」を「*」に置き換え
src = 0x3938
srclen = 2
dist = 0x9681
distlen = 2



SYAM

リンク

2009/8/5(Wed) 22:08:53|NO.26787

比較してるとこが1バイトしか比較してないからじゃないかなと思います。
この比較の仕方だとsrclen=1でしか動きません。
紛らわしいことしましたごめんなさいorz

もし入力が文字列である(途中に0x00を含まない)なら、instrを使っているスクリプトがそのまま使えるはずで、そちらは置換元の文字列長に制限はありません。



shinkun

リンク

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



ONION software Copyright 1997-2021(c) All rights reserved.