[Top Page][Edit][Discussion][Edit History][All Pages][Recent Changes][->Japanese]

HSP2.6:hsp2str


HSP ver2文字列のひみつ(TIPS)

はじめに

HSPでは、数値型、文字列型の2つの種類を変数の内容として保持することができます。 このドキュメントでは、その中でも文字列型がどのようにHSPで処理されているかを説明しながら、 それぞれの命令の詳細と、その応用についての説明をしています。文字列の取り扱いを理解することで、 より細かく文字列の操作を行なうことができるはずです。また、 メモリ管理やファイルの扱いに関係する部分もあるので、知っておくときっと役に立つ時が来るでしょう。

文字列の基本

まず、HSPでの文字列の取り扱いの基本をおさらいしておきましょう。 文字列とは、"(ダブルクォーテーション)で囲まれた、文字の集合体です。 HSPでは、メッセージの表示や、ファイル名などあらゆる場面で文字列を使用します。たとえば、

print "TEST MESSAGE"

という命令では、「"」で囲まれた「TEST MESSAGE」という文字列を画面に表示します。 また、変数に文字列を記憶させておくこともできます。たとえば、「a="TEST MESSAGE"」のように書けば、 変数aに「TEST MESSAGE」という文字列が記憶されます。そうすれば、 「print a」のように文字列を指定するかわりに変数名を指定して、 変数に記憶されている文字列をパラメータとして使うことができるようになります。 以上は基本ですが、この応用として文字列に式を加えることができるようになっています。 たとえば、

a="TEST"+" MESSAGE"

上の例では、「TEST」という文字列と、「 MESSAGE」という文字列を「+」で足し算しています。 数値の計算ならば、加算されるところですが、文字列を足し算した場合は、文字列が連結されます。 つまり、2つの文字列をつなげて「TEST MESSAGE」という文字列になるのです。文字列で使える式は、 足し算のみですが「a="ABC"+"DEF"+"GHI"」のようにいくつも繋げることができますし、 「a="ABC"+b+"DEF"+c」のように間に変数をはさむことも可能です。ですから、

a="TEST"
b=" AND "
c="MESSAGE"
print a+b+c

のようにすると、3つの変数の内容が連結されて「TEST AND MESSAGE」という文字列が画面に表示されます。 もしこれが、文字列型の変数でなく、数値が記憶されている数値型変数を途中で足し算した場合には、 どうなるのでしょうか? HSPでは、次のようなお約束があります。

  • 「文字列型」に「数値型」を足し算した場合には、「数値」を文字列にして連結される。
  • 「数値型」に「文字列型」を足し算した場合には、「文字列」を数値にして加算される。

つまり、計算式で最初の項になっている型に自動的に合わせてくれるということです。ですから、

a="TEST "
b=12345
c=" MESSAGE"
print a+b+c

のように数値型の変数が間にあっても、「TEST 12345 MESSAGE」という表示になり、これはあくまで文字列として扱われます。

文字列の限界

文字列には限界があります。どのような限界かと言うと、文字の長さ、 つまり文字数に制限があるということです。まず気をつけておかなければいけないのは、 変数に文字列を記憶される場合は、通常63文字までが限界ということです。

どうしてそんな意地悪をするのか疑問に感じるかもしれませんが、 快適な速度でHSPのスクリプトが実行できるための策だと思ってください。もちろん、 文字数の制限なく文字列を記憶できるといいのですが、コンピュータのメモリは無限にはないので、 すべての変数にたくさんの文字列を記憶させておくためのメモリを与えてしまうと、 あっという間にメモリが使われてしまうばかりか、ムダが多くなってしまいます。

ま、ここは苦しい土地行政みたいなものと思って63文字というのを覚えておいてください。 通常は63文字でこと足りることが多いと思いますが、中には長い文字列を使いたいこともあるでしょう。 そんな時は、sdim命令を使います。これは、たとえば、

sdim a,256

のように使うと、変数aが256文字(正確には255文字)まで使えるようになります。 その気になれば、「sdim a,5000」(5千文字)とか、 「sdim a,20000」(2万文字)とかいくらでも大きな文字数を取ることができます。

このように、あらかじめ長くなりそうな文字列型変数は、 sdim命令でサイズを宣言しておくと、 ちゃんと使えるようになります。なぜ使えるようになる文字数が、設定したサイズより1文字少ないのかは、 ここでは謎ということにしておいてください。いずれその謎が解けます。

