Unicodeで書かれたハングル文字のテキストファイルをVBAで読み込みセルに描画するには?

解決


ちい  2003-11-06 03:03:35  No: 80256

下記関数でハングル文字を含むテキストファイルを読み込むと
ハングル文字の部分だけ文字化けします。

デバッグでステップ実行すると、
読み込んだ後のStrにはUnicodeのテキストファイルを
SJISで開いたときに表示される文字がウォッチできます。

ハングル文字を表示させる環境は整っています。
また、そのテキストファイルをそのままエクセルで開くとちゃんと表示されます。
ファイル(TEST.txt)の中身は、
ハングル文字や半角英数字がUnicodeで箇条書きされているだけです。

関数:
Sub LoadFile()

    Dim idx as Long     'セルの列
    Dim n As Long       '
    Dim sFileName       '読み込むファイル名
    
    '変数初期化
    idx = 1
    n = FreeFile
    sFileName = "C:\〜\TEST.txt"

    'ファイルオープン 
    Open sFileName For Input As #n

    'ヘッダ読み飛ばし 
    Do Until EOF(n)
    
        'ファイル読み込み 
        Line Input #n, Str
        
        'セルに書き込み  
        ActiveWorkbook.ActiveSheet.Cells(idx, 1) = Str
        
    Loop
    
    'ファイルクローズ 
    Close #n

End Sub

過去ログをあさってみたのですが、どうにもつかめません。
よろしくお願いいたします。


魔界の仮面弁士  2003-11-06 03:39:16  No: 80257

具体的には、どのUnicodeですか?
(恐らく、UTF-8 か UTF-16 あたりだとは思いますが)

> ハングル文字の部分だけ文字化けします。
ADO(2.5以上)の Streamオブジェクトを利用できます。
Charsetで文字コード、LineSeparatorで改行コードを指定します。

    Dim Stm As Object
    Dim SourceFilePath As String
    Dim rngCells As Excel.Range
    
    Const adCRLF = -1
    Const adReadLine = -2

    Set rngCells = Sheet1.[A1]
    
    SourceFilePath = "C:\〜.TXT"

    Set Stm = CreateObject("ADODB.Stream")
    Stm.Open
    Stm.Charset = "UTF-8"
    Stm.LineSeparator = adCRLF
    Stm.LoadFromFile SourceFilePath

    Do Until Stm.EOS    'End Of Streamまでループ
        rngCells.Value = Stm.ReadText(adReadLine)
        Set rngCells = rngCells.Offset(1)
    Loop
    Set rngCells = Nothing

    Stm.Close
    Set Stm = Nothing

上記では1セルずつ出力させているので、行数が多くなると重くなるかもしれません。
必要に応じて、適宜書き換えてみてください。


ちい  2003-11-06 09:56:15  No: 80258

Unicodeは16です。
テキストをダンプしたら、
全てが2バイトになってました。


魔界の仮面弁士  2003-11-06 10:39:42  No: 80259

> Unicodeは16です。
ごめんなさい、意味がわかりません。。。
UCS-2、という事でしょうか?
http://homepage1.nifty.com/nomenclator/unicode/ucs_utf.htm

> テキストをダンプしたら、
> 全てが2バイトになってました。
う〜ん。多分、UTF-16かUTF-16LEだとは思いますが…。
中身を見ないと、何とも言えないですね。


ちい  2003-11-06 11:01:09  No: 80260

すいません。
変な省略をしてしまいました。
UTF-16LE
です。


ちい  2003-11-06 11:05:36  No: 80261

BOMありです。
(BOMとはファイルの先頭に0xFF,0xFEがついてることですよね?)


魔界の仮面弁士  2003-11-06 12:31:14  No: 80262

> UTF-16LE
であれば、先のサンプルのCharsetの指定部分を指しかえれば、
おそらく、そのまま動くと思いますよ。
(実際に試して見ると、どうなりますか?)

> BOMありです。
UTF-16LEは、BOMの無いエンコーディングです。
なのでそれは、おそらく UTF-16 ですね。

