入出力を行う単語   



 この章では、コンソールへの出力、 キーボードからの入力、 コマンドラインから起動する時の引数、 クロックなど、 周辺装置との入力を行う単語について解説する (ファイルの入出力については次の 第9章「ファイル」 で扱う)。



 
 
コンソールへの出力
 
 


 
文字列を表示
 
 すでにプログラム例として使ってきた「表示」ほか、表示関係の機能は以下のものがある。

構文=
<文字>を   一文字表示  → ・ <文字列>を  表示     → ・ <文字列>を  一行表示   → ・  一行表示とは 表示し 改行すること。         空白表示   → ・ <空白数>だけ 複数空白表示 → ・         改行     → ・ <改行数>だけ 複数改行   → ・ <文字列>を  ダブルクオートで囲んで表示 → ・
 上記最後の「ダブルクオートで囲んで表示」は「表示」の代わりに使うもので、"あいう" のように、指定した文字列をダブルクオートで囲んで表示するものである。文字列両端にタブや空白などが含まれている場合に、これを使うとはっきり視認できるので、プログラムの動作確認に使うことも多い。

 
音を鳴らす
 
 多くのOSで、コンソールへBELLコード(十六進で07H)を一文字表示することで音(警告音)が鳴る(古い言い方では「ベルが鳴る」)ようになっているが、これを利用して音を鳴らす機能である。音の種類や大きさは指定できず、システムのデフォルト音が鳴るだけである。

構文=
音を鳴らす → ・    注:「ベルを鳴らす」も等価定義されており、どちらを使っても良い。    注:Linux環境ではターミナルソフト依存であり、鳴らないこともある。

 
数値を表示
 
 数値を表示するためには次の単語が利用できる。

(整数向け)

構文=
<数値>を       数値表示    → ・ <数値>を       二桁で数値表示 → ・ <数値>を       三桁で数値表示 → ・ <数値>を       四桁で数値表示 → ・ <数値>を       五桁で数値表示 → ・ <数値>を <桁数>で 桁数指定で数値表示 → ・
 第7章「文字列操作」の「数値を文字列に変換」 において、数値を文字列に変換する「文字列変換」という処理単語の系統で以下のようなプログラム例を示した。

   [A1]   
   (再掲)
    123を 文字列変換し 一行表示する     123を 10桁で 桁数指定で文字列変換し 一行表示する     123456を 10桁で 桁数指定で文字列変換し 一行表示する
 実は上記は「数値表示」系の処理単語を使えば、以下のように簡単になる。

   [A2]   
    123を 数値表示し 改行する     123を 10桁で 桁数指定で数値表示し 改行する     123456を 10桁で 桁数指定で数値表示し 改行する
 つまり、「文字列変換」した結果を「表示」させるだけなのであれば、これをワンセットで行う「数値表示」を1つ使うほうが合理的ということである。
123 ←数値表示 123 ←桁数指定で数値表示 123456 ←桁数指定で数値表示 ←-10桁--→
 ちなみに、「数値表示」は以下のように定義されていて、「文字列変換」の上位語である。
数値表示とは (数値 → ・)     文字列変換し 表示すること。      注:上は説明のためにあえて簡単にしてあるが、実際は          処理単語属性を付与したり、変換用の文字列実体変数を          自前で用意するなど、もう少し複雑な定義になっている。
このほかに、上級者向けの機能として、「ニブル表示」、「バイト表示」「ワード表示」、「ダブル表示」がある。これらは整数値を16進数で表示するもので、バイナリーデータの表示に用いられる。詳しくは上級編を参照されたい。

(小数向け)
 小数値を表示するためには次の単語が利用できる。

構文=
<小数値>を        <小数桁数>で 小数形式で表示      → ・ <小数値>を <全桁数>と <小数桁数>で 桁数指定で小数形式で表示 → ・ <小数値>を        <小数桁数>で 指数形式で表示      → ・ <小数値>を <全桁数>と <小数桁数>で 桁数指定で指数形式で表示 → ・
 第7章「文字列操作」の「数値を文字列に変換」 において、小数値を文字列に変換する「文字列変換・小数形式」、「文字列変換・指数形式」という処理単語の解説で以下のようなプログラム例を示した。

   [B1]   
   (再掲)