さて、もう1つ文字列の限界があります。それは、 32000文字を越える文字列は、 一度には扱えないという制限です。これは、命令のパラメータや変数などすべてにおいて文字列が扱われる場合には、 32000文字を越えてはならないということになります。これは結構苦しそうに見えます。

しかし、この「一度には」扱えないというところがポイントです。このドキュメントの先の方で詳しく扱いますが、 長い文字列というのはテキストファイルや、メッセージデータなど改行を含む、 複数の行に渡っている文字列がほとんどです。 HSPでは、 複数行を扱うためにメモリノートパッド命令セットというのを備えています。これを使うことにより、 たとえ全体で32000文字を越える文字列であっても、任意の行データだけを取り出して、 1行だけの文字列として扱えば32000文字を越えることはありません。このように文字列には、 限界がありますが、それを知った上で上手にスクリプトを書けば決して足を引っ張るほどの制限にはならないはずです。

最後に、ちょっとした裏ワザをご紹介しましょう。上で、 扱える文字列の限界は32000文字と書いていましたが、この限界を突破できる命令が存在します。それは…、

a+="TEST"

のような直接文字列を足し算する代入です。これは、「a=a+"TEST"」と同じことですが、 HSPの内部では「a+="TEST"」の方が処理が軽く、しかも32000文字という制限がありません。 もちろん、「"TEST"」の部分が32000文字を越えてはいけないのですが、文字列を連結した結果が、 32000文字を越えても正常に動作します。

メモリノートパッド命令を使って、32000文字を越えた文字列も作成できますが、 もっと単純な方法として、文字列の最後に文字を連結させる場合には、「a+="文字列"」という記述も選択の1つになるでしょう。

文字列とは何か?

HSPでは文字列をどのように管理しているのでしょうか? 前にも言ったように、 文字列は長さ不定の文字の集合体です。文字列型変数では、決まった文字数(通常は63文字)のぶんだけ記憶できるように、 メモリに場所を取ってあります。ここで、「文字列」と「メモリ」、「バッファ」の関係を知っておくと、 文字列の扱いがよりスッキリとします。

HSPでは、文字列の1文字1文字を「コード」という数値で扱って管理しています。これは、 どんなコンピュータでも内部では数値で管理されているためです。下の表を見てみてください。

