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


HSPTV!掲示板


未解決 解決 停止 削除要請

2021
0820
ペス文字列のメモリ確保について解説求む!2解決


ペス

リンク

2021/8/20(Fri) 14:30:25|NO.93637

今まで避けてきた、文字列操作やメモリ操作などを理解しなくてはならない状況になってしましました。
文字列バッファの考え方や処理方法などを学ぶにあたり、公式のドキュメントやstrlen, strmid関数の使い方を学ぶにあたり
とあるサイトさんにお世話になりましたが、

このページで公開されている自作関数『strlen_, strmid_』では、strlen, strmidと違い、全角文字も1文字としてカウントするように改造しているようです。

その中で定義してるstrmid_の中の最後に、

memcpy buf, buf, l * 2, 0, i * 2 wpoke buf, l * 2, 0x0000
という部分があります。

wpokeは、バッファの加工が終わった後に最後の最後に終端文字を書き込んで終わりという意味なのはわかるんですが、
その直前のmemcpyで何やってるのかが理解できなくて悩んでます。

恥ずかしながら、個人的な現状の理解度では、『ここまでの処理で、バッファにはもう既に必要な文字列が全て格納されているはずだよね?memcpyって必要あるの?』と思ってしまいます。
これをやらないと、どんな困った事が起こるのでしょうか?
頭が悪い自分にも分かるように教えて頂けると嬉しいです。


URLは記載できないみたいなので、無断転載ですが、以下が問題のスクリプトになります。



/** * メイン */ // 文字列の長さ mes strlen_("aiueo") mes strlen_("あいうえお") mes strlen_("aいuえo") mes // 文字列の一部を取り出す mes strmid_("aいuえo", 2, 2) mes mes strmid_("aいuえo", -1, 1) mes strmid_("aいuえo", -2, 2) mes strmid_("aいuえo", -3, 1) mes mes strmid_("aいuえo", 0, -1) mes strmid_("aいuえo", 2, -1) mes strmid_("aいuえo", 3, -1) mes strmid_("aいuえo", -3, -1) mes stop /** * 文字列の長さを調べる (日本語等も一文字) */ #defcfunc strlen_ str s l = 0 sdim buf, strlen(s) * 2 + 2 cnvstow buf, s repeat if ( 0x0000 == wpeek(buf, cnt * 2) ) : break l++ loop return l /** * 文字列の一部を取り出す (日本語等も一文字) * * index がマイナスのときの挙動変更 (後ろからのインデックス) * len がマイナスのときの挙動変更 (後ろからのインデックス) */ #defcfunc strmid_ str s, int index, int len sdim buf, strlen(s) * 2 + 2 cnvstow buf, s // インデックスを修正 if ( index >= 0 ) { i = 0 repeat index if ( 0x0000 == wpeek(buf, cnt * 2) ) : break i++ loop } else { i = 0 repeat if ( 0x0000 == wpeek(buf, cnt * 2) ) : break i++ loop i = limit(i + index, 0, i) } // 長さを修正 if ( len >= 0 ) { l = 0 repeat len, i if ( 0x0000 == wpeek(buf, cnt * 2) ) : break l++ loop } else { l = 0 repeat , i if ( 0x0000 == wpeek(buf, cnt * 2) ) : break l++ loop l = limit(l + len, 0, l) } // 切り出し memcpy buf, buf, l * 2, 0, i * 2 wpoke buf, l * 2, 0x0000 return cnvwtos(buf)



この記事に返信する


沢渡

リンク

2021/8/20(Fri) 16:15:43|NO.93640

これはcnvstowを使って文字コードをUTF-16(Unicodeの一種)に
変換していることがポイントです。
UTF-16の仕様は、普段HSPが使用しているShift_JIS
(公式のドキュメントで解説されている文字コード)とはだいぶ異なり、

・常にデータは2バイト単位で扱い、全角・半角に関係なく「1文字=2バイト」である。 ・終端ヌルも2バイトの0x0000である。 ・ただし、一部の文字(Unicodeの番号が0x10000以上の文字)については  2文字分(都合4バイト)のデータを組み合わせて1文字を構成する。
という特徴があります。

これを踏まえて問題の部分を見ますと、
この時点でlには「元の文字列から抜き出したい文字数」、
iには「左から何文字目から抜き出すか」が入っています。
文字数に2をかければバイト数になりますから、したがって、

memcpy buf, buf, l * 2, 0, i * 2 wpoke buf, l * 2, 0x0000
これは、「bufに入っているUTF-16形式の文字列の中から
『i文字目から始まってl文字分の文字列』を抜き出し、
それをbufの冒頭にコピーする」
「その後、コピーした文字列の後に終端ヌルをくっつけて、
新しい文字列を完成させる」
という処理になります。

そして最後に、UTF-16の文字列をcnvwtosでShift_JISに変換し、
それをreturnで返すという流れになっています。



ペス

リンク

2021/8/20(Fri) 16:38:12|NO.93641

お早い返信ありがとうございます!
沢渡さんは、いつも丁寧で優しく教えているみたいなので、沢渡さんにお答えしていただけて嬉しいです^_^

返信していただいた説明を読んで、自分が勘違いをしていた事に気付けました。
サンプルの機能は、元文字列から指定部分『だけ』を抜き出す為に、位置と長さを修正してるんですよね。
ということは、bufにmemcpyする意味って、元文字列を『該当文字列だけ』にする必要があるからやってる、一番肝の部分じゃないですか。

bufにもう文字列入ってるから、やらなくても変わんないんじゃない?って思ってたんですが、勘違いでした。
おかげさまでmemcpyの使い方を初めて理解できました。
加えて、終端文字列を初めて意識できました。

ありがとうございました。



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