文字列操作関数の動作の違い

Microsoft Access Visual Basic のリファレンス

文字列操作関数の動作の違い

Microsoft Access 95 以降のバージョンの Visual Basic for Applications (VBA) のコードと Access 95 より以前のバージョンの Access Basic のコードとでは、文字列のメモリ上の格納形式が異なります。文字列は、Access Basic のコードでは ANSI 形式で格納され、Visual Basic では Unicode 形式で格納されます。

Visual Basic で Unicode 形式を使用するのは、Visual Basic と密接な関係のある OLE の内部での文字列の形式に合わせるためです。

たとえば、"ABC" という文字列はメモリ上では次のように格納されています。

格納形式 格納状態 内容
Unicode 41 00 42 00 43 00 42 30 44 30 46 30 各文字が 2 バイトで格納されています。
ANSI 41 42 43 82 A0 82 A2 82 A4 ASCII 文字は 1 バイトで格納され、ダブル バイト文字は 2 バイトで格納されています。

この内部形式の違いにより、Access Basic と Visual Basic では動作が異なる文字列操作処理関数があります。動作の異なる関数およびステートメントは次のとおりです。

Asc 関数、Chr 関数、InputB 関数、InStrB 関数、LeftB 関数、LenB 関数、RightB 関数、MidB 関数、および対応するステートメント。

また、Visual Basic では、ChrB 関数と AscB 関数が追加されています。

これらの関数とステートメントは、バイト単位で文字列を操作する点では Access Basic、Visual Basic ともに同じですが、処理する文字列の格納形式が異なるため、動作が異なります。たとえば、LenB("A") は、Access Basic では 1 を、Visual Basic では 2 を返します。

Access 95 以前のバージョンで作成されたプログラムが、これらのバイト単位で動作する文字列操作関数を使用している場合は、Visual Basic では Unicode を意識したソース コードの変更が必要になります。ただし、Len 関数、Left 関数、Right 関数など、文字単位で操作する文字列操作関数だけを使用している場合は、特に認識する必要はありません。

Microsoft Access の以前のバージョンで作成したプログラムを現在のバージョンの Microsoft Access に移動する場合は、文字列処理について、次の点に注意してください。

Asc 関数と AscB 関数

このプログラムは、Access 95 以前のバージョンでは正確に動作していましたが、現在のバージョンの Access の Visual Basic では実行時エラーが発生します。

Print Asc(MidB("", 2,1))

これは、Asc 関数の引数である MidB("", 2,1) が Unicode の文字列として正しくないデータを返すためです。

このプログラムを現在のバージョンの Microsoft Access で動作させるためには、次のように AscB 関数を使います。

Print AscB(MidB("", 2,1))

このプログラムでは、Unicode の 2 バイト目の値 (&H30) が返されます。

Chr 関数と ChrB 関数

Microsoft Access の Chr 関数は、常に 2 バイト文字を返します。以前のバージョンの Microsoft Access では、たとえば Chr(&H41) と ChrB(&H41) が等しくなりますが、現在のバージョンでは Chr(&H41) と ChrB(&H41) + ChrB(0) が等しくなります。

また、以前のバージョンでは、"" は ChrB(&H82) + ChrB(&HA0) で表現されましたが、現在のバージョンでは ChrB(&H42) + ChrB(&H30) で表現されます。

Windows API の呼び出し

いくつかの Windows API 関数では、文字列のバイト長が重要な意味を持ちます。たとえば、次のプログラムは、Windows がセットアップされているフォルダを返します。Microsoft Access では、LeftB(Buffer, ret) は正しい文字列を返しません。これは、ret の値が、ANSI 文字列のバイト長を示しているにもかかわらず、LeftB 関数が Unicode の文字列を操作しているためです。このような場合は、InStr 関数を使って、文字列の中の Null 終端文字列までの文字を返すように修正します。

Private Declare Function GetWindowsDirectory Lib "kernel32" _
    Alias "GetWindowsDirectoryA" (ByVal lpBuffer As String, _
    ByVal nSize As Long) As Long

Private Sub Command1_Click()
    Buffer$ = Space(255)
    ret = GetWindowsDirectory(Buffer$, 255)
    ' WinDir = LeftB(Buffer, ret)   '<--- 正しくないコード"

    WinDir = Left(Buffer$, InStr(Buffer$, Chr(0)) - 1)
                                        '<--正しいコード"
    Print WinDir
End Sub

Input 関数と InputB 関数

Microsoft Access の Input 関数は、文字をファイルから読み出すときに指定した文字数を Unicode の文字列に変換して変数に読み込みます。一方、InputB 関数は、データをバイナリ データであると仮定して Unicode には変換せずにそのまま変数に格納します。次のように固定長のフィールドで格納されているファイルを読むときに InputB 関数を使う場合は、固定バイト長のデータを読み込んでから Unicode に変換する必要があります。

Open "Data.Dat" For Input As 1
dat1 = StrConv(InputB(10, 1), vbUnicode)
dat2 = StrConv(InputB(10, 1), vbUnicode)
dat3 = StrConv(InputB(10, 1), vbUnicode)

===DATA.DAT
123456789012345678901234567
名前      住所      電話