メインとは         値は 小数変数     1.23を 値に 入れ  (小数形式での表示)     値を 0桁と 5桁で 文字列変換・小数形式し     「[」を 表示してから 表示し 「]」を 一行表示し     値を 20桁と 5桁で 文字列変換・小数形式し     「[」を 表示してから 表示し 「]」を 一行表示し  (指数形式での表示)     値を 0桁と 5桁で 文字列変換・指数形式し     「[」を 表示してから 表示し 「]」を 一行表示し     値を 20桁と 5桁で 文字列変換・指数形式し     「[」を 表示してから 表示し 「]」を 一行表示すること。
 上記は「小数形式で表示」、「指数形式で表示」系の処理単語を使えば、以下のように簡単になる。

   [B2]   
メインとは         値は 小数変数     1.23を 値に 入れ  (小数形式での表示)     「[」を 表示し     値を 5桁で 小数形式で表示し 「]」を 一行表示し     「[」を 表示し     値を 20桁と 5桁で 桁数指定で小数形式で表示し 「]」を 一行表示し  (指数形式での表示)     「[」を 表示し     値を 5桁で 指数形式で表示し 「]」を 一行表示し     「[」を 表示し     値を 20桁と 5桁で 桁数指定で指数形式で表示し 「]」を 一行表示し
[1.23000] ←小数形式で表示 [ 1.23000] ←桁数指定で小数形式で表示 [1.23000e+000] ←指数式で表示 [ 1.23000e+000] ←桁数指定で指数形式で表示 ←-------20桁-------→


 
 
コンソールからの入力
 
 


構文=
文字列入力 → <文字列>
 キーボードから文字列を受け取り、ライブラリ内部の文字列実体変数「入力文字列」に格納すると同時に、スタックにそこを指す文字列情報を返す。
 以下に使い方の例を示す。

   [1]    
メインとは         文字列1は 文字列     ここから         「>」を 表示し         文字列入力し 文字列1に 入れ         文字列1が 「おわり」に 等しい文字列             ならば 打ち切り             つぎに         「入力した文字列は "」を 表示し         文字列1を 表示し 「" です」を 一行表示し     繰り返し。
 上記では「文字列入力」から返されたデータを局所変数(文字列1)に代入してから使っている。
 しかし入力した文字列の実体は、内部に確保された(大域変数としての)文字列実体変数「入力文字列」に格納されるので、ユーザプログラムから直接その変数を参照しても良い。これを利用したのが次のプログラムとなる(局所変数が不要となった)。

   [2]    
メインとは     ここから         「>」を 表示し         文字列入力したものが 「おわり」に 等しい文字列             ならば 打ち切り             つぎに         「入力した文字列は "」を 表示し         入力文字列を 表示し 「" です」を 一行表示し     繰り返し。
 上の2つのプログラムは同じ動作をし、結果は以下のようになる。
>abc                                       ←キーボードから入力
入力した文字列は "abc" です
>おわり                                    ←キーボードから入力
 文字列の入力中に文字の訂正(編集)をおこなうことができるが、それはOSの機能であるためMindは関知しない。エンターキーを打鍵することでMindに戻ってくるが、最後のエンターキーのコード(CRまたはLF)は返される文字列に含まれない。


 
 
起動引数
 
 

 OSの仕様として、プログラムを(正確にはコンソールから起動するようなプログラムを)起動する際、起動するプログラム名以外に、文字列としてのデータ(引数)も与えることができる。この引数をMindでは「起動引数」呼ぶ。
 たとえば、コマンドプロンプトの画面から、Mindにコンパイルさせるために、
>mind hello file
と入力した場合、mind が起動すべきプログラムであり、hellofile はその起動時の引数、すわなち起動引数である。
 Mindのプログラムは、起動引数 という名前の関数を呼び出すことで、自分が起動されたときの起動引数を参照することができる。以下のような仕様である。