> (BOMとはファイルの先頭に0xFF,0xFEがついてることですよね?)
今回使用するデータの先頭にある 0xFF,0xFE は、確かにBOMです。

しかしそれを逆にして、「BOMとはファイルの先頭に0xFF,0xFEがついてること」というのは誤りかと。
(エンコーディングによっては、BOMが 0x00,0x00,0xFE,0xFF という並びになる物もありますから…)


ちい  2003-11-06 19:42:40  No: 80263

度々のレスありがとうございます。
さて、

> Stm.Charset = "UTF-8"

Stm.Charset = "UTF-16"
にすると
「実行時エラー'3001':
  引数が間違った型、または許容範囲外であるか、競合しています。」
となってしまいます。
"UTF-8"では、動いていますが、テキストが"UTF-16"(or"UTF-16LE")なので
化けてしまいます。

ちなみにテキストファイルは
--------------------

[FONT]
  FONT1 = "■"
  FONT2 = "■"

--------------------

ダンプしたものは、
--------------------
FF FE 0D 00 0A 00 5B 00 46 00 4F 00 4E 00 54
00 5D 00 0D 00 0A 00 09 00 46 00 4F 00 4E 00
54 00 31 00 20 00 3D 00 20 00 22 00 00 AC 22
00 0D 00 0A 00 09 00 46 00 4F 00 4E 00 54 00
32 00 20 00 3D 00 20 00 22 00 00 AC 22 00 0D
00 0A 00 0D 00 0A 00        
--------------------

となっております。


ちい  2003-11-06 19:44:40  No: 80264

↑テキストファイル中の"■"はハングル文字で、ここでは化けてしまうかもしれないと思いわざと■にしました。
実際はどちらもハングル文字の
"フト"
という文字です。(半角カナで再現してみました。)


魔界の仮面弁士  2003-11-06 23:03:27  No: 80265

> Stm.Charset = "UTF-16"
これで駄目ならば、"unicode" だとどうでしょうか。エラーになるかも知れませんが、
うまくいけば、リトルエンディアンのUTF-16として認識されるかと思います。

> 引数が間違った型、または許容範囲外であるか、競合しています。
う〜む。私の環境では、"UTF-16"、"UTF-16BE"、"UTF-16LE"のいずれも認識されるのですけれどね。。。

なお、Charsetプロパティに指定できる値は、環境によって異なるそうです。
(IEのバージョン、OSの種類や導入しているサーバソフトウェアなどで使用可能な種類が増えるようです)

どうしても駄目なら、Get#ステートメントを使って、先頭2バイトを除いたそれ以降のデータをバイト配列に受け取り、それを利用されては如何でしょうか。
試してはいませんが、VBの内部文字コードは UCS-2 なので、そのバイトデータをUnicode文字列として扱えるかも知れません。

> テキストが"UTF-16"(or"UTF-16LE")なので
確かに、UTF-16LEの可能性も残されますが……その場合、先頭の FFFE は、BOMではなく、文字として扱われる事になりますよ。
(いわゆる、ZWNBSP文字———ZERO WIDTH NO-BREAK SPACE——ですね)


ちい  2003-11-07 01:07:57  No: 80266

レスありがとうございます。

テキストは、
http://homepage1.nifty.com/nomenclator/unicode/ucs_utf.htm#utf16
にある、
「UTF-16のリトルエンディアン 」("abc" -> FF FE 41 00 42 00 43 00)
にあたると思います。
この場合の"FF FE"はBOMではなくZWNBSP文字なのでしょうか?
また、LEとはリトルエンディアンのことだと認識してますが、
「UTF-16のリトルエンディアン 」と「UTF-16LE」は、
別物のように書かれているような印象を受けますが、別物なのでしょうか?

これから
Get#で試してみます。


ちい  2003-11-07 01:10:54  No: 80267

ちなみに環境は、
Windows2000(WindowsUpdateは頻繁にやってます)
Office2000(OfficeのUpdateも頻繁にやってます)
VB6.0
IE6.0
です。


魔界の仮面弁士  2003-11-07 03:39:05  No: 80268

