VBAにてセルに入力した文字列を"EUC"でバイナリデータとして出力するには

解決


ILL  2007-11-09 10:00:01  No: 99670

現在Excelにて、セルに文字列を入力して、文字コードをEUCに変換して
バイナリ形式でデータを出力する処理を行っているのですがどうにもうまくいかない部分があるので質問させて頂きたいのですが、

現在行っている主な処理は

1)任意の文字列をセルに入力する

2)その文字列をADODB.StreamのWriteTextでEUCを指定して、全てテキストファイルとして出力。

3)出力されたテキストファイルをFileStreamObjectで別のセルに出力。

4)3)で出力されたセルの文字列(EUCに変換されているもの)をバイナリ形式でファイルに書き込み出力。

といった流れで処理を行っているのですが、出力されたバイナリデータをみると、正しくEUC形式で変換されているものといないものが出てしまっています。

「る」などが正しく表示されなかったりするのですが、原因がどこにあるのかが掴めないでいます。

つたない質問の仕方で申し訳ないのですが、ご意見いただければと思います。宜しくお願いします。


魔界の仮面弁士  2007-11-09 12:36:00  No: 99671

> ご意見いただければと思います。
コードや実際のデータ等を見ないと何とも…。

> 1)任意の文字列をセルに入力する
EUC の範囲外の文字を入力しているわけでは無いのですよね?

> 2)その文字列をADODB.StreamのWriteTextでEUCを指定して、全てテキストファイルとして出力。
Charset プロパティは、"EUC-JP" ですか?

> 3)出力されたテキストファイルをFileStreamObjectで別のセルに出力。
なぜここで FSO が出てきているのでしょうか?
FSO をファイルコピーの目的で利用しているのなれば良いですが、
TextStream を利用する事が目的だとすれば、ここで使うべきではありません。
TextStream は、EUC ファイルに対応していませんので、データが破損します。

> 4)3)で出力されたセルの文字列(EUCに変換されているもの)をバイナリ形式でファイルに書き込み出力。
文字列を EUC-JP エンコードのバイナリとする、という所は分かるのですが、
EUC に変換されている文字列、という言葉の意味が良く分かりません。
ここでいう EUC というのは、文字集合(charcter set)の意味ですか?
それとも、バイナリへの符号化(encoding)の意味ですか?


ILL  2007-11-09 20:40:06  No: 99672

お返事ありがとうございます。

>EUC の範囲外の文字を入力しているわけでは無いのですよね?
これはないと思います。

>Charset プロパティは、"EUC-JP" ですか?
"euc-jp"を指定しています。

>文字集合(charcter set)の意味ですか?
>それとも、バイナリへの符号化(encoding)の意味ですか?

後者の方です。

またいくつか誤りや補足があったので記載します。

追2)adodbstream.WriteText (セル) & vbCrLf

    といった形でセル毎に改行して一行ずつファイルに出力しています。

×3)出力されたテキストファイルをFileStreamObjectで別のセルに出力。

○3)Set stream = CreateObject("ADODB.Stream")
    Set stream2 = CreateObject("ADODB.Stream")
    
    stream.Open
    stream2.Open
    
    stream.Type = 2
    
    stream.charset = "shift_jis"
    stream2.charset = "euc-jp"
    
    stream.LoadFromFile "2)で出力したテキストファイル"
    
    'コピー
    stream.CopyTo stream2
    
    stream2.Position = 0
    
    stream2.LineSeparator = 10
    
    Do While Not stream2.EOS

  一行ずつデータをセルへもどしています。

    loop

○4)ここで、3)でセルへ入れたデータをFSOでバイナリ形式で出力しています。

実際に「春夏秋冬」とセルにいれ一連の処理を行うと、きちんとEUCに変換され、
バイナリへエンコードされているのですが
「はるなつあきふゆ」等と入れると

"は〓E覆弔△④○〓"  (○は読めませんでした)