構文=
起動引数個数 → <個数>           ↑引数が無い場合は0が返される             ↓n番目の引数を表す文字列が返される 起動引数(n) → <文字列>      ↑nは1〜起動引数個数の整数 起動引数をモニタ → ・     (起動引数の内容をモニタ表示する)
 以下はサンプルプログラムである。
 (プログラム "testarg.src")
メインとは     起動引数個数が ゼロ?         ならば 「起動引数無し」を 一行表示し         さもなければ             起動引数個数を             回数指定し                 「 Arg(」を 表示し                 回数を 数値表示し                 「)=[」を 表示し                 起動引数(回数)を 表示し                 「]」を 一行表示し             繰り返し         つぎに。
 (実行結果)
>testarg 起動引数無し >testarg abc 1234  Arg(1)=[abc]  Arg(2)=[1234] >testarg "abc 1234"  Arg(1)=[abc 1234]
 上の実行画面では3つのケースをテストしている。
 1つ目は起動引数無しの起動であり、プログラムもそれを確認できている。
 2つ目は abc と 1234 の2つの起動引数が与えられ、これをプログラムが認識している。
 3つ目はコマンドラインからの指示を "abc 1234" としているが、それが空白を含んだ1つの文字列(引数)として扱われ、そのような認識結果となっている。
 さて、コマンドラインからファイル名が与えられ、何らかのファイル処理をおこなうようなプログラムを作成した場合、複数のファイルを一括指示するために、実ファイル名の羅列ではなくワイルドカードを使いたいこともある。
 Mindのプログラムを書く際にワイルドカードを考慮する必要はなく、自動的に行なわれる。すなわち、プログラムが起動した時(正確には「メイン」の実行に入った時)には既にワイルドカードが展開されて起動引数群には実ファイル名が設定済みである。
 次が実行例である。
 (実行結果)
>testarg test*  Arg(1)=[test.src]  Arg(2)=[test2.src] >testarg *test*  Arg(1)=[test.src]  Arg(2)=[test2.src]  Arg(3)=[処理test.inf]  Arg(4)=[処理test.src] >testarg *TEST*  Arg(1)=[test.src]  Arg(2)=[test2.src]  Arg(3)=[処理test.inf]  Arg(4)=[処理test.src] >testarg obj\test.*  Arg(1)=[obj\test.exe]  Arg(2)=[obj\test.his]  Arg(3)=[obj\test.mco]  Arg(4)=[obj\test.sym]
 上記を補足する。
 Windowsではワイルドカードの展開機能はOSによるものではなく、Mindの標準ライブラリがプログラム起動直後に介入しておこなっている。LinuxではOSが(シェルが)ワイルドカード展開を行うのでMindは介入しない。
 ワイルドカードに使える文字は *(任意の複数文字に合致)と ? (任意の1文字に合致)である。
 WindowsではOSがファイル名の英字大小を同一視するのと同様にワイルドカードもそのように扱われる(上記で3例目)。
 また、ワイルドカードにパス名を付けることもできる(上記で4例目)。
注1
サンプル testarg.src による起動引数のダンプ処理であるが、本項冒頭の構文のところに示したように、同じことをおこなう処理単語「起動引数をモニタ」が標準で備わっており、この1語を書くだけで起動引数の状況確認ができる。プログラムの動作確認に利用して欲しい。
注2
起動引数に含まれるオプションを解析するのみ便利な「起動引数をシフト」という機能もある。上級編で解説する。


 
 
私のプログラム名、私のパス名
 
 


構文=
私のプログラム名    → <自身のmcoファイル名(パス付き)> 私のプログラム名・短縮 → <自身のmcoファイル名(パス無し、拡張子無し)> 私のパス名       → <自身のmcoファイルが格納されているパス>
 自身のプログラムの名前をプログラムの中から知りたいことがあり、そのための単語である。
 自身のプログラムの.mcoファイルの名前、あるいは(パスも拡張子も無い)単純なプログラム名、あるいはパスのみ‥を文字列として得ることができる。
 以下は例である。
     (Windows)
               sample¥hello.exe を実行したとき