コード(10進数) コード(16進数) 0 1 2 3 4 5 6 7 8 9 a b c d e f
32 $20 ! " # $ % & ' ( ) * + , - . /
48 $30 0 1 2 3 4 5 6 7 8 9 : ; < = > ?
64 $40 @ A B C D E F G H I J K L M N O
80 $50 P Q R S T U V W X Y Z [ \ ] ^ _
96 $60 ` a b c d e f g h i j k l m n o
112 $70 p q r s t u v w x y z { | } ~

これは、「アスキーコード表」と呼ばれているものです。どんな文字にも、 それに対応する数値(コード)があります。その中でも標準的に使われている、 半角のアルファベットや記号だけを抜き出したものをアスキーコード ( ASCII : American Standard Code for Information Interchange ) といいます。

英語ではこれで十分なのですが、日本語ではこの他に全角の文字が多数あり、 それぞれにやはりコードがあります。これは何も難しい概念ではなくて、 単純に「A」という文字はコード65(10進数)、「$」は 36(10進数)というように数字に直すことができるよ、 というだけの話です。この表の見かたは、たとえば「@」であれば左の「コード」にある数値の通り「64」になり、 以後右にずれるたびに1づつふえていきます。つまり、「A」は65、「B」は66…というぐあいです。 「コード」のところに10進数と、16進数が書いてありますが、 コンピュータの世界では16進数だと区切りがいいということで、HSPでは10進数でもかまいませんし、 16進数でももちろんOKです。 (HSPで16進数は「$48」など$を頭につけて表記します) ですから、 「N」は10進数では78、16進数では$6eになります。混乱すると困るので、ここでは特に表記がない限り、 コードは10進数で扱っていきます。

ちなみに、スペースキーを押して出てくるスペースは、コード32になります。 他にスペースになっている場所もありますが、通常は使われません。 さて、文字列は、このコードの集まりだと考えることができます。コードは、0から255までの数値になります。 つまり256種類の文字を表わすことができるというわけです。しかし、日本語の漢字などを含めると膨大な量になり、 とても256種類では足りません。そこで、 日本語はコードを2つ使用して256種類×256種類=65536種類(そんなにないけど)ぶんのコードを使うことにしています。 日本語の文字が全角と呼ばれ、英文字が半角と呼ばれるのは、 英文字(アスキーコード)が日本語文字に対して半分サイズのコードだからなのです。 このように、「文字を表わすコードが、いくつか並んでいるものが文字列」なのだということをここでは覚えておいてください。 これが何の役に立つか、もうすぐわかるはずですよ。

文字列とバッファ

文字は、0〜255までの数値で表わされるコードにすることができることがわかりました。 この、256種類という数字はコンピュータの世界ではよく出てくる1バイト(byte)という単位になります。 バイトとは、数値をコンピュータのメモリに記憶させる際の基本となる単位で、 1バイトには、0〜255までの数値を記憶させておくことができます。1メガバイトのメモリは、 バイトに直すと、ざっと1,000,000バイトですからごくごく小さな単位です。 こんなハード寄りの話を出したのは、難しくしたいためじゃありません。 この1バイト、0〜255までの数値というところでHSPの、 poke命令およびpeek命令のことを思い出してもらいたかったからです。 poke命令、peek命令はメモリバッファの読み書きをするための命令で、 ちょっと文字列とは無縁な感じがします。しかしながら、 変数の内容が記憶されている場所もまた、 メモリバッファの1つ なのです。これは知っておくと役に立つ重要な概念です。 文字列型変数に記憶されている文字列の内容や、数値型の変数、そして配列までもがpoke命令、 peek命令で読み書き可能であるということです。同様に、 メモリバッファを対象にした命令(bload命令やbsave命令)もすべて通常の変数に対して使用できます。 ここでためしに、

a="*"
peek b,a,0
mes "b="+b

というスクリプトを実行してみてください。変数aに代入された「*」という文字列が記憶されているバッファから、 1バイトを取り出して表示していますが、「*」の文字コードである42という数値が取り出されています。 このように、poke命令、peek命令を使うことで、記憶されている文字列に対してより細かなアクセスができることになります。 これからいよいよ、その細かなアクセスの詳細について説明していきましょう。

文字列のしくみ

文字列が記憶されているバッファには一定のルールがあります。まずは、この重要なルールをまず最初に覚えておいてください。

  1. 1バイトにつき1文字のコードが連続して置かれている。
  2. 文字列の一番最後は、終わりを示すコードとして0が置かれている。

この2つのルールさえわかってしまえば、あとはこれに従ってバッファをどういじっても問題ありません。 まず簡単な例として次のようなスクリプトがあったとします。

a="ABCDE"

これが実行されると、変数aの内容が記憶されているバッファは次のようになります。

A B C D E 終了コード
65 66 67 68 69 0

文字列「ABCDE」は、バッファの中では「65」「66」「67」「68」「69」「0」という数値になって記憶されることになります。 peek命令は、バッファの中の好きな場所から1バイト、データを取り出すことのできる命令です。ヘルプによれば、

peek p1,p2,p3                   バッファから1byte読み出し

p1=変数 : 内容を読み出す先の変数名 
p2=変数 : バッファを割り当てた変数名 
p3=0〜 : バッファのインデックス(Byte単位) 

ということなので、

a="ABCDE"
peek b,a,2

とすれば、変数aの3文字目にある文字のコードを読み出すことができます。 なぜ3文字目になるかと言うと、peek命令の3番目のパラメータは、 0から始まって1バイトごとの指定なので、2を指定した場合は0・1・2の順で3文字目にあたるコードを取り出しているためなのです。 poke命令は逆にバッファにデータを書き込みための命令です。ヘルプによれば、

poke p1,p2,p3                   バッファに1byte書き込み

p1=変数 : バッファを割り当てた変数名 
p2=0〜 : バッファのインデックス(Byte単位) 
p3=0〜255 : バッファに書き込む値 または 文字列(Byte単位) 

ということなので、たとえば、

a=""
poke a,0,65
poke a,1,66
poke a,2,67
poke a,3,0
mes a

とすれば、変数aには何も文字列を代入していないにもかかわらず、 バッファに直接データを書き込んだために、「ABC」という文字列が記憶されています。 忘れてはいけないのは、最後に終わりを示すコードとして0を入れておくことです。 HSPは、0が出てくるまでバッファの内容を文字列として解釈します。 もし、0を忘れてしまうと余計なデータを文字列に加えてしまったり、 最悪の場合、一般保護エラーが出るまでメモリを読み続けてしまいます。

この終わりを示すコードを逆手に取って次のようなこともできます。

a="ABCDEF"
poke a,3,0
mes a

このスクリプトでは、変数aに「ABCDEF」という文字列を代入していますが、 その後でpoke命令により、途中に終了コードを書き込まれています。その結果、 「ABC」の後に終了コードがあるためにそこで文字列は終了と判断され、「ABC」という文字列だけしか表示されません。 このように、文字列のしくみを理解することで、文字列の各文字に対してのアクセスが容易になります。 ここで、前に残してあった謎が解ける時が来ました。sdim命令で文字列のバッファを宣言した時に、 なぜ使えるようになる文字数が、設定したサイズより1文字少ないのかという謎…。 それはつまり、256バイトのバッファを確保したとして、255文字までしか使えないのは、 最後に終了コード0を入れる場所が必要だからなのです。「sdim a,256」は、 255文字+終了コードまでは領域を確保するという意味になるわけです。これで、謎が解けてスッキリできたでしょうか?

文字列の一部を文字コードにして取り出したり、 また逆に文字コードを文字列にするようなことは何かの時にあると便利です。 よく使うテンプレートとして次の2つをあげておきます。

例1: (文字コードを取り出す)

peek b,a,0  ; 変数aの最初の文字コードを変数bに代入

例2: (文字コードを文字列にする)

a="_"
b=65
poke a,0,b  ; 文字列型変数aに、変数bの文字コードを入れる

例2では、変数bに代入されている数値をpoke命令で文字コードとして書き込んでいます。 これは、変数aの1文字目に上書きされます。変数aは、最初に「_」という1文字だけが代入されていますが、 この「_」はなくなり、新しい文字コードに書き換わります。文字列の最後にある終わりのコード(0)は、 最初の「a="_"」ですでに設定されているので、もう一度書き込む必要はありません。 このような手順で、文字コードを1文字の文字列型変数に変換することができます。 これらの例は、MSXやPC98のMicrosoft系BASICを使った人であれば、 ASC、CHR$といった関数と同じような働きと言えば分かりやすいかもしれません。 文字コードを扱うことは、通常あまりないかもしれませんが、いざという時に小回りのきく手段として覚えておくといいでしょう。

もう1つ便利な例をご紹介しましょう。 peek命令を使えば、 文字列の中から任意の場所にある1文字をコードとして取ってくることができます。 しかし、取り出すのはあくまでコード(数値)でありいちいちアスキーコード表とてらし合わせるのは面倒です。 そんな時に使うと便利なのが「'(シングルクォーテーション)」による表記です。 これは、「'A'」のように1文字を「'」で囲んで使用します。こうすると、 「'A'」は「65」と同じ意味になります。つまり、「'」で囲んだ文字をコードに直してくれるのです。ですから、

a="***TEST"
peek b,a,0
if b='*' : mes "*がありました"

とすれば、変数aの1文字目が「*」のコードかどうかを手軽に調べることができます。 「'」は、文字列を扱う「"(ダブルクォーテーション)」に似ていますが、 実際は数値を示すので間違えないようにしてください。

ここで説明した内容を最後に、おさらいしておくと、

  1. peek命令で文字列の内容をコードとして取り出すことができる。
  2. poke命令で文字列の一部を指定したコードに書き替えることができる。

ということになります。この概念と、文字コードの存在を知っておくだけでもいいと思います。

テキストファイルのしくみ

メモ帳やテキストエディタなどで開いたり保存したりしているテキストファイルですが、 HSPでもこのテキストファイルを読み込んだり、作成することができるようになっています。 テキストファイルとは何なのでしょうか? 実は、テキストファイルとは文字列の内容をそのままファイルにしただけのものなのです。 いままで説明してきたように、変数に記憶された文字列はメモリ上のバッファに、 コードという形で記憶されています。この内容をそのままbsave命令でファイルにセーブすると、 テキストファイルができあがります。逆に言えば、テキストファイルの内容は単なる文字列にほかならないのです。 たとえば、

a="TEXT FILE."
bsave "test.txt",a

のようなスクリプトでも「TEXT FILE.」という内容のテキストファイルを作成できます。 しかし、これだけだとファイルサイズがバッファのサイズと同じになってしまってムダができてしまいます。 文字列として使用している分だけをファイルにセーブするようにすれば完璧です。そこで、

a="TEXT FILE."
strlen size,a
bsave "test.txt",a,size

上のように、strlen命令を使って文字列の長さを調べてから、 そのサイズだけセーブするようにすれば適切な長さのテキストファイルになります。 同じ要領で、テキストファイルの読み込みもbload命令で行なうことができます。たとえば、

sdim a,32000
bload "test.txt",a
mes a

このようなスクリプトになります。最初のsdim命令は、 読み込んでくるテキストファイルが大きいかもしれないので約32000文字分のバッファを確保しています。 これで、32000バイトのファイルまでは読み込むことが可能です。bload命令でテキストファイルを読み込んで、 最後にmes命令でその内容を表示しています。 sdim命令で、文字列型の変数としてバッファを初期化しているので、 mes命令ではバッファの内容を文字列として単純に表示します。 バッファに読み込まれたテキストファイルもまた、文字列と同じなので、 そのままちゃんと表示されるというわけです。

テキストファイルは、文字列型変数のバッファをファイルにしたものと言ってきましたが、実は1つだけ異なる部分があります。それは、

  • 最後に文字列の終わりを示すコード0が入っていない。

という点です。これは、ファイルにセーブされたサイズが、すなわち文字列のサイズということなので、 終わりを示すコードが必要ないためです。 (ただし、一部のテキストエディタではわかりやすいようにテキストファイルにも終了コードを入れてくれるものがあります。 しかし、これは[EOF]と呼ばれるコードで、0ではなく、 26になります) HSPでテキストファイルを読み込んできた場合には、最後に0を入れる必要があるのに、 そのまま使えています。これには、深ーいわけがあります。そのわけは、sdim命令やalloc命令など、 変数のバッファが確保された直後は、その内容がすべて 0でクリアされているのです。 このバッファに、終了コードの入っていないテキストファイルを読み込んでも、 読み込まれたデータの続きには、必ず終了コードである0が入っているというわけです。 そんなに深いわけでもなかったですね。ですから、一度テキストファイルを読み込んできたバッファに、 もう一度別なテキストファイルを読み込んだ場合には、以前のテキストファイルよりも小さなサイズだった場合には、 以前のデータが残ってしまいます。そうならないためには、 テキストファイルを読み込む前に必ずsdim命令やalloc命令などでバッファを確保するようにして、0でクリアしてください。

複数行文字列のしくみ

HSPが扱う文字列は大きく分けて、1行だけの単純な文字列と、複数行を含む文字列とに分けることができます。 この2つは、特に処理の上では違いがありません。 単に、スクリプトを作るユーザーが意識しておけばいいだけのものです。複数行文字列とは、 改行が含まれている文字列のことです。改行とは、それ以降を次の行にもっていくための、しるしみたいなものです。 ためしに、

a="ABC\nDEF"
mes a

というスクリプトを実行してみると、

ABC
DEF

という2行にまたがって、メッセージが表示されます。 これは、変数aの中に「\n」という改行のしるしが含まれているためです。 いままで、文字にはコードがあり数値の形で、バッファに記憶されているということを言ってきましたが、 改行はどうなっているのでしょうか。上の例にあげたスクリプトの場合、変数aの内容はバッファの中では、

A B C \n D E F 終了コード
65 66 67 13 10 68 69 70 0

というデータが入っています。「\n」が改行を示すものですが、 その部分は「13」「10」という2つのコードになっています。そして、 これがまさに「改行コード」なのです。改行コードは、キーボードから入力できないので、 HSPではかわりに「\n」という文字で表わすようにしていますが、 実際は「13」と「10」の2つのコードが文字列の中に置かれています。 困ったことに、改行コードにはいくつかの方言があります。MS-DOSやWindowsでは「13」「10」というコードですが、 MacintoshのMacOSでは「13」のみです。また、UNIXでは「10」になっています。 HSPでは、改行コードとして、MS-DOSやWindowsの「13」「10」の他にも、 MacOSの「13」だけの場合にも対応していますが、UNIXの改行コードには対応していません。 そもそも多くのUNIXでは日本語のコードも違っているので、 DOS形式へのコンバーターなどであらかじめテキスト全体を変換しておいた方がいいでしょう。

メモリノートパッド命令のしくみ

改行コードが入ることにより、文字列は非常に便利になりますが、複雑さは増します。 テキストファイルには、当然改行コードが含まれてたくさんの行があるでしょうし、 HSPのmesbox命令による入力ボックスも、改行を入れることができます。 また、dirlist命令や、listbox命令、combox命令で使用する文字列にも改行コードが入っています。 改行コードを入れることにより、たくさんの情報を整理することができる反面、 文字列としての取り扱いはどんどんやりにくくなっていきます。 そこでHSPでは、メモリノートパッド命令という一連の命令セットにより 改行コードを含んだ文字列を比較的簡単に扱えるようにしています。 複数の行があるテキストを扱う場合に問題となるのは、1行ごとに何かをチェックしたい場合や、 1行単位で文字列を操作したくなった場合です。この1行単位での仕事では、 2つのプロセスに分けると作業がすっきりとします。つまり、

  1. 複数行から任意の行の内容だけを取り出す
  2. 取り出した文字列に対してチェックをしたり、加工をする
  3. 必要ならば、それをもとの行に戻す

このように、複数行の文字列データから、1行だけの改行コードを含まない文字列を取り出してから作業をして、結果をふたたび戻すという手順であれば、改行コードについての面倒なチェックも必要なくなり効率もよくなります。そのための命令セットがメモリノートパッド命令です。これは、以下のような命令から成っています。

命令 おもな機能 備考
notesel メモリノートパッドとして扱う変数の指定
notemax 全体の行数を取得
noteadd 指定行に内容追加 挿入/上書きモードあり
noteget 指定行の内容読み出し
notedel 指定行の削除

命令がいくつもありますが、使い方は難しくありません。 基本的には、最初に対象となる文字列を記憶している変数名をnotesel命令で指定します。 それから、もし1行読み出す場合にはnoteget命令を、行の内容を変更・追加する場合には、 noteadd命令を使います。たとえば、複数行ある文字列を含む変数aで、最初の行にある文字列を取り出したい場合は、

a="ABC\nDEF"
notesel a
noteget b,0
mes b

のようになります。この時、注意しなければならないのは、 noteget命令で指定する行インデックスは0から始まるので、1行目の指定が0になるということです。 そして細かいことですが、noteget命令は読み出す先の変数を必ず文字列型にセットします。 上の例で言うと、変数bがそれまで数値型であってもnoteget命令を実行した後は、文字列型になるということです。 notemax命令とrepeat〜loop命令を使うことで、すべての行に対しての処理を効率的に記述できます。たとえば、

a="ABC\nDEF\GHI"
notesel a
notemax m
repeat m
noteget b,cnt
mes "["+b+"]"
loop

このスクリプトでは、変数aのすべての行の内容をカッコでくくった状態で表示させます。 notemax命令は、行がいくつあるかを調べて変数に返す命令です。ここで調べた回数だけ、 repeat〜loop命令でループさせます。このループ中は、システム変数cntが0から順番に数字が上がっていくので、 noteget命令で読み出す行インデックスの指定に、システム変数cntを使えば各行を順番に取り出していけることになります。

新規文字列操作命令もお目見え

HSP ver2.4dからは、新しく文字列の検索をするためのinstr命令と、 文字列の一部を取り出すためのstrmid命令が追加されました。このドキュメントを読むと、 同様の処理を peek命令やpoke命令でも実現できることがわかると思いますが、 そこは当然最初から機能がサポートされていた方がいいに決まってますよね。 この2つの新しい命令は、単純ながら多くの場所で使える便利な命令です。 まず、strmid命令はだいたい以下のようになっています。

strmid p1,p2,p3,p4                      文字列の一部を取り出す

p1=変数名  : 取り出した文字列を格納する変数名
p2=変数名  : 取り出すもとの文字列が格納されている変数名
p3=0〜(0)  : 取り出し始めのインデックス
p4=0〜(0)  : 取り出す文字数

MSXやPC98などのMicrosoft系のBASICを使ったことのある人であれば、 文字列の取り出しに LEFT$、RIGHT$、MID$という3つの関数があったのを覚えているかもしれません。 これらは、それぞれ文字列の「左からn文字」「右からn文字」「n1文字目からn2文字」を取り出して、 その内容を返すというものでしたが、strmid命令は、その3つの機能を合わせ持った命令です。 p1に取り出した結果を代入する変数名、p2に取り出す元の文字列が記憶されている変数名を指定して、 p3で取り出し始める文字インデックス(0から始まる)、p4で取り出す文字数を指定するというものです。 注意しなければいけないのは、p3の文字インデックスは、 1文字目が0になり、2文字目が1…という0からの順番になっているということ。 これで、たとえば変数bの一部を変数aに取り出す場合、

  • 「左からn文字」を取り出す場合は、「strmid a,b,0,n」となり、
  • 「右からn文字」の場合は、「strmid a,b,-1,n」となり、
  • 「n1文字目からn2文字」は、「strmid a,b,n1-1,n2」

になります。 もう1つ追加されたのは、文字列の中に、指定した文字列が含まれているかどうかを調べるinstr命令です。これはヘルプでは、

instr p1,p2,"string",p3                         文字列の検索をする

p1=変数名  : 検索の結果が代入される変数名
p2=変数名  : 検索される文字列が格納されている文字列型変数名
"string"   : 検索する文字列
p3=0〜(0)  : 検索を始めるインデックス

というようになっています。 p2で指定した文字列型変数の中に、 "string"で指定した文字列があるかどうか調べて、 p1で指定した変数に文字インデックスを代入します。結果は数値になるという点に注意してください。 文字インデックスでは、文字列の始まり1文字目を0として、1,2,3...と順番に増えていきます。 (strmid命令で指定するインデックスと同様)。もし、見つからなかった場合には-1が代入されます。 また、p3で調べ始める文字インデックスを指定することができます。 (指定を省略した場合は、最初(0)からになります) この場合、 検索の結果は調べ始めた場所からのインデックスが返されるということに注意してください。 文字列の最初からのインデックスではなくなります。

日本語文字列のしくみ

いままで説明したことは、とりあえず半角の文字列。アスキーコード表の文字に関しての話です。

 HSPで用意された文字列操作に関する命令も、すべて文字の単位は半角文字になっています。

扱う文字列が、英文字だけであれば問題はありません。しかし、 これが日本語の全角文字を含んでいるとちょっとトリッキーになります。前にも言ったように、 日本語は1バイト(256種類)では表わしきれないので、 2バイト使って表現しています。 つまり、英文字2文字分で日本語1文字なのです。しかしこれらは、 混在できるようになっているために、ちょっと複雑なルールがあります。次のようなルールです。

  • コードが129〜159か、224〜252の範囲にある場合は、次の1バイトと合わせて1文字の全角コードとなる。

つまり、コードの数値がある範囲にある時だけは、2文字分で1つとする特殊なルールを作ってあるわけです。 これは、WindowsやMS-DOSにおいて有効なルールで、 他のOSやインターネット上から持ってきたテキストでは通用しないことがあります。 このような日本語文字コードのことを普通、「シフトJISコード」と呼んでいます。 日本語は、 2バイトで1文字ですから、日本語の文字コードを取り出す場合などはpeek命令を2回行なうか、 wpeek命令を使う必要があります。ただし、wpeek命令は、「$81」「$24」(それぞれ16進数) という2バイトのコードがあった場合、「$2481」(16進数)のように、 2バイトを逆転して 1つの数値にしてしまうので注意が必要です。 そんなこんなで、日本語コードを扱うのは英文字よりも少しやっかいです。 ただし、英文字と日本語を混在させないなどの制限をつけてやれば、 必ず2バイトで1文字という法則になり、文字を取り出したり、長さをチェックしたりする時には容易になるでしょう。

最後に

このドキュメントでは、文字列のしくみを理解することで、 より細か効率的に文字列を操作する方法について説明してきました。 改行コードを含まない文字列に対してのアプローチと、それにメモリノートパッド命令を加えることで、 複数行に渡る文字列であっても効率的に処理することができるようになると思います。 このドキュメントで、その作業が少しでも楽になってくれれば幸いです。