となって正しい結果が得られていません。

他に突っ込みどころ等ありましたら宜しくお願いします。


魔界の仮面弁士  2007-11-09 22:03:35  No: 99673

> ○3)Set stream = CreateObject("ADODB.Stream")
>      Set stream2 = CreateObject("ADODB.Stream")
>      stream.charset = "shift_jis"
>      stream2.charset = "euc-jp"
前者が、Shift_JIS ストリーム、
後者が、EUC-JP ストリームですね。
(stream2 などではなく、意味のある変数名にした方が混乱が少ないですよ)

>    stream.Type = 2
2というと…… adTypeText の事ですね。
(マジックナンバーでは、コードの意図が分かりにくくなります。定数を使いましょう)

>    stream.LoadFromFile "2)で出力したテキストファイル"
テキストファイルは、EUC-JP でエンコードされたバイナリなのですよね?
それを、Shift_JIS のストリームである stream に読み込んでしまっては駄目でしょう。文字化けしますよ。

読み込むなら、EUC-JP ストリームである stream2 の方でしょう。
これなら、手順(2)の EUC ファイル出力用に使った adodbstream と同じ内容になるはず。

> ○4)ここで、3)でセルへ入れたデータをFSOでバイナリ形式で出力しています。
FSO というのは、Scripting.FileSystemObject の事ですか? それとも別のもの? (ADODB.Stream?)
(FileSystemObject の事だとしたら、バイナリ出力機能は備えていないはず)

それはそうとして:

>>> セルに文字列を入力して、文字コードをEUCに変換して
>>> バイナリ形式でデータを出力する処理を行っている

そもそも、『バイナリ形式でデータを出力する』というのが、どういう処理を意味しているのかがわかりません。

ファイル化という意味であれば、
  (1) セルの値を String 変数に受け取る。
  (2) それを、EUC-JP な ADODB.Stream に WriteText する。
  (3) SaveToFile メソッドでファイルに保存。
となるだけですし、バイト配列化という意味なら、
  (1) セルの値を String 変数に受け取る。
  (2) それを、EUC-JP な ADODB.Stream に WriteText する。
  (3) .Position = 0 に戻してから、.Type = adTypeBinary に変更。
  (4) buf = .Read(adReadAll) でバイト配列化。
となりますから、Stream オブジェクトは 1 つあれば事足りるでしょうし、
Shift_JIS ストリームの出番は全く無いと思うのですが…。


ILL  2007-11-10 02:34:01  No: 99674

>stream2 などではなく、意味のある変数名にした方が混乱が少ないですよ
>マジックナンバーでは、コードの意図が分かりにくくなります。定数を使いましょう

確かに…。質問する上でも判り辛い書き方になってしまっていて申し訳ありません。

>テキストファイルは、EUC-JP でエンコードされたバイナリなのですよね?
>それを、Shift_JIS のストリームである stream に読み込んでしまっては駄目でしょう。文字化けしますよ。

EUC-JP にエンコードしたテキストでした。
またShift-JISのストリームに読み込んでいたのは、EUC-JPのバイトデータ?(バイナリエディタ等で見た文字)を
そのままセルに出したかったから行っていたのですが、どうにもここも具合が悪いみたいですね。

>FSO というのは、Scripting.FileSystemObject の事ですか? それとも別のもの? (ADODB.Stream?)
>(FileSystemObject の事だとしたら、バイナリ出力機能は備えていないはず)

申し訳ありません。見間違いをしていまして、FileSystemObjectは使用していませんでした。
Open ""ファイルネーム For Binary Lock Read Write As #ファイルナンバー
でバイナリでファイルに書き込んでいます。

>そもそも、『バイナリ形式でデータを出力する』というのが、どういう処理を意味しているのかがわかりません。

データをEUCにエンコードし、バイト単位で書き込みファイル化したいのですが、
ここで出力したデータを他のアプリケーションで構造体として読み込むので