私のプログラム名    → c:¥pmind¥sample¥hello.mco
私のプログラム名・短縮 → hello
私のパス名       → c:¥pmind¥sample
     (Linux)
               sample/hello を実行したとき

私のプログラム名    → sample/hello.mco
私のプログラム名・短縮 → hello
私のパス名       → sample

                ./hello を実行したとき

私のプログラム名    → ./hello.mco
私のプログラム名・短縮 → hello
私のパス名       → .
 上記のように、WindowsとLinuxとでパスの返り方が異なるので注意したい。
 Windowsではプログラムを起動する際のパスが絶対パスであろうと相対パスであろうと絶対パスで返されるが、Linuxではプログラムを起動した際のパス指定がそのまま返される。
 もしLinuxで相対パスで起動された場合に、プログラムの絶対パスを得たい場合は、以下のようなコードで対応して欲しい。
私のパス名の 左端文字が パス記号に 等しい     ならば ※ 絶対パスで起動された(PATH見て起動含む) ※         私のパス名をつみ     さもなければ         ※ 相対パスで起動された ※         カレントディレクトリと 私のパス名で 絶対パスと相対パスをマージする     つぎに ・・・・する



 
 
時間待ち、クロック
 
 


 
時間待ち
 
 時間待ち(ディレイ)用に次の単語が使用できる。

構文=
十ミリ秒単位待つ → ・ 百ミリ秒単位待つ → ・ 百ミリ秒待つ   → ・ 二百ミリ秒待つ → ・ 三百ミリ秒待つ → ・ 五百ミリ秒待つ → ・ 七百ミリ秒待つ → ・ 一秒待つ → ・ 二秒待つ → ・ 三秒待つ → ・ 五秒待つ → ・ 七秒待つ → ・ 十秒待つ → ・
 タイマ機能として以下のものが利用できる。

構文=
 (起点となる時からの経過時間を知りたい場合(カウントアップ)) 計時開始 → ・   Windows版Mindのみ 経過時間 → ミリ秒 Windows版Mindのみ  (あらかじめ設定した時間が経過したかを知りたい場合(カウントダウン)) <ミリ秒>を タイマ設定する → ・ タイムアウト? → <真/偽> タイムオーバ? → <真/偽>     ←「タイムアウト?」の等価語
 「タイマ設定」と「タイムアウト?」について補足する。
 このの組み合わせにより、何か別のことを処理しつつ、一定時間が経過したかを時々検査することができる。(単に時間待ちなら「〜秒待ち」を使うのが良い)
 以下にサンプルプログラムを示す。
 (プログラム "タイマ設定.src")
メインとは     起動引数(1)を 数値変換し (真偽を) 捨ててから タイマ設定し     ここから         タイムアウト?             ならば 打ち切り             つぎに         「・」を 表示し         百ミリ秒待ち      ←重要     繰り返し     改行すること。
 (実行結果)
>タイマ設定 10000 ・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・ ・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・ ・・・・・・・・・・・・・・・・・・・ >
 上記ではプログラム起動時の引数で 10000 を指定しているが、単位がミリ秒なので10秒を設定したことになる。
 繰り返しループ内で「タイムアウト? ならば ・・・」を記述することでタイムアウトになったかを調べている。まだタイムアウトでない場合には、1ループ毎に1つ「・」を表示するとともに、100ミリ秒だけディレイしてからループ先頭に戻している。
 タイムアウトでないときのパスで「百ミリ秒待ち」をおこなっていることはとても重要である。この例のようなプログラムではループ内の1回の処理時間が短いため、意図的な時間待ちを入れないと、極めて高速にループを回ることになり、CPUに過大な負荷をかけてしまう。(空のループを回すとCPU負荷が100%近くになることもあるので注意すること)
 ちなみに、「n秒待ち」系の時間待ち単語ではその内部でOSに制御を戻すような別の機構を使っており、時間待ち中のCPU負荷は無い。


 