> この場合の"FF FE"はBOMではなくZWNBSP文字なのでしょうか?
[FF FE 0D 00 0A 00] というバイトデータの並びは、
UTF-16 として見れば『ChrW(&HD) & ChrW(&HA)』の意味ですが、
UTF-16LE として見れば『ChrW(&HFEFF) & ChrW(&HD) & ChrW(&HA)』の意味です。

つまり、そのテキストがどのエンコーディングで作成されているかによって、どちらの意味にも取れるわけです。
(どちらと見るべきなのかは、そのテキストの作成者に聞いてください)
まぁ十中八九、UTF-16 だと思いますけれどね。

> また、LEとはリトルエンディアンのことだと認識してますが、
それは確かにその通りです。
UTF-16LEとUTF-32LEはリトルエンディアンの形式、UTF-16BEとUTF-32BEはビッグエンディアンの形式です。

なお、UTF-16とUTF-32の形式は、BOMによってリトルエンディアンにもビッグエンディアンにもなります。
(UTF-16でBOMが無い場合は、ビッグエンディアンです)

> 「UTF-16のリトルエンディアン 」と「UTF-16LE」は、
> 別物のように書かれているような印象を受けますが、別物なのでしょうか?
実は、それらは別物なのです。

BOMはエンディアンの識別の目的で使われるものですから、UTF-16LE や UTF-16BE のように、バイト順が分かっている時には不要なのです。
UTF-16BE、UTF-16LEの先頭に ZWNBSP が来た場合、それは文字と扱われ、BOMとして削除される事はありません。

ちなみに、今回の [フト] というハングル1文字、すなわち、UCS-2の
  U+AC00  "Hangul syllable Kiyeok A"
をUTF-16で表現した場合、以下のいずれかのバイトの並びとなります。

1.  [AC 00] ……BOM無しのUTF-16
2.  [FE FF AC 00] ……ビッグエンディアン(BOM付き)のUTF-16
3.  [FF FE 00 AC] ……リトルエンディアン(BOM付き)のUTF-16

> これから
> Get#で試してみます。
『.Charset = "unicode"』も駄目だった、という事でしょうか?


ちい  2003-11-07 20:01:38  No: 80269

詳しい解説ありがとうございます。

> 『.Charset = "unicode"』も駄目だった、という事でしょうか?
これは、やってみたのですが、化けてしまいました。
エラーは出ません。

テキストについてですが、作成者は私です。
要は、考えるに「リトルエンディアン(BOM付き)のUTF-16」です。


ちい  2003-11-07 20:27:38  No: 80270

すいません。
> > 『.Charset = "unicode"』も駄目だった、という事でしょうか?
> は、やってみたのですが、化けてしまいました。
> エラーは出ません。
できました。
あとでまとめてもろもろ報告いたします。


魔界の仮面弁士  2003-11-07 20:40:08  No: 80271

> テキストについてですが、作成者は私です。
であれば、その際にどの文字コードを選択したか……ですね。
(例えば、最近の秀丸なら、保存時にBOMの有無まで含めて指定できます)

> 要は、考えるに「リトルエンディアン(BOM付き)のUTF-16」です。
了解です。

————それで結局の所、先の
> これから
> Get#で試してみます。
の方は、うまく行ったのでしょうか? 駄目だったのでしょうか?

ちなみに、少なくとも当方では、先述の
--------------------

[FONT]
    FONT1 = "フト"
    FONT2 = "フト"

--------------------
なファイルを、問題なく表示出来ています。

Dim B() As Byte
Dim F As Integer
Dim rngCells As Excel.Range
Dim R As Excel.Range
Dim ColumnData As Variant
Dim LineData As Variant

F = FreeFile()
Open "C:\B.TXT" For Binary As #F
If LOF(F) > 2 Then  '先頭2バイトはBOM
    ReDim B(LOF(F) - 3)
    Get #F, 3, B    '3バイト目から読み込む
Else
    B = ""
End If
Close F