1)2バイト以上のデータはエンディアンを変換して書き込み。
2)数値のデータと文字列のデータがあり、文字列のデータは固定長分のバイト数を確保し
文字列で埋まらなかった部分は0でパディングを行う。

という形で処理を作ろうとしています。

EUCにエンコードせずに、上記の条件を満たして出力することは可能になったので
変換した物をそれに合わせて作ろうとしているところです。

ですのでできれば
Open ""ファイルネーム For Binary Lock Read Write As #ファイルナンバー

でファイルを開き、そこへセルのデータを書き込む形にしたいと思い
EUCにエンコードしたものをセルにもどし、そのままセルにはいっている文字列(EUCエンコード済みなので記号の組み合わせみたいになっていますが)
の内容をバイト単位でファイルに書き込もうとしています。

またわかりづらい書き込み方で申し訳ありません。


魔界の仮面弁士  2007-11-10 03:48:13  No: 99675

> またShift-JISのストリームに読み込んでいたのは、EUC-JPのバイトデータ?(バイナリエディタ等で見た文字)を
> そのままセルに出したかったから行っていたのですが、
表示したいのは、00"〜"FF" の 16進数の事でしょうか?
だとすれば、こんな感じで変換できます。

  Function ToHexString(ByRef bin() As Byte) As String
    With CreateObject("Microsoft.XMLDOM").createElement("h")
      .DataType = "bin.hex"
      .NodeTypedValue = bin
      ToHexString = .Text
    End With
  End Function

それとも、16進数ではなく、バイナリエディタ上で「化けて見える文字列」のことでしょうか?
だとすると、セルから得たデータを、わざわざ化けさせてセルに書き戻す意図が分かりませんが、
提示されたコードの場合には、
    EUC バイナリを Shift_JIS 扱いでデコードした化け文字列
を得ているわけではなく、
    EUC バイナリを Shift_JIS 扱いでデコードした化け文字列を、さらに Unicode 変換した結果
を得ていますから、バイナリエディタと同じ結果にはならないでしょうね。

> どうにもここも具合が悪いみたいですね。
「バイナリエディタで見た文字列」をクリップボードにコピーして、
それをメモ帳に貼って保存しても、元のデータは復元されませんよね。
(クリップボードにコピーした時点で、データは破損していますから)

それと同じ事で、誤ったストリームを中継させた時点で、データは破損してしまうでしょう。
しかも、化けたデータをさらに Unicode 変換するのですから、破損はさらに進むかと。
(ReadText および WriteText で扱われる文字列は、Unicode のバイナリです)

> データをEUCにエンコードし、バイト単位で書き込みファイル化したいのですが、
ファイル化なら、EUC な Stream を SaveToFile するだけですよね。

> Open ""ファイルネーム For Binary Lock Read Write As #ファイルナンバー
Put # ステートメントを使っているということですか?
だとすれば、「EUC なテキストファイル」を作る必要さえ無いかと。

バイナリ化したデータ自体が欲しいなら、その EUC な Streamに対して、
  stmEUC.Position = 0                '先頭に戻してから
  stmEUC.Type = adTypeBinary         'バイナリモードに切り替えて、
  binData = stmEUC.Read(adReadAll)   'バイナリを一括取得
とすれば得られます。

データ全部ではなく、一部だけを取り出したいなら、その EUC な Streamに対して、
  stmEUC.Position = 0         '先頭に戻してから
  stmEUC.Type = adTypeBinary  'バイナリモードに切り替えて、
  stmEUC.Position = 12        '読み込みたいカーソル位置に移動し、
  binData = stmEUC.Read(34)   'そこから、任意のバイト数を取り出す
とすることができます。


ILL  2007-11-10 05:45:46  No: 99676

>表示したいのは、00"〜"FF" の 16進数の事でしょうか?
>だとすれば、こんな感じで変換できます。