日時の参照(基本)
 

構文=
日時を得る → ・      上記は簡易的な使い方であり、このほかに、値を取得する先の      構造体を明示する使い方もある。詳しくは上級編にて解説する。
 「日時を得る」は、この単語を実行した瞬間の日時情報を得る機能である。スタックは空で戻るが、別にライブラリ内構造体の中に各種値が設定されているので、そちらの値を直接に参照して利用する形となる。
 「日時を得る」の実行によってセットされる変数群は以下の通りである。

構文=
年は  変数。    (1900〜) 月は  変数。    (1〜12) 日は  変数。    (1〜31) 時は  変数。    (0〜23) 分は  変数。    (0〜59) 秒は  変数。    (0〜59) 曜日は 変数。    (1〜 7 ‥ 1=日曜、7=土曜)      上記は厳密には通常変数ではなく構造体の要素変数なのだが、      とりあえずは簡単に「通常変数として参照できる」と理解して      欲しい。詳しくは上級編にて解説する。
 上記で「曜日」は文字列ではなく整数値(1〜7)なので注意すること。「月」「火」・・のような文字列が必要であれば、次のプログラム例のように、曜日を表す整数値を文字列に変換するコードを書く必要がある。
 「日時を得る」を使うプログラム例を以下に示す。
 (プログラム "日時を得る.src")
メインとは     日時を得て     「今の日時は 」を 表示し     年を 数値表示し 「年」を 表示し     月を 数値表示し 「月」を 表示し     日を 数値表示し 「日」を 表示し     時を 数値表示し 「時」を 表示し     分を 数値表示し 「分」を 表示し     秒を 数値表示し 「秒」を 表示し     「(」を 表示し     曜日で     選択する         「日」 「月」 「火」 「水」 「木」 「金」 「土」     選択終り     表示し 「曜日)」を 一行表示すること。
 (実行結果)
>日時を得る 今の日時は 2016年12月19日18時7分37秒(月曜日) >

 
日時の参照(高度なもの)
 

構文=
日時を値で得る → <日時パック値(整数)>
 年〜秒までのすべての日時情報を整数(32ビット)にパックした値を得る。情報量が少なくて済み、また単純な整数であるため、多数の日時情報を記憶したり、日時同士を大小比較するのも容易である。

構文=
<日時パック値(整数)> 日時を分離 → ・      上記は簡易的な使い方であり、このほかに、値を格納する先の      構造体を明示する使い方もある。詳しくは上級編にて解説する。
 前記の「日時を値で得る」によって得られた日時情報(32ビット整数)を与えることで、「日時を得る」と同様に、ライブラリ内構造体の中の変数群(「年」〜「秒」という名前の変数)に値が設定される(スタックは空で帰る)。変数群については「日時を得る」の解説を参照されたい。

構文=
日時を正規化 → <日時パック値(整数)>      上記は簡易的な使い方であり、要素変数を持つ構造体を明示する      使い方もある。詳しくは上級編にて解説する。
 「日時を正規化」は、一つ前の「日時を分離」の逆をおこなうもので、ライブラリ内構造体の中の変数群(「年」〜「秒」という名前の変数)に値が設定されている状態で呼び出すことにより、その日時情報(32ビット整数)を得ることができる。変数群については「日時を得る」の解説を参照されたい。
 「日時を正規化」には以下のような用途がある。
(上段2つは要素変数への反映を期待するものなので、スタックに返された整数値は捨ててしまっても良い)
(Linux環境では時・分・秒は正規化されない)

構文=
日時を正規化し 正規化日時を英語に変換する → <文字列>
 必ず「日時を正規化」との2語のペアで使う。正規化の終わった日時情報をもとに英語の日時を表す文字列を生成する。
 返される文字列の実体は標準ライブラリ内に格納されたものなので、この単語を再度呼び出すと最初の文字列が破壊されてしまう。直ちに消費しない場合には自前のの文字列実体変数に退避するのが安全である。
 以下にサンプルプログラムを示す。
 (プログラム "正規化日時を英語に変換.src")