Set rngCells = Sheet1.Range("A1")
For Each LineData In Split(B, vbCrLf, , vbBinaryCompare)
    Set R = rngCells
    For Each ColumnData In Split(LineData, vbTab, , vbBinaryCompare)
        R.Value = ColumnData
        Set R = R.Offset(0, 1)
    Next
    Set R = Nothing
    Set rngCells = rngCells.Offset(1)
Next
Set rngCells = Nothing


魔界の仮面弁士  2003-11-07 20:44:01  No: 80272

# おっと、すれ違い。

> できました。
おぉ、うまく行ったのですね。良かった良かった。(^^;

> あとでまとめてもろもろ報告いたします。
楽しみに待ってます。


ちい  2003-11-07 22:07:31  No: 80273

>魔界の仮面弁士様
いろいろ詳しく教えていただきありがとうございます。
本当に助かりました。

> Stm.Charset = "UNICODE"
で「リトルエンディアン(BOM付き)のUTF-16」をVBAに読み込むことができました。
これにて
「Unicodeで書かれたハングル文字のテキストファイルをVBAで読み込みセルに描画するには?」
は、解決いたしました。

で、解決したのですが、もう少し甘えさせてください。

> Stm.Charset = "unicode"     ' 文字コード
> Stm.LineSeparator = adCRLF  '(←Const adCRLF = -1)

なのですが、
Charset や LineSeparator に指定できるものというのは、
いったいどのようにして知ることができるのでしょうか?

例えば、
今回のソースを流用して日本語のShiftJIS(改行は0x0Aのみ)のファイルを読み込みたい場合、
Charset は "ShiftJIS", "Shift-JIS", "Shift_JIS", "SJIS"
などが考えられますよね?
そのうちのどれが正解なのか(←いろいろ試したら"Shift_JIS"が正解でした)とか知りたい場合や
LineSeparator は0x0Aの場合、数値でいうところの-1ではなく何にあたるのか?(←まだわかりません。)
等です。

そもそも、"ADODB.Stream"のメソッドやプロパティーにはどんなものがあるのか等もよくわかってません。


魔界の仮面弁士  2003-11-07 23:02:38  No: 80274

> いろいろ詳しく教えていただきありがとうございます。
本題から脱線して、一部Unicode講座になっちゃいましたけれどね。(汗

> Stm.Charset = "unicode"     ' 文字コード
> Stm.LineSeparator = adCRLF  '(←Const adCRLF = -1)
これら "Unicode" と adCRLF は、それぞれのプロパティの既定値です。
なので今回の場合は、
    Set Stm = CreateObject("ADODB.Stream")
    Stm.Open
    Stm.LoadFromFile SourceFilePath
だけでもOKだったりします。
(まぁ、一応指定しておいた方が良いでしょうけれどね)

> Charset は "ShiftJIS", "Shift-JIS", "Shift_JIS", "SJIS"
当方の環境では、
  NG: "ShiftJIS"
  OK: "Shift-JIS"
  OK: "Shift_JIS"
  OK: "X-SJIS"
  OK: "SJIS"
でした。ただ、HTMLでの文字コード表記で利用される
  <META Http-equiv="Content-Type" Content="text/html;charset=Shift_JIS"> 
に合わせる意味で、"Shift_JIS"に合わせて置くのが無難でしょう。

> Charset や LineSeparator に指定できるものというのは、
これらは、ADO(バージョン2.5以上)のヘルプで調べる事ができます。

まず LineSeparator ですが、これは adCR (=13)、adCRLF (=-1)、adLF (=10) の3つのうち、いずれかを指定します。
それぞれが何を意味する値かは、説明不要ですよね。

なお、参照設定で Microsoft ActiveX Data Object の 2.5(またはそれ以降)をチェックしておくと、adCRLF などの値を自分で定義する必要は無くなります。
(ちなみに、C:\Program Files\Common Files\System\ado\adovbs.incに、VBScript用の定数定義一覧があります)

それと、Charset ですが、これは以下のようにヘルプに記載されています。

*************
利用できる値は、インターネット文字セット文字列としてインターフェイスで渡すことのできる通常の文字列です ("iso-8859-1"、"Windows-1252" など)。システムが認識できる文字セット文字列の一覧については、Windows レジストリの HKEY_CLASSES_ROOT\MIME\Database\Charset のサブキーを参照してください。
*************

例えば、先述のシフトJISについて、上記レジストリキーを参照すると、

[HKEY_CLASSES_ROOT\MIME\Database\Charset\shift_jis]
"Codepage"=dword:000003a4
"InternetEncoding"=dword:000003a4

[HKEY_CLASSES_ROOT\MIME\Database\Charset\csShiftJIS]
"AliasForCharset"="shift_jis"

[HKEY_CLASSES_ROOT\MIME\Database\Charset\csWindows31J]
"AliasForCharset"="shift_jis"

[HKEY_CLASSES_ROOT\MIME\Database\Charset\ms_Kanji]
"AliasForCharset"="shift_jis"

[HKEY_CLASSES_ROOT\MIME\Database\Charset\shift-jis]
"AliasForCharset"="shift_jis"

[HKEY_CLASSES_ROOT\MIME\Database\Charset\x-ms-cp932]
"AliasForCharset"="shift_jis"

[HKEY_CLASSES_ROOT\MIME\Database\Charset\x-sjis]
"AliasForCharset"="shift_jis"

のように書かれており、基本的には"shift_jis"であり、その他の表記は
shift_jisの別名(エイリアス)に過ぎないことが推測できます。

ただし、指定できる値は、上記のレジストリキーだけで管理されているというわけでは無さそうです。
実際、私の環境では、上記に登録されていない "utf-16" という値を使う事ができますので…。


ちい  2003-11-10 22:28:05  No: 80275

そもそもの質問で申し訳ないのですが、
ADO
とは何でしょうか?


ちい  2003-11-11 03:33:28  No: 80276

http://www.microsoft.com/japan/msdn/library/default.asp?url=/japan/msdn/library/ja/jpado260/htm/mdmscadoapireference.asp
を見たらなんとなくこういうものなのかという感じになりました。

今度は、ファイルを作成したいのですが、

Sub CreateFile()
    Dim Stm As Object
    Dim SourceFilePath As String
    Const adCRLF = -1
    Const adSaveCreateOverWrite = 2
    Const adWriteLine = 1
    Const adTypeText = 2
    
    SourceFilePath = "C:\〜\TEST.TXT"
    Set Stm = CreateObject("ADODB.Stream")
    Stm.Open
    Stm.Charset = "UNICODE" 
    Stm.Type = adTypeText
    Stm.LineSeparator = adCRLF
    Stm.SaveToFile SourceFilePath, adSaveCreateOverWrite
    
    Stm.WriteText "こんにちは", adWriteLine 
    Stm.Close
    Set Stm = Nothing
    
End Sub

とやってみたのですが、
空(ファイルサイズ0Byte)のファイルが作成されてしまい、
”こんにちは”が書き出されていません。

ご教授願います。


ちい  2003-11-11 05:08:28  No: 80277

すいません。

Sub CreateFile()
    Dim Stm As Object
    Dim SourceFilePath As String
    Const adCRLF = -1
    Const adSaveCreateOverWrite = 2
    Const adWriteLine = 1
    Const adTypeText = 2
    
    SourceFilePath = "C:\〜\TEST.TXT"
    Set Stm = CreateObject("ADODB.Stream")
    Stm.Open
    Stm.Charset = "UNICODE" 
    Stm.Type = adTypeText
    Stm.LineSeparator = adCRLF
'    ↓ここじゃない!
'    Stm.SaveToFile SourceFilePath, adSaveCreateOverWrite
    
    Stm.WriteText "こんにちは", adWriteLine 

'  ↓ここ!
    Stm.SaveToFile SourceFilePath, adSaveCreateOverWrite

    Stm.Close
    Set Stm = Nothing
    
End Sub

にしたらできました。
勘違いしてました。

ひとまずこれで全て解決いたしました。
魔界の仮面弁士様
ありがとうございました。


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

※Google reCAPTCHA認証からCloudflare Turnstile認証へ変更しました。






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