>  Function ToHexString(ByRef bin() As Byte) As String
>    With CreateObject("Microsoft.XMLDOM").createElement("h")
>      .DataType = "bin.hex"
>      .NodeTypedValue = bin
>      ToHexString = .Text
>    End With
>  End Function
>それとも、16進数ではなく、バイナリエディタ上で「化けて見える
>文字列」のことでしょうか?
>だとすると、セルから得たデータを、わざわざ化けさせてセルに
>書き戻す意図が分かりませんが、
>提示されたコードの場合には、
>    EUC バイナリを Shift_JIS 扱いでデコードした化け文字列
>を得ているわけではなく、
>    EUC バイナリを Shift_JIS 扱いでデコードした化け文字列を、
>さらに Unicode 変換した結果
>を得ていますから、バイナリエディタと同じ結果にはならないでしょうね。

後者の化けて見える文字列の方を、なんとかセルに文字列で
入れようとしていたのですが、話を聞く限り、間違ったストリームを経由しなくても正しくセルに入れることは出来ないみたいですね。

'バイトデータ書き込み用
Public Function write_byte(off As Long, data As Integer)
    Dim bytedata As Byte
    If data < 0 Then
        bytedata = 255 - (data + 1)
    Else
        bytedata = data
    End If
        Put FileNum, off, bytedata  
    off = off + 1
End Function

'文字列書き込み用
Public Function write_string(off As Long, str As String)
    Put FileNum, off, str
    off = off + LenB(str)
    Call write_byte(off, 0)
End Function

'2バイトデータ書き込み用
Public Function write_short(off As Long, data As Integer)
    Put FileNum, off, data
    off = off + 2
End Function

'パディング用
Public Function pading(off As Long, pad_num As Integer)
    Dim i As Integer
    For i = 0 To pad_num - 1
        Call write_byte(off, 0)
    Next
End Function

またエンディアン変換用の関数もこれと合わせて使える形で作ってあったので
、できればこれを使うために、化けて見える文字列の方を、なんとかセルに入れられないかと試行錯誤していました。

>バイナリ化したデータ自体が欲しいなら、その EUC な Streamに対して、
>  stmEUC.Position = 0                '先頭に戻してから
>  stmEUC.Type = adTypeBinary         'バイナリモードに切り替えて、
>  binData = stmEUC.Read(adReadAll)   'バイナリを一括取得
>とすれば得られます。

>データ全部ではなく、一部だけを取り出したいなら、
>その EUC な Streamに対して、
>  stmEUC.Position = 0         '先頭に戻してから
>  stmEUC.Type = adTypeBinary  'バイナリモードに切り替えて、
>  stmEUC.Position = 12        '読み込みたいカーソル位置に移動し、
>  binData = stmEUC.Read(34)   'そこから、任意のバイト数を取り出す
>とすることができます。

これは

dim binData() as BYTE

のように宣言して、そこに一括して入れるということでしょうか?
これですと、データ間に詰め物をしたい時などに

例えば20byteの文字列用に確保してあるバッファに
8byte分のデータをいれ、のこり12byteに0をいれて詰めたい場合

1)stmEUC.writetext セルに入っている文字列(4文字)

2)stmEUC.writetext 12byte分"0"をいれる?

3)全て書き込み終わったら、
stmEUC.Position = 0
stmEUC.Type = adTypeBinary
binData = stmEUC.Read(adReadAll)

のように書き込む段階で詰め物をしておくのでしょうか?

もしくは必要なデータだけ書き込み
stmEUC.Position = stmEUC.Position + 12

みたいな形でデータを送るのでしょうか?
一応試してみて、エラーになりうまくいかなかったのですが
もしかしたら他の部分に問題があったかもしれないので…。

幾重にもご丁寧な回答に感謝しています。


魔界の仮面弁士  2007-11-10 07:01:03  No: 99677

