はじめまして。VB6.0 + MySQL 4.1.22の環境で、接続文字列を使いデータベース接続を行っています。キャラクタセットはUTF-8なので、
DRIVER={MySQL ODBC 3.51 Driver}
;SERVER=nnn.nnn.nnn.nnn
;DATABASE=db_name
;UID=uid
;PWD=pwd
;STMT=SET CHARACTER SET UTF8
という接続文字列になっていますが、「東京」という文字列を取得すると「テァツヲツ湘・ツウツカ」と文字化けしてしまいます。
http://www.tenjinmail.net/library/index.jsp?id=1198294141826
を参照してみましたが結果は×でした。
接続文字列が正しくないのでしょうか。
×だったリンク先に加えて
ODBCの設定画面の「Connect Option」タブに、
Initial StatementにSET NAMES SJISと入力
してみたらいかがでしょうか?
http://blog.goo.ne.jp/smiu2/e/2003d3885c7d54206c9551652e1a8327
やじゅさん、ご回答ありがとうございます。
おっしゃっていたステートメント設定を試しましたがダメでしたので、発想を変え、取得した文字列を変換する方法を取ろうと考えました。
そこで、
'UTF8 → Unicode 変換関数
'**************************************************
Private Function UTF8_TO_UNICODE(strData As String) As String
Dim bytData() As Byte
Dim objStream As Object
bytData = StrConv(strData, vbFromUnicode)
Set objStream = CreateObject("ADODB.Stream")
With objStream
.Open
.Type = adTypeBinary
.Write bytData
.Position = 0
.Type = adTypeText
.Charset = "UTF-8"
UTF8_TO_UNICODE = .ReadText()
.Close
End With
Set objStream = Nothing
End Function
'**************************************************
'Unicode → UTF8 変換関数
'**************************************************
Private Function UNICODE_TO_UTF8(strData As String) As String
Dim bytData() As Byte
Dim objStream As ADODB.Stream
bytData() = StrConv(strData, vbUnicode)
Set objStream = CreateObject("ADODB.Stream")
With objStream
.Open
.Type = adTypeBinary
.Write bytData()
.Position = 0
.Type = adTypeText
.Charset = "UTF-8"
UNICODE_TO_UTF8 = .ReadText()
.Close
End With
Set objStream = Nothing
End Function
'**************************************************
という関数を作ってみました。データベース接続して取得した文字列を
strText = UTF8_TO_UNICODE("譚ア莠ャ")
のように変換・取得し、
strText = UNICODE_TO_UTF8("東京")
のように変換・利用しようと思います。「譚ア莠ャ」=「東京」。
よくよく調べてみると、http://madia.world.coocan.jp/vb/vb_bbs2/200506/200506_05060016.htmlの質問と同様でした。
しかし、UNICODE_TO_UTF8関数を実行しても思った結果が得られません。コードのどの部分が誤りなのでしょうか?
> 発想を変え、
変えるべきでは無いと思いますけれども…。
> 取得した文字列を変換する方法を取ろうと考えました。
う〜ん、いろいろと混乱があるようですね。
> 'UTF8 → Unicode 変換関数
> Private Function UTF8_TO_UNICODE(strData As String) As String
そもそも、UTF-8 も Unicode である、という点はさておき。
http://homepage1.nifty.com/nomenclator/unicode/ucs_utf.htm
String 型は、UTF-16 形式で格納される事を前提に設計されています。
UTF-8 データの受け渡しなら、Byte配列/ファイルパス/Stream などを使うべきかと。
そもそも、文字列そのものには、文字コード変換(エンコード/デコード)の概念はありません。
それが必要になるのは、文字列をテキストファイル化するなどの目的で、バイトデータの並びに
変換するとき(あるいは逆に、そうしたバイナリから文字列に復元する際)なのですから、
文字コード変換時の入出力に String を使うのは不自然です。
まぁ、String は任意のバイナリを格納できますから、使えないわけではありませんけれども。
で、本題。
>'UTF8 → Unicode 変換関数
> :
> bytData = StrConv(strData, vbFromUnicode)
この処理は、strData 内のバイナリを「UTF-16 → Shift_JIS 変換」するためのものです。
すなわち strData の内容は、UTF-16 エンコードされた文字列で無ければなりません。
(要するに、VB6で通常利用されている標準的な文字列データであるべき、という事です)
もし、strData の内容が UTF-8 バイナリなのであれば、この時点で NG となります。
そして提示されたコードを見ると、その Shift_JIS に変換されたバイナリを、
> .Charset = "UTF-8"
> UTF8_TO_UNICODE = .ReadText()
のようにして、「UTF-8 とみなして無理に読み込もうとしている」のですから、
データが破損するのも当然かと。
> 'Unicode → UTF8 変換関数
> :
> bytData() = StrConv(strData, vbUnicode)
こちらは、strData 内のバイナリを、「Shift_JIS → UTF-16 変換」するものです。
という事で、
> UNICODE_TO_UTF8 = .ReadText()
これも無意味です。
そもそも、Stream の ReadText メソッドは、データを String 型の文字列として返します。
(すなわち、このメソッドを使う限り、出力結果のエンコードは、常に UTF-16 という事です)
# 読み返すと、我ながら、何だかよく分からない事を書いているなぁ…。
ざっくりとまとめ。
・VB6 の String 型は、符号化文字集合としては UCS-2 相当。
・VB6 の String 型は、文字符号化方式としては UTF-16 相当。
・VB6 の StrConv の vbFromUnicode 指定は、UTF-16 なデータを
システム規定のコード(日本語環境では Shift_JIS) に変換する処理。
・ADODB.Stream のテキストモードは、データが UCS-2 の文字集合として管理される。
・ADODB.Stream の Charset 指定は、WriteText されたデータを内部保持用のバイナリに
エンコードする際、および、逆にそれを ReadText で読み出す際などに利用される。
・ADODB.Stream の WriteText メソッドは、(UTF-16 バイナリな)String を受け取る。
・ADODB.Stream の ReadText メソッドは、(UTF-16 バイナリな)String を返す。
http://msdn.microsoft.com/library/ja/jpado260/htm/mdprocharset.asp
質問の仕方が正しくありませんでした。申し訳ありません。
「東京」「譚ア莠ャ」という2つの文字列があり、「譚ア莠ャ」は「東京」のUTF-8でエンコードされた文字列です。
UTF8_TO_UNICODE関数を利用し、「譚ア莠ャ」を「東京」にすることは成功したのですが、UNICODE_TO_UTF8関数を利用し、「東京」を「譚ア莠ャ」することができません。
この場合どうしたらいいのでしょうか?
ここらへんと同じことのような
http://madia.world.coocan.jp/cgi-bin/VBBBS2/wwwlng.cgi?print+200804/08040045.txt
「東京」ぐらいなら、どうにか逆変換できますが、文字によっては不可逆となります。
無理に VB 側でコード変換するのではなく、環境設定側を見直すべきかと思いますよ。
(ここは MySQL の掲示板では無いので、設定方法に関する回答がつくかどうかは微妙ですが…)
>「東京」「譚ア莠ャ」という2つの文字列があり、「譚ア莠ャ」は「東京」のUTF-8でエンコードされた文字列です。
この部分で、微妙に認識が間違っているようです。
まず、「東京」という文字列が、《UTF-8 でエンコード》された場合、
『E6 9D B1 E4 BA AC』という 6 バイトのバイナリになります。
その 6 バイトのバイナリを、《Shift_JIS と仮定》してデコードすると、
E69D = "譚"
B1 = (半角カナの)"ア"
E4BA = "莠"
AC = (半角カナの)"ヤ"
という事で、(見た目上は)「譚ア莠ャ」という 4 文字の文字列になります。
これが、今見えているものの正体でしょう。
そしてそれらのバイナリが、(Byte 配列ではなく)VB の String 型に渡されているため、
実際に変数の内容は《UTF-16 にてエンコード》されて、
"譚" => U+8B5A
(半角カナの)"ア" => U+FF71
"莠" => U+83A0
(半角カナの)"ヤ" => U+FF6C
すなわち、『5A 8B 71 FF 6C FF』の 6 バイトで格納される事になります。
これが、実際のメモリ上にあるものの正体かと。
> UTF8_TO_UNICODE関数を利用し、「譚ア莠ャ」を「東京」にすることは成功したのですが
残念ながら、それは、たまたまうまくいっただけのように思えます。
現在の関数仕様は、
入力:文字列(UTF-16バイナリ)「譚ア莠ャ」
出力:文字列(UTF-16バイナリ)「東京」
になっていますが、本来は、
入力:UTF-8バイナリ『E6 9D B1 E4 BA AC』
出力:文字列(UTF-16バイナリ)「東京」
であるべきです。関数内のコーディング以前に、関数仕様そのものが不適切です。
一応、今の引数仕様のままでも、呼びだし側で
Dim UTF8(5) As Byte
UTF8(0) = &HE6
UTF8(1) = &H9D
UTF8(2) = &HB1
UTF8(3) = &HE4
UTF8(4) = &HBA
UTF8(5) = &HAC
Dim s As String
s = UTF8
などとして、String 変数に『E6 9D B1 E4 BA AC』という UTF-8 バイナリを代入する事は
できますが、その場合の(MsgBox s 等での)見た目上は、「鷦」であるかのように見えるでしょう。
2 文字目は Chr(&HF68A) 、3 文字目はハングルゆえ日本語環境では文字化けします。
# E6 9D => (U+9DE6)"鷦"
# B1 E4 => (U+E4B1)" (私用領域の文字)
# BA AC => (U+ACBA)ハングル音節文字(Hangul syllable Kiyeok-Yeo-Pieupsios)
ということで、変換関数に渡しているという【UTF-8 な東京】が
『E6 9D B1 E4 BA AC』という 6 バイトのバイナリであるのなら、
それを元の「東京」に戻すために、UTF8_TO_UNICODE の実装として
(1) Stream をバイナリモードで開く。
(2) そこに、『E6 9D B1 E4 BA AC』を Write する。
(3) Position を先頭に戻し、UTF-8 テキストモードに切り替え。
(4) ReadText で読み込んで「東京」を得る。
という手順を取ることができます。
また、元データが「譚ア莠ャ」という 4 文字の String であるならば、
(1) それらを Byte 配列に無変換代入し、『E6 9D B1 E4 BA AC』を得る。
(2) あとは、先ほどと同様の手順で「東京」を得る。
という流れになります。
# ただし、本来とは異なる Byte 配列が得られた場合は、その時点で
# すでにデータが破損しているため、変換処理は続行できません。
なお、逆変換である UNICODE_TO_UTF8 の場合は、渡すべきデータは
「東京」という 2 文字の String からの変換になるはずなので、
(1) Stream を UTF-8 のテキストモードで開く。
(2) "東京" の 2 文字を WriteText する。
(3) Position を先頭に戻し、バイナリモードに切り替え。
(4) Read メソッドで読み込んで、『E6 9D B1 E4 BA AC』のバイナリを得る。
という実装になりますね。
> (ここは MySQL の掲示板では無いので、設定方法に関する回答がつくかどうかは微妙ですが…)
MySQL を知らないので、解決案と言えるのかどうかは分かりませんが、
環境設定として、このあたりなどは如何でしょう。
http://puchiko.lowtech.ne.jp/?itemid=349&catid=27
http://www.millionwaves.com/200601220359.html
http://winofsql.jp/VA003334/install060720182639.htm
MySQLは良く分からないので SQL Serverでテストしました
書込みは出来ているでしょうから
MySQLからのReadのみ
F1のフィールドタイプはバイナリ
Dim strS As String
Dim bytS() As Byte
With RecordSet
Do While Not .EOF
bytS() = !F1 ’バイナリフィールドからバイト配列へ
strS = Utf8Decode(bytS()) ' 変換
.MoveNext
Loop
.Close
End With
'(Bas)
Public Declare Function WideCharToMultiByte Lib "kernel32" _
(ByVal CodePage As Long, _
ByVal dwFlags As Long, _
ByVal lpWideCharStr As Long, _
ByVal cchWideChar As Long, _
ByRef lpMultiByteStr As Any, _
ByVal cchMultiByte As Long, _
ByVal lpDefaultChar As String, _
ByVal lpUsedDefaultChar As Long) As Long
Public Declare Function MultiByteToWideChar Lib "kernel32" _
(ByVal CodePage As Long, _
ByVal dwFlags As Long, _
ByRef lpMultiByteStr As Any, _
ByVal cchMultiByte As Long, _
ByVal lpWideCharStr As Long, _
ByVal cchWideChar As Long) As Long
Public Const CP_UTF8 = 65001
Public Function Utf8Decode(ByRef bytUtf8() As Byte) As String
Dim lngUtf8Size As Long
Dim strBuffer As String
Dim lngBufferSize As Long
Dim lngResult As Long
On Error GoTo EndFunction
lngUtf8Size = UBound(bytUtf8) + 1
lngBufferSize = lngUtf8Size * 2
strBuffer = String$(lngBufferSize, vbNullChar) 'デコード文字列を格納するバッファを確保
lngResult = MultiByteToWideChar _
(CP_UTF8, _
0, _
bytUtf8(0), _
lngUtf8Size, _
StrPtr(strBuffer), _
lngBufferSize) 'MultiByteToWideChar で UTF-8 → Unicode 変換
If lngResult Then 'MultiByteToWideChar が成功した場合
Utf8Decode = Left$(strBuffer, lngResult)
End If
Exit Function
EndFunction:
' Error 処理
End Function