■閉鎖した昔のブログの記事復活
[2025/05/14] BHT-BASIC4.0:変数宣言の書き方その2(BHT-1300) (No.438)
[2025/05/14] BHT-BASIC4.0:変数宣言の書き方(BHT-1300) (No.437)
[2025/05/14] BHT-BASIC:DENSOのハンディターミナルの開発言語のBHT-BASIC4.0を使ってみた (No.436)
[2025/05/14] VB.NET:アプリケーションのエントリポイント(Mainメソッド)について (No.435)
[2025/05/14] VB.NET:ADO.NETを使用したSQL-ServerのテーブルのCSV出力 (No.434)
[2025/05/14] BHT-BASIC4.0:変数宣言の書き方(BHT-1300) (No.437)
[2025/05/14] BHT-BASIC:DENSOのハンディターミナルの開発言語のBHT-BASIC4.0を使ってみた (No.436)
[2025/05/14] VB.NET:アプリケーションのエントリポイント(Mainメソッド)について (No.435)
[2025/05/14] VB.NET:ADO.NETを使用したSQL-ServerのテーブルのCSV出力 (No.434)
-
ハンディターミナルの開発言語であるBHT-BASIC4.0についての3回目になります。 前回は変数宣言について説明しましたが、追加でいくつかの内容を記します。
■変数宣言されない変数
コンパイラの指定で「オプション」の「宣言されていない変数をエラーにする」チェックをOFFすると 変数の宣言が無くても、変数を初めて使用した時点で変数が宣言されたことになります。
以下のソース「MODULE1.SRC」を見てください。
'File [MODULE1.SRC] Sub Test1 WK$ = "ABC" '自動変数[WK$]宣言 Print "WK$=" + WK$ 'Test1内変数[WK$]を参照 End Sub WK$ = "123" 'モジュール変数[WK$]宣言 Sub Test2 Print "WK$=" + WK$ 'モジュール変数[WK$]を参照 WK$ = "***" 'モジュール変数[WK$]を参照・変更 Print "WK$=" + WK$ 'モジュール変数[WK$]を参照 End Sub Call Test1 'MODULE2.SRCのTest1関数をコール Call Test2 'MODULE2.SRCのTest1関数をコール Print "WK$=" + WK$ 'モジュール変数[WK$]を参照 WK$ = "@@@" '上のモジュール変数[WK$]と同じ Print "WK$=" + WK$ 'モジュール変数[WK$]を参照 Wait 0, &h01 'キー入力待ち END
これを実行すると以下の表示の様になります。
コンパイラはソースの先頭からスキャンするため、 最初に関数Test1の中のWK$が出てきた時に、自動変数として宣言します。 この変数はTest1の中でのみ有効です。
Test1のすぐ下でWK$への値に代入が出てきた時点で、 このモジュール内の変数として、WK$が有効となります。
関数Test2の中でWK$を使用していますが、 これは直前のモジュール変数を参照することになります。
上記のソースを実行すると、考え方としてはCall Test1から走るような感覚です。
この例でも分かるように、自動変数を許可するとどこで変数の値が変化してしまうのかが デバッグ時に混乱してしまいます。 それで通常は、コンパイラオプションの「宣言されていない変数をエラーにする」チェックを ONにして、変数は必ず宣言する様にします。
■文字列変数について
今まで気にせずに文字列変数を宣言してきましたが、 実は文字列の格納できる長さについての制限があります。 以下のソースを見てください。
モジュール変数を例にしていますが、最初はWK1$として変数名そのままで宣言しています。 この場合、文字列として格納できる最大の文字数は 40 までです。 暗黙の了解として文字数のMAXを宣言しなければ、40なのです。これは忘れがちなので注意して下さい。
(配列変数の場合は20文字がデフォルトです)
また、この文字数の最大を超えて値を設定すると実行時エラーとして「0Fh:文字列長が範囲外です。」が発生します。 以下のソースでも7行目でエラーが発生します。
さて、2番目の宣言WK2$[10]ですが、これはWK2$の文字数が最大10であることを宣言しています。 そこで以下のソースの中で7行目をコメント行にして実行すると、当然13行目でエラーが発生します。
'File [MODULE1.SRC] PRIVATE WK1$ WK1$ = "123" Print "WK1$=" + WK1$ 'モジュール変数[WK$]を参照 WK1$ = "0123456789012345678901234567890123456789" Print "WK1$=" + WK1$ 'モジュール変数[WK$]を参照 WK1$ = "0123456789012345678901234567890123456789@" Print "WK1$=" + WK1$ 'モジュール変数[WK$]を参照 PRIVATE WK2$[10] WK2$ = "123" Print "WK2$=" + WK2$ 'モジュール変数[WK$]を参照 WK1$ = "0123456789@" Print "WK1$=" + WK1$ 'モジュール変数[WK$]を参照 Wait 0, &h01 'キー入力待ち END
ちなみに、文字列の文字数宣言で最大は8192文字までです。
文字列処理はエラーが発生しない様に、文字数宣言を超えない様に文字数を大きくとるか、 または、最大文字数を超えない様な文字列連結の関数を作るかするしかないと思います。
=====
2016/04/02:の時の情報
PR -
ハンディターミナルの開発言語であるBHT-BASIC4.0についての2回目ですが、 今回はデータ型に続いて変数宣言や、式、演算子などについて説明します。
一般的にプログラム言語で存在する、グローバル変数、モジュール変数、ローカル変数がBHT-BASICにもあります。 グローバル変数、モジュール変数とはユーザが宣言する関数の外で静的に存在する変数です。 ローカル変数とはユーザ関数の中で一時的に存在する変数のことです。
それでは各変数それぞれについて詳しくみていきます。
■グローバル変数
ユーザが宣言する関数の外で、PUBLIC または GLOBALの命令語(ステートメント) で変数を宣言すると、グローバル変数として宣言できます。
PUBLIC と GLOBALですがどちらで宣言してもいい様ですが、 それぞれ違いがあります。 まずはPUBLIC宣言のソースの例です。 この例では「MODULE1.SRC」と「MODULE2.SRC」の2つのソースがあります。 尚、プロジェクトのプロパティでメインモジュールとして「MODULE1.SRC」を指定してあります。
'File [MODULE1.SRC] PUBLIC PDT1% 'グローバル変数宣言・整数型 PUBLIC PDT2$[256] 'グローバル変数宣言・文字列型 Declare Sub Test1 '別ソースの関数を使用する為に型宣言 Main PDT1% = 100 'グローバル変数に値代入 PDT2$ = "1234567890" Call Test1 'MODULE2.SRCのTest1関数をコール Wait 0, &h01 'キー入力待ち END
'File [MODULE2.SRC] 'テスト関数の宣言 Sub Test1 Print "PDT1%=" ; PDT1% 'グローバル変数[PDT1%]を参照 Print "PDT2$=" + PDT2$ 'グローバル変数[PDT2$]を参照 End Sub
これを実行すると以下の表示の様になります。
PUBLIC宣言はソースファイルのどこか一カ所で行えば、 別のソースでそれを参照する場合は、宣言なしでその変数名を使えば 参照できます。
同じ変数名で別のソースでPUBLIC宣言すると二重定義エラーがリンク時に発生します。 但し、同じ変数名でもモジュール変数として宣言すれば別の変数として扱われますので、 注意が必要です。
上記のソースで宣言した、「PDT1%」「PDT2$」を今度はGLOBALで宣言します。 今回の場合は「MODULE2.SRC」の方にも同じ宣言をしないと、 リンク時に変数宣言エラーが発生します。
'File [MODULE1.SRC] GLOBAL PDT1% 'グローバル変数宣言・整数型 GLOBAL PDT2$[256] 'グローバル変数宣言・文字列型 Declare Sub Test1 '別ソースの関数を使用する為に型宣言 Main PDT1% = 100 'グローバル変数に値代入 PDT2$ = "1234567890" Call Test1 'MODULE2.SRCのTest1関数をコール Wait 0, &h01 'キー入力待ち END
'File [MODULE2.SRC] GLOBAL PDT1% 'グローバル変数宣言・整数型 GLOBAL PDT2$[256] 'グローバル変数宣言・文字列型 'テスト関数の宣言 Sub Test1 Print "PDT1%=" ; PDT1% 'グローバル変数[PDT1%]を参照 Print "PDT2$=" + PDT2$ 'グローバル変数[PDT2$]を参照 End Sub
どちらの方法でも同じ結果になるのですが、 PUBLIC または GLOBALのどちらがいいとも言えません。
PUBLICの場合は、他のソースで宣言しなくても使えるので楽と言えば楽かもしれませんが、 変数が多くなりソースファイルの数が増えると管理が厄介です。
但し、PUBLICを宣言するところを一カ所に限定し、 変数の命名規則でPUBLICだとわかるプリフィックスを先頭に付けるのも方法かもしれません。
GLOBALはその変数を参照しているソースファイルで同じ宣言を行う必要が在ります。 この宣言を別ファイルにし、変数の宣言のみのソースとして、各ソースファイルで インクルードすることで煩雑ではなくなると思います。以下のソースがその例です。
'File [TESTINC.INC] GLOBAL PDT1% 'グローバル変数宣言・整数型 GLOBAL PDT2$[256] 'グローバル変数宣言・文字列型
'File [MODULE1.SRC] '$include:'TESTINC.INC' '定数宣言ファイルインクルード Declare Sub Test1 '別ソースの関数を使用する為に型宣言 Main PDT1% = 100 'グローバル変数に値代入 PDT2$ = "1234567890" Call Test1 'MODULE2.SRCのTest1関数をコール Wait 0, &h01 'キー入力待ち END
'File [MODULE2.SRC] '$include:'TESTINC.INC' '定数宣言ファイルインクルード 'テスト関数の宣言 Sub Test1 Print "PDT1%=" ; PDT1% 'グローバル変数[PDT1%]を参照 Print "PDT2$=" + PDT2$ 'グローバル変数[PDT2$]を参照 End Sub
■モジュール変数
ユーザが宣言する関数の外で、PRIVATE または DIMのステートメント で変数を宣言すると、モジュール変数として宣言できます。
PRIVATE 、 DIMのどちらでも効果は同じなのですが、 DIMはレジスタ変数としては宣言できない様です。
以下のソースを見てください。「MODULE2.SRC」では同じ変数名でモジュール変数を宣言しています。 「MODULE2.SRC」のモジュール変数は同じ変数名ですが、「MODULE1.SRC」とは全く異なるメモリ領域に確保されます。
'File [MODULE1.SRC] PUBLIC PDT1% 'グローバル変数宣言・整数型 PUBLIC PDT2$[256] 'グローバル変数宣言・文字列型 Declare Sub Test1 '別ソースの関数を使用する為に型宣言 Main PDT1% = 100 'グローバル変数に値代入 PDT2$ = "1234567890" Call Test1 'MODULE2.SRCのTest1関数をコール Wait 0, &h01 'キー入力待ち END
'File [MODULE2.SRC] PRIVATE PDT1% 'モジュール変数宣言・整数型 PRIVATE PDT2$[256] 'モジュール変数宣言・文字列型 'テスト関数の宣言 Sub Test1 Print "PDT1%=" ; PDT1% 'モジュール変数[PDT1%]を参照 Print "PDT2$=" + PDT2$ 'モジュール変数[PDT2$]を参照 End Sub
これを実行すると以下の表示の様になります。 「MODULE2.SRC」のTest1関数が実行される時には、「MODULE2.SRC」内のモジュール変数として 初期化されたそれぞれの内容が表示されます。
■ローカル変数
ユーザが宣言する関数の内で、PRIVATE、DIM、STATICの各ステートメント で変数を宣言すると、ローカル変数として宣言できます。
グローバル変数やモジュール変数と同じ名前でローカル変数を宣言しても、 全く別ものとして扱われます。(全く別のメモリ領域に割り当てられます)
'File [MODULE2.SRC] PRIVATE PDT1% PRIVATE PDT2$[256] 'テスト関数の宣言 Sub Test1 PRIVATE PDT1% PRIVATE PDT2$[256] PDT1% = 200 'ローカル変数に値代入 PDT2$ = "ABC" Print "PDT1%=" ; PDT1% 'ローカル変数[PDT1%]を参照 Print "PDT2$=" + PDT2$ 'ローカル変数[PDT2$]を参照 End Sub
この様に組むと、関数の中から同じ名前のモジュール変数が参照できなくなります。 そのため、通常プログラムする時にモジュール変数とローカル変数は、なるべくかぶらない様にしています。
=====
2016/04/02:の時の情報
-
DENSOのハンディターミナルBHT-1300を使ったシステムを作成することがあったので、 ハンディターミナルの開発言語であるBHT-BASIC4.0についていろいろ載せていきたいと思います。
このBHT-BASIC4.0ですが言語としてはBASICなのですが、 最近のはやりのVisualBasic.NETとは全然違っています。
一言でいえば相当昔のBASICで、言語仕様的には30数年前の NECのPC8001やPC8801などのROM-BASICと呼ばれていたBASICに近いです。 これらのBASICは元々はMicroSoftが開発したBASICを日本のメーカが採用し ROMの中に入れていたものです。 パソコンの電源ONでいきなりBASICが走る感じになっていました。
なので、昔にBASICを触ったことが有る方ならば懐かしい感じで プログラムできるのではないでしょうか。 実際私も、昔を思い出しながら組みました。
昔語りはこの辺にしまして、BHT-BASIC4.0に話を戻します。 BHT-BASIC4.0(以降はBHT-BASICと称します)はハンディターミナルの上で動作させるためなのか、 昔のBASICよりは制約がいろいろあります。 変数の名前の長さであったり、文字列のMAX長であったりそこは ハンディターミナルなのでメモリの制約があると思われます。
それでは今回は初回なので、変数などについて記します。
■BHT-BASICのハンディターミナル内での構成
ハンディターミナルのメモリにはフラッシュメモリとRAMがあり、 フラッシュメモリの中では以下の構成部分からなります。
・システムプログラム
(BHT-BASICインタプリタ、拡張ライブラリ、拡張関数、システムドライバ等)
・ユーザプログラム
(ユーザが作成した実行可能なプログラムファイル)
・ユーザデータファイル
(ユーザプログラムで作成・使用されるデータファイル等)
■BHT-BASICの開発など
・BASICの文法はほぼMicroSoftのBASICと同じです。
・専用のコンパイラ・リンケージエディタで開発します。
・コンパイラ言語なので行番号は不要です。
・実行可能プログラムファイルは中間言語に変換される。
(ファイル拡張子は PD4 )
・開発環境はWindows7,Windows8などの32bitパソコンが必要です。
・実行プログラムをハンディターミナルに転送する通信ユニットが必要です。
(通信ユニットにはUSBまたはRS-232Cでパソコンと接続)
■BHT-BASICの特徴など
・プログラムの1行は512文字(バイト)
・変数名に使用できるのは、英数字とピリオドのみ
・変数名は大文字と小文字の区別は無く、255文字まで
(但し、文字列変数を表す $ 、 整数変数を表す % は文字数に含めない)
・変数のデータ型には以下の4種類がある。
データ型 型宣言文字 値の範囲 整数型
% -32,768 ~ 32,767 長整数型
& -2,147,483,648 ~ 2,147,483,647 実数型
無し (ゼロ)0
(正数)0.1000000000E-64 ~ 0.9999999999E+63 (負数)-0.1000000000E-64 ~ -0.9999999999E+63文字型
$ 0文字 ~ 8192文字
文字列型の文字数のMAXが8192文字(バイト)なのは注意が必要です。 また、文字列型は他にも注意する点があるのですが、それはまた次回に記します。
今回はこの辺りまでで、終わります。
次回は、変数の宣言の仕方や、式の書き方などにします。
=====
2016/04/02:の時の情報
-
アプリケーションのエントリポイントとは、そのアプリケーションが開始した時に、呼び出されるメソッドのことです。
VB.NETでは、Mainメソッドがエントリポイントですが、 WindowsフォームアプリケーションプロジェクトにはMainメソッドが見当たりません。 エントリポイントであるMainメソッドは、コンパイラによって自動的に作成されます。 以下の様にプロジェクトのプロパティ設定で「スタートアップ オブジェクト」を Form1 に 設定すれば自動でMainメソッドが生成され、その中で Form1 を生成し、表示する様になっています。
しかし、 Form1 の表示前に何かをしたいことがある場合、 例えば、アプリケーションの二重起動のチェックだとか、データベースのオープン処理などがあります。 その場合には、新しいモジュールかクラスを追加し、その中に Sub Main を宣言します。 以下は Sub Main をモジュールに宣言したときの例です。
(ファイル名は MainModule.vb としました)
Module MainModule 'アプリケーションのメイン・エントリポイント <STAThread()> Sub Main() '----- 'メインフォームを表示する前に必要な初期処理を行う・・・ '----- '例:二重起動をチェックする If Diagnostics.Process.GetProcessesByName(Diagnostics.Process.GetCurrentProcess.ProcessName).Length > 1 Then MessageBox.Show("多重起動はできません。") Return End If 'メイン画面の表示(ここでは例としてForm1の表示) Dim frm As New Form1 Application.Run(frm) '----- 'メインフォームが閉じた後で必要な終了処理を行う・・・ '----- End Sub End Module
尚、この MainModule を有効にするには、プロジェクトのプロパティ設定で 以下の様にする必要があります。
「アプリケーションフレームワークを有効にする」のチェックを外し無効にします。
無効にすることで、「スタートアップ オブジェクト」のコンボボックスに MainModule が現れますので、それを選びます。
モジュールではなく、クラスで行う場合は以下の様になります。
Public Class ClsMain 'アプリケーションのメイン・エントリポイント <STAThread()> Shared Sub Main() '----- 'メインフォームを表示する前に必要な初期処理を行う・・・ '----- '例:二重起動をチェックする If Diagnostics.Process.GetProcessesByName(Diagnostics.Process.GetCurrentProcess.ProcessName).Length > 1 Then MessageBox.Show("多重起動はできません。") Return End If 'メイン画面の表示(ここでは例としてForm1の表示) Dim frm As New Form1 Application.Run(frm) '----- 'メインフォームが閉じた後で必要な終了処理を行う・・・ '----- End Sub End Class
このソースの例では、二重起動のチェックを行っていますが、その他、データベースのオープン等 メイン画面が表示される前までに行いたい処理を、初期処理の部分に記述できます。
=====
2016/04/02:の時の情報
-
ADO.NET を使用した SQL-Server のテーブルをCSVファイルに出力する簡単な関数を作成してみました。
CSVファイルを作成する関数への引数は、データベース名とデータソース名と、SQL文、それとCSVファイル名とCSV内のカラムヘッダを 出力するかのフラグになります。
CSV出力処理関数内では SQL-Server への接続をオープンし、指定されたSELECT文を実行します。 SELECT文の結果の行が在れば、1行づつデータを取得します。 初回の行の処理の場合には、リーダーオブジェクトからカラム名を取得しヘッダ行を書き込みます。 その後、各データ行をCSVファイルに書き込みます。 CSVの各データは強制的にダブルクォートで囲む様にしていますので、 データ内のダブルクォートはエスケープ処理しています。
Public Class Form1 'フォーム上に張り付けたボタンでの処理 Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click SqlToCsv("TEST", ".\SQLEXPRESS12", "SELECT * FROM TEST1 ORDER BY CODE", "TEST.CSV", True) End Sub ''' <summary>指定SQL文のCSVファイル出力</summary> ''' <param name="Database">データベース名</param> ''' <param name="DataSource">データソース名</param> ''' <param name="SQL">SELECT文</param> ''' <param name="CsvFilePath">CSVファイルパス</param> ''' <param name="fHeader">カラムヘッダ出力指示</param> Private Sub SqlToCsv(ByVal Database As String, ByVal DataSource As String, ByVal SQL As String, _ ByVal CsvFilePath As String, ByVal fHeader As Boolean) 'CSVファイル書込Encoding(Shift-JIS) Dim Enc As System.Text.Encoding = System.Text.Encoding.GetEncoding("Shift_JIS") 'CSVファイルオープン Dim SWrt As New System.IO.StreamWriter(CsvFilePath, False, Enc) 'SQL-Server接続文字列 Dim DBConnection As String = "Persist Security Info=false;Integrated Security=SSPI;" & _ "Database=" & Database & ";Data Source=" & DataSource & ";" 'SQLコネクションクラス Dim Conn As New System.Data.SqlClient.SqlConnection(DBConnection) Conn.Open() 'コマンドオブジェクト Dim Cmd As New System.Data.SqlClient.SqlCommand(SQL, Conn) 'TESTデータ取得(リーダーオブジェクト) Dim Reader As System.Data.SqlClient.SqlDataReader = Nothing Reader = Cmd.ExecuteReader() '読込行数が存在?? If Reader.HasRows Then Dim fFirst As Boolean = True '読込 While Reader.Read() = True If fHeader = True And fFirst = True Then fFirst = False Dim ColName As String = "" 'カラム数分の処理 For i As Integer = 0 To Reader.FieldCount - 1 'カラム名取得し、ダブルクォートで囲む ColName = SuroundDblQuotes(Reader.GetName(i)) 'カラム名書込 SWrt.Write(ColName) 'カンマ書込 If i < Reader.FieldCount - 1 Then SWrt.Write(","c) End If Next '改行書込 SWrt.Write(vbCrLf) End If Dim ColData As String = "" For i As Integer = 0 To Reader.FieldCount - 1 'データ取得し、ダブルクォートで囲む ColData = SuroundDblQuotes(Reader.GetValue(i).ToString()) 'データ書込 SWrt.Write(ColData) 'カンマ書込 If i < Reader.FieldCount - 1 Then SWrt.Write(","c) End If Next '改行書込 SWrt.Write(vbCrLf) End While End If 'CSVファイルクローズ SWrt.Close() 'クローズ Conn.Close() End Sub ''' <summary>文字列をダブルクォートで囲む</summary> Private Function SuroundDblQuotes(ByVal str As String) As String If str.IndexOf(""""c) > -1 Then '文字列をダブルクォートをダブルクォートでエスケープ str = str.Replace("""", """""") End If Return """" & str & """" End Function
尚、今回のテーブルは以下の生成文で生成しました。
CREATE TABLE TEST1 ( CODE VARCHAR(8) NOT NULL ,DATA1 VARCHAR(16) ,DATA2 VARCHAR(16) ,DATA3 VARCHAR(16) ,CONSTRAINT TEST1_PKC PRIMARY KEY (CODE) ); INSERT INTO TEST1 VALUES('00000001', 'A000001', 'BBBBBB1', 'C001'); INSERT INTO TEST1 VALUES('00000002', 'A000002', 'BBBBBB2', 'C002'); INSERT INTO TEST1 VALUES('00000003', 'A000003', 'BBBBBB3', 'C003'); INSERT INTO TEST1 VALUES('00000004', 'A000004', 'BBBBBB4', 'C004');
結果的にCSVファイル内容は以下の様になります。
"CODE","DATA1","DATA2","DATA3" "00000001","A000001","BBBBBB1","C001" "00000002","A000002","BBBBBB2","C002" "00000003","A000003","BBBBBB3","C003" "00000004","A000004","BBBBBB4","C004"
=====
2016/03/22:の時の情報