メインとは     日時を得て     日時を正規化し 正規化日時を英語に変換し     「今の英語の日時は 」を 表示してから 表示し     「 です。」を 一行表示すること。
 (実行結果)
今の英語の日時は Tue Dec 20 11:10:29 2016 です。


構文=
日時をGMTで得る → ・      上記は簡易的な使い方であり、このほかに、値を取得する先の      構造体を明示する使い方もある。詳しくは上級編にて解説する。
 「日時をGMTで得る」は「日時を得る」と類似の機能だが、グリニッジ標準時での日時を返す点が異なる。インターネット上など、GMTで日時を処理したい場合に使う。
 基本的な動作は、「日時を得る」の解説を参照されたい。


 
日時を文字列に変換、表示
 
 本章の「日時の参照(基本)」のセクションの中で、現在の日時を得てそれをコンソールに表示するサンプルプログラムを提示した。そこの実行例では現在の日時が、
2016年12月19日18時7分37秒(月曜日)
のように表示される。しかしサンプルのソースコードをご覧いただくと分かるように、これだけの表示をするのにそこそこの分量のプログラムを必要とする。
 このようなケースでは、次に示すような日時を文字列に変換する処理単語を利用すると良い。

構文=
<日時パック値(整数)>を <フォーマット文字列>で             スタンプを日時文字列に変換 → <文字列情報>    注1:変換結果を文字列実体変数として暗黙の変数「日時変換文字列」に         生成し、そこを指す文字列情報がスタックに返される    注2:生成先の文字列実体変数を明示することもできる。上級編で解説する
 まず上記の「日時パック値(整数)」は、処理単語「日時を値で得る」で得られるような形式であり、もし年・月・日バラバラの値から使うのであれば、前処理として「日時を正規化」によってこの形式の値にすることができる。
 次に「フォーマット文字列」というのは、以下のような、年・月・日・時・分・秒・曜日を表すようなシンボルと、その他の一般文字を並べたものである。
 注:1桁の月、たとえば6月はデフォルトでは「06」と変換される。
   これを「6」や「 6」にするには下記表末尾の '#' 指示を使う。
Y 年(4桁) y 年(末尾2桁) M 月(2桁) D 日(2桁) h 時(2桁) m 分(2桁) s 秒(2桁) w 曜日('日'〜'土'の1文字) W 曜日("Sun"〜"Sat"の3文字) (下は1桁の数字の扱い指示。その箇所以降のフォーマットに適用される) #- 足りない桁を詰め (6→「6」となる) #s 足りない桁を空白にする(6→「 6」となる) #0 足りない桁を'0'にする (6→「06」となる(デフォルト))
 以下は変換例である。
フォーマット文字列 変換結果
Y-M-D h:m:s 2013-08-23 14:20:36
Y年M月D日 h時m分  2013年08月23日 14時20分
Y年#-M月D日 h時m分  2013年8月23日 14時20分
h時m分s秒(w)    14時20分06秒(水)
h時m分s秒 w曜日   14時20分06秒 水曜日
h:m:s (W)    14:20:36 (Wed) 



構文=
<日時パック値(整数)>を 日時を表示 → ・ <日時パック値(整数)>を タイムスタンプを表示 → ・    注:上記2語は等価なのでどちらを使っても良い
 前記の「スタンプを日時文字列に変換」の上位語で、与えられた日時パック値(整数)をもとに日時を表示する。次のように利用する。
現在の日時を表示とは (・ → ・) 「現在の日時は 」を 表示し 日時を値で得て 日時を表示し 「 です。」を 一行表示すること。 ↓出力例 現在の日時は 2017-02-23 17:15:30 です。
ちなみに、処理単語「日時を表示」はfileライブラリ内で次のように定義された簡単なものである。
日時を表示とは 処理単語 .N (日時(32) → ・)         実体1は 文字列実体 長さ 日時変換文字列長さ     "Y-M-D h:m:s"で 実体1をつかい スタンプを日時文字列に変換し     表示すること。