Access 95 以降のバージョンで ANSI 文字列のバイト操作を行う場合

Microsoft Access で ANSI 文字列のバイト単位の操作をする必要がある場合は、StrConv 関数を使用してください。定数 vbUnicodevbFromUnicode を指定することによって、ANSI と Unicode 間で文字列を変換することができます。文字列をいったん ANSI 文字列に変換してからバイト単位の操作を行い、処理が終わった後で Unicode に再度変換すると、比較的簡単に Access 95 より以前のバージョンのコードを使用できます。

'
			
ANSI 
dat = StrConv(dat, vbFromUnicode)
.
.
.    '
			
.    '

.
.
' 
			
Unicode 
dat = StrConv(dat, vbUnicode)

16 ビット版のバイト操作関数と互換性のある動作をする関数の例

Visual Basic for Applications では、Unicode を使用して文字列の内部処理を行っています。そのため、Access 95 より以前のバージョンの Access Basic とはバイナリ操作関数の動作が異なります。

ANSI 関数は、Access Basic と Visual Basic の動作の互換性を保つために用意されています。

メモ これらの ANSI 操作関数で入出力される文字列は、あくまでも Unicode です。関数の内部で一時的に ANSI 文字列に変換し、処理後、Unicode に戻しています。

次のように、DBCS 文字の 1 バイト目と 2 バイト目を結合して、DBCS 文字を作ることはできません。

AnsiMidB("",1,1) + AnsiMidB("",2,1)

これらの関数は、文字列をバイト単位で操作するために用意されています。しかし、バイト単位の操作で異なる文字を生成することはできません。この場合は、次のように記述します。

StrArg = ""
StrArg = StrConv(StrArg, vbFromUnicode)    ' ANSI 
RetArg = MidB(StrArg,1,1) + MidB(StrArg,2,1)    ' 
   ' 
StrArg = StrConv(StrArg, vbUnicode)    '  Unicode 
RetArg = StrConv(RetArg, vbUnicode)    ' 

一般的には、バイト文字列の処理前に文字列を ANSI 文字に変換し、処理した後で、変換した文字列と戻り値を Unicode 文字に復元してください。

バイト文字列の操作は、あくまでも文字列を操作するための関数です。バイナリ データの操作には文字列変数やバイト文字列操作関数ではなく、バイト Array を使用してください。

バイト Array に格納された文字列は、次のように表示されます。


Array 

			
Dim Var() As Byte
Var = ""             ' Unicode 
Var = StrConv("", vbFromUnicode)    ' ANSI 
Function AnsiStrConv(StrArg, flag)
   nsiStrConv = StrConv(StrArg, flag)
End Function
' LenB ANSI 
			
Unicode 
Function AnsiLenB(ByVal StrArg As String) As Long
   AnsiLenB = LenB(AnsiStrConv(StrArg, vbFromUnicode))
End Function
' MidB ANSI 
			
Unicode 
' 
Function AnsiMidB(ByVal StrArg As String, ByVal arg1 As Long, _
           Optional arg2) As String
   If IsMissing(arg2) Then
   AnsiMidB = AnsiStrConv(MidB(AnsiStrConv(StrArg, vbFromUnicode) _
           , arg1),vbUnicode)
   Else
   AnsiMidB = AnsiStrConv(MidB(AnsiStrConv(StrArg, vbFromUnicode) _
           , arg1, arg2), vbUnicode)
   End If
End Function
' LeftB 
ANSI 
			
Unicode 
Function AnsiLeftB(ByVal StrArg As String, ByVal arg1 As Long) As String
   AnsiLeftB = AnsiStrConv(LeftB(AnsiStrConv(StrArg, _
           vbFromUnicode), arg1), vbUnicode)
End Function
' RightB ANSI 
			
Unicode 
Function AnsiRightB(ByVal StrArg As String, ByVal arg1 As Long) As String
   AnsiRightB = AnsiStrConv(RightB(AnsiStrConv(StrArg, _
           vbFromUnicode), arg1), vbUnicode)
End Function
' InStrB  2  Ansi Ansi 
Function AnsiInStrB(arg1, arg2, Optional arg3) As Integer
   If IsNumeric(arg1) Then
   pos = LenB(AnsiLeftB(arg2, arg1))
   AnsiInStrB = InStrB(arg1, AnsiStrConv(arg2, vbFromUnicode) _
           , AnsiStrConv(arg3, vbFromUnicode))
   Else
   AnsiInStrB = InStrB(AnsiStrConv(arg1, vbFromUnicode) _
           , AnsiStrConv(arg2, vbFromUnicode))
   End If
End Function

バイト型のデータの使い方

Microsoft Access では、新しいデータ型としてバイト型 (Byte) が追加されています。バイナリ データの処理に文字列変数を使用すると、ANSI と Unicode 間で文字列が変換され、これによりバイナリ データは変更されます。このように、バイナリ データを処理するときは、バイト型の変数を使用します。

Dim ByteData() As Byte
ByteData = ""        ' Unicode 
ByteData = StrConv("", vbFromUnicode)     'ANSI 
ByteData = InputB(10, #1)    '

Debug.Print ByteData(5)        '