Chr関数で期待した文字を取得するには?

解決


Markn  2007-06-27 01:12:25  No: 136834

VB6のChr関数で期待した文字が返ってこなくて困っています。
0x81-0x9F、0xE0-0xFC の範囲で、コード 0x00 の文字を返します。

たとえば、
Open "Test.dat" For Output As #1
 For I = 0 To 255
  Print #1, Chr(I);
 Next I
Close #1
として、Test.dat をバイナリエディタで開くと、上記の範囲が全て 00 になっています。

これは英語版のWin2k+VB6では正常に処理されます。日本語の環境の時のみ発生するようです。
どなたか正しい文字を得る方法をご存じないでしょうか?


tuyu  2007-06-27 01:53:46  No: 136835

割り当てされていないからでは無いですか?

下記でJIS8とEBCDICを切り替えて表示させてみて下さい。

http://www.infonet.co.jp/ueyama/ip/binary/charcode.html


我龍院  2007-06-27 02:00:26  No: 136836

>正しい文字を得る方法をご存じないでしょうか?
正しい文字といっても・・(^^;
Shift-JISの場合その部分は全角文字の第1バイトになっている訳で、
1バイトだけでは文字にならないので、Chr関数が0を返すのでしょう。


我龍院  2007-06-27 02:27:24  No: 136837

えーと、割り当てされていないと言うか、それなら0x80(&h80)や0xFD(&hFD)、
0xFE、0xFFもCStr(Asc(Chr(&h80)))などとやったとき0を返すと思われますが、
実際には数字を返しますよね、自信は無いですが。


Markn  2007-06-27 02:46:40  No: 136838

イミディエイトウィンドウで
? CStr(Asc(Chr(&h80))) とすると 128 が返り、
? CStr(Asc(Chr(&h81))) とすると 0 が返ります。

ちなみに
Open "Test.dat" For Binary As #1
  For I = 0 To 255
    Put #1, I + 1, I
  Next I
Close #1
とすると0x00から0xFFまで1バイトの文字が正しく出力されます。(最後におまけがつきますが・・・)

英語版の環境ではChr関数を使ってN88と同じように処理されます。

「JIS8とEBCDICを切り替えて」というのは、VBの文字セット?を変えるいうことでしょうか?


tuyu  2007-06-27 03:05:51  No: 136839

先のホームページはご覧になりましたか?

「JIS8とEBCDICを切り替えて」というのは、
表示されたキャラクタ表の下側にあるオプション・ボタンです。
ここをJIS8にした時、表示されないエリアがあります。
ホームページの下の方の文中にも下記のような記載がありますが、

*空白の部分は図形キャラクタ (文字) も制御キャラクタも
*割り当てられていない、 未定義の領域です。 

キャラクタが割り当てられていないエリアならChr関数でも
ゼロになるのでは無いでしょうか?

「EBCDIC」は英語圏用、「0x81-0x9F、0xE0-0xFC の範囲」に
アルファベットの小文字、大文字が割り当てられていますが...。


魔界の仮面弁士  2007-06-27 03:13:57  No: 136840

そもそもバイナリ値を送りたいなら、文字列処理であるところの
Print # や Chr/ChrW を使ってはマズイでしょう。
Put # して、無変換のバイナリ値をそのまま出力するべきでしょう。
(英語版では、たまたまその問題が発覚しないというだけで)

> ? CStr(Asc(Chr(&h80))) とすると 128 が返り、
> ? CStr(Asc(Chr(&h81))) とすると 0 が返ります。
コード変換を行わないようにしましょう。

? Hex(AscB(ChrB(&H80))), Hex(&H80) なら、"80" ですし、
? Hex(AscB(ChrB(&H81))), Hex(&H81) なら、"81" ですよね。

Chr関数は、
  入力 … 文字コード値(OS既定のコードページで)
  出力 … 文字(UTF-16 形式のバイナリ)
を返します。

日本語環境では、Shift_JIS の範囲の値しか受け取れず、
範囲外の値の場合の動作は未定義です。

> 最後におまけがつきますが・・・
Byte 値(1バイト)ではなく、Integer値(2バイト)のデータを Put しているからでは。


Markn  2007-06-27 03:19:17  No: 136841

Javaの致命的エラーが発生してしまい、表が表示できません。
割り当てられていないというのはわかるんですが、ある外部の機器と通信する際、1バイト文字を送受信しなければならず、その文字が取るコードの範囲が 0x00 から 0xFF までの全域なので、困っています。


Markn  2007-06-27 03:44:40  No: 136842

具体的な話ですが、例えば以下のような文字列が必要です。
"ABC123#_xx"
ここで、"ABC123#_"は固定部分、"x"は1バイトのHEXデータで、0x00-0xFFの範囲をとります。
どのように一つの文字列にしたらいいのでしょうか?


我龍院  2007-06-27 04:02:25  No: 136843

文字列にしないでバイナリ データで送って下さい。
RS-232Cならここにサンプルが。
http://support.microsoft.com/kb/411403/ja


Markn  2007-06-27 04:33:01  No: 136844

送受信は関数になっています。送信するコマンドは1つめの引数で、文字列になっています。
例えば送信は以下のようになっています。
Declare Function WCom Lib "XXXUSB2.dll" (ByVal str As String, ByVal len2 As Integer) As Integer
(DLL名などは変えてあります)
それで、文字列にしたいんですが、他に逃げ道はありますでしょうか?


魔界の仮面弁士  2007-06-27 04:54:38  No: 136845

> 文字列にしたいんですが
やめましょう。それは最後の手段です。

> 他に逃げ道はありますでしょうか?
もしかしたら、その宣言を
  Declare Function WComBin Lib "XXXUSB2.dll" Alias "WCom" _
    (ByRef data As Byte, ByVal len2 As Integer) As Integer
に変更し、
  Dim data() As Byte
  data = 「送信データ」
  ret = WComBin(data(0), 長さ)
のような感じで処理できるかも。


Markn  2007-06-27 06:04:08  No: 136846

魔界の仮面弁士さんの"逃げ道"を試してみました。
他にもいろいろな設定で試してみましたが、VBがフリーズしたり、エラーが出てだめでした。

う〜ん・・


魔界の仮面弁士  2007-06-27 06:34:17  No: 136847

> 他にもいろいろな設定
こちらには実験環境はおろか、その XXXUSB2.dll の仕様書さえ無いので、
具体的にどんな設定を試したのか書いてもらわないと、回答がつけにくいです。

DLL 側が要求するデータが、C形式の文字列なのか、BSTR なのか、
あるいはバイナリ値なのかさえ不明ですし。

> エラーが出てだめでした。
エラーの内容も書いていただけますか?


我龍院  2007-06-27 18:14:42  No: 136848

魔界の仮面弁士さんが書かれた様に
>Declare Function WComBin Lib "XXXUSB2.dll" Alias "WCom" _
    (ByRef data As Byte, ByVal len2 As Integer) As Integer
参照渡し、ByRef data As Byteにちゃんと変えてますか?

"ABC123#_"に&H81、&H82を付けて送るとして、
  Dim data() As Byte
  Dim strS As String
  Dim i As Integer
  strS = "ABC123#_"
  ReDim data(Len(strS) + 2)
  For i = 0 To Len(strS) - 1
    data(i) = Asc(Mid(strS, i + 1, 1))
  Next
  data(Len(strS)) = &H81
  data(Len(strS) + 1) = &H82
  data(Len(strS) + 2) = &H0
  ret = WComBin(data(0), UBound(data) - 1)

こんなことしてみるとか。


Markn  2007-06-27 23:39:42  No: 136849

おかげさまで、我龍院さんのサンプルにしたがってやってみたところ、正常に送信することができました。皆様ありがとうございます。
お世話になりついでで申し訳ありませんが、受信データの中にもテキストとバイナリがあります。以下のような関数からバイナリの部分を正常に受け取るにはどうしたらいいでしょうか?

Declare Function RComm Lib "XXXUSBDLL2.dll" (ByVal str As String, ByVal len2 As Integer, ByVal WaitTime As Integer) As Integer

Dim strResult As String * 512
Dim intResult As Integer
intResult = RComm(strResult, 512, 20)

strResult が関数から受け取る文字列データになり、"[xx]CrLf" のような形です。例えば、
0x5B 0xEE 0xEF 0x5D 0x0D 0x0A
のようになりますが、この中の 0xEE 0xEF を切り出すのはいいとして、関数から受け取るのがうまくいきません。
ご教授お願いいたします。


魔界の仮面弁士  2007-06-27 23:57:24  No: 136850

> 以下のような関数からバイナリの部分を正常に受け取るにはどうしたらいいでしょうか?
先ほどと全く同様です。
String ではなく、バイト配列としてやりとりする必要があるでしょう。

> Declare Function RComm Lib "XXXUSBDLL2.dll" (ByVal str As String, ByVal len2 As Integer, ByVal WaitTime As Integer) As Integer
As String を使うなら、バイナリ通信としては NG です。

VB 同士の通信であれば、String 型でも助かる見込みがありますが、
Declare で処理する場合、その時点で ANSI / Unicode の文字コード変換が
自動的に行われるため、Shift_JIS の範囲外のデータは保証されなくなります。


Markn  2007-06-29 20:39:04  No: 136851

Declare Function RCommBin Lib "XXXUSBDLL2.dll" Alias "RComm" _
(ByRef Data As Byte, ByVal len2 As Integer, ByVal WaitTime As Integer) As Integer

Dim binResult(512) As Byte
intResult = RCommBin(binResult, 512, 20)

とすると、RCommBinで、

コンパイル エラー:
ByRef 引数の型が一致しません。

が発生してしまいます。
どうしたらいいのでしょうか?


魔界の仮面弁士  2007-06-29 21:29:36  No: 136852

> intResult = RCommBin(binResult, 512, 20)

配列そのものを渡してはダメですよ。先頭要素を参照させないと。
先の “WComBin”のときは、
>>  ret = WComBin(data(0), 長さ)
のように使用していたことを思い出してみてください。


Markn  2007-06-29 21:48:14  No: 136853

おぉ!
そうですね!
どうもバイト配列という概念が・・・

皆様ありがとうございました。
またよろしくお願いいたします。


※返信する前に利用規約をご確認ください。




  


  このエントリーをはてなブックマークに追加