> 後者の化けて見える文字列の方を、なんとかセルに文字列で
同様の見た目を得られるか否かは、そのバイナリエディタの表示部が、
どのように実装されているかにもよると思いますよ。

・エディタのウィンドウは ANSI か Unicode か?
・文字列部は、TextBox (EditBox)として文字列を割り当てているのか?
・それとも、文字列を「描画」させているのか?

> dim binData() as BYTE
> のように宣言して、
そういう事です。(大文字/小文字の使い分けに違和感を感じますが)

> そこに一括して入れるということでしょうか?
全体一括でも一部だけでも良いですが、ストリームの一部を別ファイルに出力するために、
  eucStream.Position = [ストリーム内の読み込み位置]
  binData = eucStream.Read([サイズ])
  Put [#ファイルナンバー], [ファイル書き込み先の位置], binData
といった処理が行えますね。

> 例えば20byteの文字列用に確保してあるバッファに
> 8byte分のデータをいれ、のこり12byteに0をいれて詰めたい場合

ここでいう「確保されているバッファ」とは、EUC ストリームとは別の『バイト配列』の事ですか?
もしそうなら、たとえば、

  Dim binData() As Byte
  eucStream.Position = 0
  eucStream.Type = adTypeBinary
  eucStream.Position = 0    '読み込み位置
  binData = eucStream.Read(8)
  ReDim Preserve binData(19)

のように書けるかと。

それとも、『EUC ストリームそのもの』の事でしょうか?
もしそうなら、たとえば…。

  '20バイトの 0x00 を書き込んでおく
  Set eucStream = New ADODB.Stream
  eucStream.Open
  eucStream.Type = adTypeText
  eucStream.Charset = "ASCII"
  eucStream.WriteText String(20, 0)
  '先頭4文字(8バイト)をEUC文字列で上書き
  eucStream.Position = 0
  eucStream.Charset = "EUC-JP"
  eucStream.WriteText "さんぷる"  'これで完了
  'ストリーム内容の確認方法その1: ファイルに出力
  eucStream.SaveToFile "C:\sample.bin", adSaveCreateOverWrite
  'ストリーム内容の確認方法その2: バイト配列に受け取る
  Dim result() As Byte
  eucStream.Position = 0
  eucStream.Type = adTypeBinary
  result = eucStream.Read(adReadAll)


ILL  2007-11-11 23:30:05  No: 99678

>全体一括でも一部だけでも良いですが、ストリームの一部を別ファイルに>出力するために、
>  eucStream.Position = [ストリーム内の読み込み位置]
>  binData = eucStream.Read([サイズ])
>  Put [#ファイルナンバー], [ファイル書き込み先の位置], binData
>といった処理が行えますね。

>それとも、『EUC ストリームそのもの』の事でしょうか?
>もしそうなら、たとえば…。

>  '20バイトの 0x00 を書き込んでおく
>  Set eucStream = New ADODB.Stream
>  eucStream.Open
>  eucStream.Type = adTypeText
>  eucStream.Charset = "ASCII"
>  eucStream.WriteText String(20, 0)
>  '先頭4文字(8バイト)をEUC文字列で上書き
>  eucStream.Position = 0
>  eucStream.Charset = "EUC-JP"
>  eucStream.WriteText "さんぷる"  'これで完了
>  'ストリーム内容の確認方法その1: ファイルに出力
>  eucStream.SaveToFile "C:\sample.bin", adSaveCreateOverWrite
>  'ストリーム内容の確認方法その2: バイト配列に受け取る
>  Dim result() As Byte
>  eucStream.Position = 0
>  eucStream.Type = adTypeBinary
>  result = eucStream.Read(adReadAll)

この処理を参考に、現在作成している環境に合わせて思い通りのデータを作成する事ができました。

要領を得ない質問の仕方しか出来ずに申し訳ありませんでしたが
最後までお付き合い頂き、真にありがとうございました。


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

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






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