[[20120603002950]] 『数値表現変換について』(フェンダー) ページの最後に飛ぶ

[ 初めての方へ | 一覧(最新更新順) | 全文検索 | 過去ログ ]

 

『数値表現変換について』(フェンダー)

下記にプログラムで
パック10進数からゾーン10進数に変換出来るように
なってます。
これをバイナリからゾーンに
変換するのにうまくいきません。
ちなみに元のパックは5バイトで
変換後は10バイトから1バイト減らしてます。

達成 パック→5バイトをゾーン9バイトに変換
j(1 To 5) As Byte
In = In + 5
Out = OutPOS + 9
プログラム内容はすべて記載してません。

課題  バイナリ→2バイトをゾーン4バイトに変換

どなたかバイナリに詳しいかたアドバイスください。
よろしくお願いいたします。

Dim ArrySize As Long
Dim dSize As Long
Dim 符号 As Byte
Dim up As Long
Dim sup As String
Dim down As Long
Dim sdown As String
Dim tmp As String

  ArrySize = UBound(B1)
  dSize = ArrySize * 2 - 1

  tmp = ""
  For i = 1 To ArrySize
'  For Each i In B1
    ArrySize = ArrySize + 1

    up = Int(B1(i) / 16)
    sup = D2H(CInt(up))

    down = B1(i) Mod 16
    sdown = D2H(CInt(down))

    tmp = tmp & CStr(sup) & CStr(sdown)
  Next i
  tmp = Mid(tmp, 1, dSize)

エクセル2010  フェンダー


 オフコン時代には、私も使っていたデータ型です。
 私等は、このゾーン10進数は、アンパック型と学びましたが・・・。

 それはさておき

 >バイナリ→2バイトをゾーン4バイトに変換

 このゾーン4バイトとは、4バイトのゾーン10進数に変換
 ということですか?

 そうだとしたら、まず、例を示してください。

 バイナリー &h100  ----->ゾーン4バイトでは、???
 バイナリー &h7fff  ----->ゾーン4バイトでは、??? 

 ちがうのなら、この辺りを説明してください

 ichinose


 全面的に寝ぼけていたようなのでいったん書き込みを削除します。失礼。

 顔を洗って、仕切り直し。

 1バイトずつに区切ったものを x とすると、その x は 0 〜 255 までの値なので以下のようにすると
 それぞれの上位4ビットの値と下位4ビットの値が取り出せないかなぁ?
 (まだ寝ぼけているかもしれないけど)
 その1バイトずつに区切った、最終バイトの下位4ビットは 12,13,15 あたりになるんだろうね。

 Sub test()
    Dim x

    x = &H89

    MsgBox Int(x / 16)
    MsgBox x - Int(x / 16) * 16
    'あるいは
    MsgBox x And &HF

    x = &H5F

    MsgBox Int(x / 16)
    MsgBox x - Int(x / 16) * 16
    'あるいは
    MsgBox x And &HF

 End Sub

 (ぶらっと)


アドバイスありがとうございます。
ゾーン10進数とアンパック10進数は共通進数と認識してます。

>このゾーン4バイトとは、4バイトのゾーン10進数に変換

 ということですか?

そうです。4バイトのゾーン10進数になります。

私も知識不足なのですが例えば
080Cのようなデータにあったとしたら

例 バイナリ10進数からゾーン10進数の変換

改造前  080C 
改造後   8C
私の認識が合っていればこのようなイメージです。
考え方がおかしければご指摘ください。

(フェンダー)


 >そうです。4バイトのゾーン10進数になります。
 だとしたら・・・・、

 >改造前  080C  
 >改造後   8C

 少なくともこれは、違います。

http://www.jtw.zaq.ne.jp/kayakaya/new/kihon/text/zonepack.htm

 上記にパック・アンパック(ゾーン10進数)
 の説明があります。

 これからすると、アンパック型の下位ハーフバイトには、0〜9しか入らないのです。

 これからすると、&H8Cとはなりませんね!!

 ここを良く調べてください。

 実は、そうなるとまだ問題があります。

 2バイトバイナリデータって、つまりVBAでいうInteger型の変数です。

 これの値の許容範囲って いくつだと思いますか?

 4バイトのゾーン10進数、4バイトでは収まりきらないと思いますよ!!

 調べてみてください。

 ichinose@今から、もろこし焼き

 


 今、外出から帰って、あらたてて自分がアップしたレスをみると、随分滑ってましたなぁ。
 無視してください。

 ところで バイナリー10進 という言葉自体、私にはよくわからないものなので
 イメージしているのとは異なっているのかもしれないんだけど、大昔我々がホストで使っていたものは
 データを16進であらわすと、たとえば12345なら

 Zoned Decimal   F1F2F3F4F5 (コードはIBMホスト用) の5Byes
 Packed Decimal  12345C の 3Bytes

 で上記 12345C を ichinoseさんもいわれた【UnPack】を行うと F1F2F3F4C5 と、最後の桁のゾーン部分が
 IBM では F ではなく C になるので、Zoned Decimal において Zone部分が F ではなく C も正の数と扱う。

 で、今回のスレ主さんのテーマは、今もある、そういったPaced Decimal のデータをどこかから、
 エクセルVBAで扱うことのできる媒体にして取り込み、それをVBAで Zoned Decimal 形式に
 変換することだと思っていたんだけど、そうじゃなかったのかな?

 (ぶらっと)


 私がオフコン時代に扱っていたアンパック型データは、前回のリンク先で示されるデータ構造と
 殆ど同じでした。殆どと言うのは、符号部分は、うろ覚えなので、なんせ25年前ですから・・・。

 数字の12345をアンパック形式で表すと・・・、

 31323334c5  という5バイトになります。符号部を除けば、文字列の数字ですから、
 実は、殆ど私は、使っていませんでした。

 本当は、入力データに対し、出力データを例を交えて説明された方がよいです。

 >バイナリ10進数からゾーン10進数の変換

 私の解釈は・・・。

 2バイトデータに 80cだとすると・・・、

 Sub test1()
    Dim x As Integer
    x = &H80C
    MsgBox x
 End Sub

 上記コードを実行すれば Hexの80Cが 10進数のい2060だと分かります。

 よってこれをアンパック型に変換すれば(私の扱っていた形式では)、

 323036c0  の4バイトになる  と言う解釈でした。

 が、こうなると2バイトデータは、正の数の限界は、32767ですから、4バイトでは、足りない
 と言うことだったのです。

 まずここの入力データと出力データをはっきりさせる事ですよ。

 はっきりすれば、パック形式からアンパック形式の変換が出来た方ですから、
 たぶん、御自分で解決できることだと思います。

 ichinose@焼きもうろこし5本で満腹


>エクセルVBAで扱うことのできる媒体にして取り込み、それをVBAで Zoned Decimal 形式に
 変換することだと思っていたんだけど、そうじゃなかったのかな?

ほかには下記のようなプログラムはVBAで組んでます。
1_16進数で入力された数字を、0〜Fで返す
2_ UNICODE文字列をUTF8文字列に変換する
3_ UNICODE文字列をEBCDIC文字列に変換する
文字コード2バイト(KEIS)1バイト(EBCDIC)文字が
混在したデータに対してパック型・バイナリ型の領域を含んだ
データをバイナリ入出力して
それをCSV変換してデータとして扱ってます。

現状のプログラムだとパック10進数領域の部分は文字と
認識されるのですが
バイナリ領域が文字化けをおこしてます。
ちなみにCells関数でバイナリ入出力する際
パック型・バイナリ型両方ともに、セルには
データとしてほしい文字はゲットできてますが
バイナリデータ自体のプログラムがだめなようです。

(フェンダー)


ichinose@焼きもうろこし5本で満腹さん

>本当は、入力データに対し、出力データを例を交えて説明された方がよいです。

現在データ自体がありませんので
またあらためてご説明させていただきます。
別の視点からのヒントのようなものから開発作業が進むと思われますので
ちょっとした情報でも助かります。
長期戦になりそうですがよろしくお願いします。

(フェンダー)


 テーマとははずれるけど、アンパックされた数字のゾーン部分、私がアップしたのはコメントしたように
 IBMホスト等のEBCDICコード体系で、一方 ichinoseさんのご紹介(ゾーン部分が 3)はASCII系。
 懐かしくなってコメントアップ。失礼。

 (ぶらっと)

途中報告をさせて頂きます。
冒頭で話しました
コードで

PSize = ArrySize * 2 - 1の
 - 1を取り
BSize = ArrySize * 2
のフラッグを立て
入出力しましたら
文字として認識するようになりました。

バイナリ10進数2byte変換前  0050
ゾーン10進数時4byte変換後  F0F0F5F0

数値にすると0060になります。
ですがこのプログラムですと

ゾーン10進数時4byte変換後の文字が003や000など
レコードによって3桁表示になってしまいます。
因みにcells関数でチェックしますと
3桁領域のところには
003Fや000Fと後ろにFが付いてくる場合があります。
これだけ解消されれば
とりあえず解決するのですが・・・

(フェンダー)


ゾーン10進数時4byte変換後のデータをチェックしてみると
4byte目に00とNULL?のようなものが付いてしまっているようです。

(フェンダー)


 >バイナリ10進数2byte変換前  0050 
 >ゾーン10進数時4byte変換後  F0F0F5F0
 10進数2byteというのは、2バイトのパック型(符号なし)で
 ゾーン10進数時4byteといのは、 アンパック型(符号なし)又は、EBCDICコードの数字

 というように考えられますが、
 >数値にすると0060

 これがわかりません。 0050 ですけどねえ・・・・、0060と言う結果が正しい結果なのですか?

 これが正しい結果なら、

 このようになる VBAコードが作成してあるなら、全部再現できるコードを提示してください。

 0050(Hex) ------------- F0F0F5F0(Hex)-----------------これが 0060(10進数)と表示される

 というコードを作ってあるんですよね?

 見せてください。

 ichinose


ご回答ありがとうございます。

>これがわかりません。 0050 ですけどねえ・・・・、0060と言う結果が正しい結果なのですか?

大変失礼いたしました。0050です。
バイナリエディタでチェックする場合、他のデータも交じってますので・・・

パック型10進数からゾーン10進数のプログラムは100%合ってます。
なぜならテストに合格しているからです。
今回はそのプログラムをちょっといじって代用しただけなので・・・
ですのでバイナリの知識が分かれば解決すると思います。

現在のバイナリ領域をゾーン領域に数値変換した部分を
テキストにした数値ですのでご確認ください。

(フェンダー)


都合上コードをすべてお見せできないのですが
○○○(1 To 2) As Byte

と各フィールドごとに分けまして

Get #InFN, InPOS, B型_1

    tmpStr = B2D2(B型_1)

    ReDim tmpByte(1 To Len(tmpStr))
    j = chg_str2byte(tmpStr, tmpByte)
  Put #OutFN, OutPOS, tmpByte
  InPOS = InPOS + 2
  OutPOS = OutPOS + 4

このようにフィールドごとに管理します。
それに加えて下記のような
コードから変換プログラムを加えます。

Function B2D2(B1() As Byte) As String

Dim ArrySize As Long
Dim dSize As Long
Dim 符号 As Byte
Dim 上位桁 As Long
Dim s上位桁 As String
Dim 下位桁 As Long
Dim s下位桁 As String
Dim tmp As String

  ArrySize = UBound(B1)
  dSize = ArrySize * 2

  tmp = ""
  For i = 1 To ArrySize

    ArrySize = ArrySize

    上位桁 = Int(B1(i) / 16)
    s上位桁 = D2H(CInt(上位桁))

    下位桁 = B1(i) Mod 16
    s下位桁 = D2H(CInt(下位桁))

    tmp = tmp & CStr(s上位桁) & CStr(s下位桁)
  Next i
  tmp = Mid(tmp, 1, dSize)

  B2D2 = tmp

End Function

あとは上位桁・下位桁を0〜Fにするイメージです。
ichinoseさんならだいたいこれでなにをしてるかお分かりになるとおもいます。

(フェンダー)


 00(10進)=00(16進)
 06(10進)=06(16進)
 80(10進)=50(16進)
 63(10進)=3F(16進)
 17(10進)=11(16進)
 現状は16進になっていますね。で、A〜Fが化けてていると。
 D2Hのコードがないので確認できませんが、D2Hからは正しい結果が戻っていますか。
 (cai)

 追加
 あと、chg_str2byteも確認。


コメントありがとうございます。
A〜Fはこんな感じで

Case 10

      D2H = "A"
    Case 11
      D2H = "B"
    Case 12
      D2H = "C"
    Case 13
      D2H = "D"
    Case 14
      D2H = "E"
    Case 15
      D2H = "F"
    Case Else
      D2H = CStr(itmp)

追加はこんな感じです。

Dim iSize As Long

  iSize = Len(uniStr)

  chg_str2byte = 1

  i = 0
  Do While (i < iSize)
    Select Case Mid(uniStr, i + 1, 1)
      Case "0"
        tmpByte(i + 1) = 240
      Case "1"
        tmpByte(i + 1) = 241
      Case "2"
        tmpByte(i + 1) = 242
      Case "3"
        tmpByte(i + 1) = 243
      Case "4"
        tmpByte(i + 1) = 244
      Case "5"
        tmpByte(i + 1) = 245
      Case "6"
        tmpByte(i + 1) = 246
      Case "7"
        tmpByte(i + 1) = 247
      Case "8"
        tmpByte(i + 1) = 248
      Case "9"
        tmpByte(i + 1) = 249
    End Select
    i = i + 1
  Loop

(フェンダー)


 断片のコードでよくわかりませんが、
 パック型10進数からの変換プログラムを元にしたとのことで、
 多分原因がわかりました。
 変換できるのは0〜9だけで、あとはうまく変換できていないのでは?
 A〜Fを変換する部分がないのでしょう。追加していますか。
 (cai)
 ああ、誤解を招きそう。
 A〜Fの部分を追加するだけではダメです。
 値の持ち方が違うので、0〜9の部分もパック型用から変更する必要があります。


 アドバイスありがとうございます。
 調べながら検証してみます。
 バイナリ型って下記の説明のように
 二進化十進表現BCD(Binary-coded decimal)
 コードのことなのでしょうか?

 文字データや10進数との対応が取りやすいように、
 10進数の0〜9に対応した4ビットの2進数で
10進数の各桁を表現する方法をBCDコード
(Binary Coded Decimal code:2進化10進コード)という。

 BCDコードでは、4ビットを単位として

 0000〜1001(2進数)までの0〜9(10進数)に対応させて、
 1011〜1111は使用しない。

 10真数の桁数に合わせて2進数の桁数も変化する可変長形式の表現である。

(フェンダー)


 それは(それが、かな)パック10進数です。
 (cai)

 >バイナリ型って下記の説明のように
 >二進化十進表現BCD(Binary-coded decimal)
 >コードのことなのでしょうか?

 あらっ、これを御存知のうえの話ではないのですか?

 もう一度、話を整理しませんか?

 質問の概要が

 >バイナリ→2バイトをゾーン4バイトに変換

 これでしたね?
 私が知りたかったのは、途中が欠けた再現できないコードではなく、

xxxという入力データに対し、プログラム実行後に YYYYYという出力結果
このデータ例を複数個、知りたかったのです。

 ただ、これがここまでの投稿ではっきりしないのです。

 最初私は、この2バイトのバイナリデータとは、VBAのInetger型のデータの事だと予想していました。
 ですから、

 >Sub test1()
 >   Dim x As Integer
 >   x = &H80C
 >   MsgBox x
 >End Sub

 >上記コードを実行すれば Hexの80Cが 10進数のい2060だと分かります。

 という記述をしました。この2060と言う数字をアンパック型又は、文字列に変換すればよい
 と言うことだったのですが・・・。

 途中、フェンダーさんから、

 >バイナリ10進数2byte変換前  0050 
 >ゾーン10進数時4byte変換後  F0F0F5F0 
 >数値にすると0060になります。
 (その後、0060は、0050だと訂正されました)

 という投稿をされましたね?

 私は、これを入力データに対し、結果として、出したいデータ例だと解釈しました。

 この例からすると、入力データである2バイトのデータって、
 2バイトのバック型のデータだと解釈できます。
 >003Fや000Fと後ろにFが付いてくる場合があります。

 これは、最後のハーフバイトがゾーンか否かの判断が必要なのだと思っていました。

 例えば

 2バイトのデータが 1230  ならば、f1f2f3f0  で1230と解釈

 2バイトのデータが 003f  ならば、f0f0f3  で003と解釈

 これは

 dim バイト(1 to 2) as byte

 バイト(1)=0
 バイト(2)=&h3F

 if バイト(2) and &h0f>9 then
  Msgbox "ゾーンあり"
 else
    msgbox "ゾーンなし"
 end if

 この程度の判断でよいかな?

 と思っていました。 

 ところが・・・・。

 現状   希望
 50     80
 50	80
 50	80
 50	80
 50	80
 50	80
 50	80
 50	80
  0	0
 50	80
 50	80
 50	80
  3	63
 11	17
  0	15
 16	22
 14	20
 13	19
  0	15
 12	18

 これが入力データに対する希望の結果なのですか?
 (3----63、0----15 これも正しいのですか?これに関しては、説明がほしいです)

 そうだとすれば、例外であげた二つを除けば、これは、最初に記述した

 >Sub test1()
 >   Dim x As Integer
 >   x = &H80C
 >   MsgBox x
 >End Sub

 この考え方と一緒です。

 入力データと出力データの関係がまだわかりません。

xxxという入力データに対し、プログラム実行後に YYYYYという出力結果
このデータ例を複数個、知りたいのです。

 ichinose


コメントありがとうございます。
そうですね。
もう一度話を整理させた方がいいですね。

>xxxという入力データに対し、プログラム実行後に YYYYYという出力結果
このデータ例を複数個、知りたかったのです。

途中が欠けた再現できないコードではなく、バイナリ入出力での実行前と実行後の値の
データ例でよろしいでしょうか??
そうであればテスト用のDATデータを作成して実行前実行後の値をすべて提示します。

(フェンダー)


例えば今のプログラムで
下記のようなテストデータ20byteを作成し
12 34 00 3F 00 04 00 50 00 10 00 00 F5 F5 F0 F7 00 09 F0 F0

バイナリ入出後は次のような結果になります。

F1 F2 F3 F4 F0 F0 F3 00 F0 F0 F0 F4 F0 F0 F5 F0 F0 F0 F1 F0 F0 F0 F0 F0 00 F5 00 F5 00 F0 00 F7 F0 F0 F0 F9 00 F0 00 F0

(フェンダー)


 それは現状のコードでの結果ですか?
 希望する結果はどうなりますか。
 (cai)

 >それは現状のコードでの結果ですか?

 前レスで記載した通り今のプログラムということになります。

 >希望する結果はどうなりますか。

 たとえばバイナリ型の0050の数値があったとします。
 10の値が16(進数)で表したとすると
 20の値が32(進数)になると思います。
 そうなると50の値の場合は80になりますよね?
 その考え方で希望する結果は計算できると思いますが
 違いますでしょうか??

 >それは(それが、かな)パック10進数です。

 指摘されて色々と調べたのですが
 二進化十進表現BCD(Binary-coded decimal)は
 バイナリではないにしろ
 パック10進数の事ではないんじゃないのでしょうか??
 私の考え方がおかしければご指摘ください。

 (フェンダー)


ちなみに過去レスで
希望数値として提示しましたが

希望数値というよりは
それが私の考えから算出した希望結果ではなくて
ユーザーからこうならなきゃいけないと
指摘された
答えになります。
0080
0080
0080
0080
0080
0080
0080
0080
0000
0080
0080
0080
0063
0017
0015
0022
0020
0019
0015
0018
0004
0000
0004
0005

(フェンダー)


 上のテストデータ20byte
 12 34 00 3F 00 04 00 50 00 10 00 00 F5 F5 F0 F7 00 09 F0 F0
 の希望する変換結果を教えてください。
 もしくは、その指摘された答えになる入力データの方を教えてください。

 BCDはこちらが参考になるでしょうか。
 -- 基礎から学ぶコンピュータ -- 《第六十号》
http://rryu.sakura.ne.jp/compfund/backnumber/compfund060.txt
 (cai)


ご回答ありがとうございます。

 >希望する変換結果を教えてください。

えっとですね。
イメージとして提示しましたが
私は把握してないかぎり
すいませんがテストケースはありえない数値も
加えていると思います。

下記のような数値が正しいです。

バイナリ型 0007  バイナリ入出力後の正しい数値 F0F0F0F7

バイナリ型 0012   バイナリ入出力後の正しい数値 F0F0F1F8

バイナリ型 0010  バイナリ入出力後の正しい数値 F0F0F1F6

バイナリ型 0020 バイナリ入出力後の正しい数値 F0F0F3F2

バイナリ型 0030 バイナリ入出力後の正しい数値 F0F0F4F8

バイナリ型 0040 バイナリ入出力後の正しい数値 F0F0F6F4

バイナリ型 0050   バイナリ入出力後の正しい数値 F0F0F8F0となっていきます。

ただしこれは提示された数値から逆算して
出した答えですので
計算ミスしている可能性もございます。
因みに二進化十進表現BCD(Binary-coded decimal)がパック10進数の事であれば
バイナリ型について詳しく記載されているサイトはご存知でしょうか??

(フェンダー)


ちなみに私が調べたサイトではBCDコードと
パック10進数は
類似したものとして説明されています。

http://mkt.blog8.fc2.com/blog-entry-12.html

(フェンダー)


 >12 34 00 3F 00 04 00 50 00 10 00 00 F5 F5 F0 F7 00 09 F0 F0

 この入力データは、実際のデータですか?

 これらを2バイトずつ取り出して、アンパック4バイトに変換する

 ということですか?

 しかも、

 >バイナリ型 0012   バイナリ入出力後の正しい数値 F0F0F1F8

 このような変換をしたいなら、20バイトの最後の2バイトである F0F0は、どのような
 結果が正しいのですか?

 バイナリ型・・・、COBOLには、BINARY(COMP)というデータ型があったと思います。

 おおよそ VBAのInteger型でよかったと思います。

 上記の正しい結果を提示してほしいのですが・・・・。

 >バイナリ型 0012   バイナリ入出力後の正しい数値 F0F0F1F8

 こんな変換でいいなら

 Sub smp1()
    Dim g0 As Long
    Dim binary(1 To 2) As Byte
    Dim resalt As Variant
    Dim dsp As String
    binary(1) = 0
    binary(2) = &H12
    resalt = cnvBtoUnp(binary())
    dsp = ""
    For g0 = 1 To 4
       dsp = dsp & Hex(resalt(g0))
    Next
    MsgBox dsp
 End Sub
 Function cnvBtoUnp(b() As Byte) As Variant
    Dim ans(1 To 4) As Byte
    Dim cnstr As String
    Dim g0 As Long
    Dim g1 As Long
    g1 = 4
    cnstr = CStr(b(1) * 2 ^ 8 + b(2))
    g1 = Len(cnstr)
    For g0 = 4 To 1 Step -1
       ans(g0) = &HF0
       If g1 > 0 Then
          ans(g0) = ans(g0) + CInt(Mid(cnstr, g1, 1))
       End If
       g1 = g1 - 1
    Next
    cnvBtoUnp = ans()
    Erase ans()
 End Function

 こんなコードで取得できます。

 ichinose


 コメントありがとうございます。
 
 COBOLに関しては全く知見がないのですが
 昔のプログラマーの方は使用されていたようですね。

 >12 34 00 3F 00 04 00 50 00 10 00 00 F5 F5 F0 F7 00 09 F0 F0

 このデータは私自身が適当に作成したデータですので
 バイナリ型でF0F0などというデータ自体ありえない数値になるかもしれないので
 忘れてください。

 >これらを2バイトずつ取り出して、アンパック4バイトに変換する

  ということですか?
 そういうことになるかと思います。

 基本的な算出方法はおそらくこうです。

 0050の場合なら10の位の5に16をかけて80となりF0F0F8F0

 0012の場合なら10の位の1に16をかけて16+2でF0F0F1F8という感じかと思われます。

 コードの提示ありがとうございます、
 ちなみに今のプログラムにバイナリ型からアンパック型
 変換できるコードを加えたいのですが

  ichinoseさんは、サンプル提示をチェックされて
 どんなことを把握してコードを書かれたのでしょうか??
 考え方が分かれば参考にしたいのですが・・・

 (フェンダー)


 >昔のプログラマーの方は使用されていたようですね。
 あはは、昔の・・か、でも、今もCOBOLで生業を立てている方もいるんですよ!!
 ちょっと前にCOBOL人口が増えているような話を聞いたことがあります。

 >どんなことを把握してコードを書かれたのでしょうか??
 基本的には、フェンダーさんの考え方と同じですよ、

 ファイルから読み込んだ2バイトのバイナリデータが Byte型変数二つに分けられて格納されています。

 この二つのByte型変数から、求めるべきバイナリデータを10進数で取得し、得られた10進数をアンパック型に
 再構築し、4バイトのデータを4つのByte型変数に分けて格納する。このデータをファイルに書き込む

 バイナリデータ  1234 (16進数)

 これが 二つのByte型変数に分けられたものが入力データです。

 dim a as byte  '上位1バイト
 dim b as byte  '下位1バイト

 a=&h12
 b=&h34

 が、この時点では

 12(16進数)
 34(16進数)

 という二つの数字が与えられているだけですよね?

 ここから 1234(16進数)という4桁の16進数を作成しなければならないのですよね?

 &H12
 &H34

 &H12の桁を上げなければなりません。

 10進数なら、12*100+34 と言う計算でよいのですが、16進数ですから、

 &h12*16^2+&h34 となります。

 前回提示のコード内で

 b(1) * 2 ^ 8 + b(2) こんな式を記述しましたが、ビット単位に桁上げすると意味的には2^8ですが、
 結果は、16^2と同じですよね? 2^8=16^2 ですから・・・。

 でも、16進で表記するのですから 16^2 か &h100と記述した方がよかったかなあ

 いずれにせよ

 &h12*16^2+&h34  この計算から 4660(10進数)と言う結果を得ることが出来ます。

 後は、アンパック型に加工すれば、よいですよね

 前回投稿のコードをちょっと変更

 Sub smp()
    Dim bb(1 To 2)
    Dim unp(1 To 4) As Byte
    Dim g0 As Long
    Dim ans As String
    bb(1) = &H12
    bb(2) = &H34
    Call cnvBtoUnp(bb(1), bb(2), unp())
    For g0 = 1 To 4
       ans = ans & Hex(unp(g0))
    Next
    MsgBox ans
 End Sub
 Sub cnvBtoUnp(ByVal upper As Byte, ByVal rower As Byte, oarray() As Byte)
    Dim cnstr As String
    Dim g0 As Long
    Dim g1 As Long
    g1 = 4
    cnstr = CStr(upper * 16^2 + rower)
    g1 = Len(cnstr)
    For g0 = 4 To 1 Step -1
       oarray(g0) = &HF0
       If g1 > 0 Then
          oarray(g0) = oarray(g0) + CInt(Mid(cnstr, g1, 1))
       End If
       g1 = g1 - 1
    Next
 End Sub

 cnvBtoUnpのインタフェースを代えました。この方が都合がよさそう
 smpを実行してみてください。

 b(1),b(2)の値を変えて試してみてください
 提示した例は  バイナリ 1234 の例です。

 ichinose


丁寧な説明どうもありがとうございます。

COBOLは詳しく分かりませんが
最近オブジェクト指向になったみたいですね。
私は現在JAVAを勉強してますが・・・

基本的には、フェンダーさんの考え方と同じですよ

ichinoseさんの知恵をお借りしたとしたら
今まで提示してきたコードで
バイナリ型の入出力を考えるとしたら
下記のコードを見直す必要があるんですかね??

Function B2D2(B1() As Byte) As String

Dim ArrySize As Long
Dim dSize As Long
Dim 符号 As Byte
Dim 上位桁 As Long
Dim s上位桁 As String
Dim 下位桁 As Long
Dim s下位桁 As String
Dim tmp As String

  ArrySize = UBound(B1)
  dSize = ArrySize * 2

  tmp = ""
  For i = 1 To ArrySize

    ArrySize = ArrySize

    上位桁 = Int(B1(i) / 16)
    s上位桁 = D2H(CInt(上位桁))

    下位桁 = B1(i) Mod 16
    s下位桁 = D2H(CInt(下位桁))

    tmp = tmp & CStr(s上位桁) & CStr(s下位桁)
  Next i
  tmp = Mid(tmp, 1, dSize)

  B2D2 = tmp

End Function

(フェンダー)


 >Function B2D2(B1() As Byte) As String

 このFunction、パック型からアンパック型に変換する関数ですよね?

 >バイナリ10進数2byte変換前  0050 
 >ゾーン10進数時4byte変換後  F0F0F5F0

 Byte型の配列に 0050(16進数)これが入力データだとすると、
 F0F0F5F0 という文字列を返すのですよね?

 でも、私には、D2H というFunctionプロシジャーを提示してくれないので、
 再現が出来ないですよね?
 想像は付きますよ、そりゃあね、でも、ここは、D2Hのコードを提示してもらわないと
 私がテストできないですから・・・。

 まっ、それはそれとして・・・・、

 >バイナリ型の入出力を考えるとしたら 
 >下記のコードを見直す必要があるんですかね??

 ここでこの御発言をされる ということは、
 未だに パックデータとバイナリデータとの違いを御理解いただいていない
 ということでしょうか?
 そうなると、

 >基本的な算出方法はおそらくこうです。 
 >0050の場合なら10の位の5に16をかけて80となりF0F0F8F0 
 >0012の場合なら10の位の1に16をかけて16+2でF0F0F1F8という感じかと思われます。

 これも違うかもしれないということになり、私が提示したコードも正しくない かもしれない

 と言うことになってしまいます。

 パックデータ とバイナリデータのデータの意味が違うのですから、
 ここから同じアンパックデータを作成する

 入力データが違うのですから、コードは違います。

 もう一度、整理しましょう

 パックデータとバイナリデータ

 1234(16進数)  同じ2バイトのデータで同じ値でも、パックデータと認識した場合と
 バイナリデータと認識した場合とでは、まるで中身の値は違ってきます。

 パックデータとして認識した場合、

 1234(16進数)  この値は、見てのとおり、10進数の 1234 と解釈します。

 16進で表現しても10以上の数字つまり、A〜Fは、出てきません。
 出てくるとしたら、形式によってですが、最後のゾーン部分だけです。

 バイナリデータと認識した場合は、

 1234 1*16^3+2*16^2+3*16^1+4 この計算式で表される数字、つまり、4660が値です。

 内部形式では、同じ16進数の1234でも型の認識の違いで 値が違うのですから、変換方法が違うのは
 当然ですよね!!

 因みにB2D2同様に バイナリデータを変換して文字列で返すなら、

 Sub smp()
    Dim bb(1 To 2)
    Dim g0 As Long
    Dim ans As String
    bb(1) = &H12
    bb(2) = &H34
    ans = cnvBtoUnp(bb(1), bb(2))
    MsgBox ans
 End Sub
 Function cnvBtoUnp(ByVal upper As Byte, ByVal rower As Byte) As String
    Dim ans As String
    Dim cnstr As String
    Dim g0 As Long
    Dim g1 As Long
    g1 = 4
    cnstr = CStr(upper * 16 ^ 2 + rower)
    g1 = Len(cnstr)
    For g0 = 4 To 1 Step -1

       If g1 > 0 Then
          ans = "F" & Mid(cnstr, g1, 1) & ans
       Else
          ans = "F0" & ans
       End If
       g1 = g1 - 1
    Next
    cnvBtoUnp = ans
 End Function

 同じ2バイトで同じ値(16進数)であってもパックデータと見なすか バイナリデータと見なすかで
 データの意味が違うことを理解してください。

 ichinose


 もしかして・・・・。

 パック型とバイナリ型の違いなど分かっているが、
 なんとか、Function B2D2(B1() As Byte) As String

 この関数を利用できないか?

 ということでしょうか?

 だとしたら、

 バイナリデータをパック型に変換して、これをB2D2の入力データにすればよいということになりますが。

 検討してみてください

 ichinose


コメントありがとうございます。

いや、下記のバイナリ領域の入出力で

Get #InFN, InPOS, バイナリ型

        tmpStr = B2D2(バイナリ型)

    ReDim tmpByte(1 To Len(tmpStr))
    j = chg_str2byte(tmpStr, tmpByte)
  Put #OutFN, OutPOS, tmpByte
  InPOS = InPOS + 2
  OutPOS = OutPOS + 4

Function B2D2(B1() As Byte) As Stringから
利用はしてます。

ichinoseさんの提示していただいたコードで
bb(1) = &H12
bb(2) = &H34

の部分の数値を変更すれば
確かに私が求めていた正確な数値が返ってきます。

因みにDH2のコードはこれですが・・・

Function D2H(itmp As Long) As String

 Select Case itmp
    Case 10
      D2H = "A"
    Case 11
      D2H = "B"
    Case 12
      D2H = "C"
    Case 13
      D2H = "D"
    Case 14
      D2H = "E"
    Case 15
      D2H = "F"
    Case Else
      D2H = CStr(itmp)
  End Select

End Function

もう一度整理してみます。

(フェンダー)


 B2D2というFunctionプロシジャーやっと、コードの提示があったので、
 試してみました。

 でも・・・、この関数、想像と違い、

 Sub testtest()
    Dim bb(1 To 2) As Byte
    bb(1) = &H12
    bb(2) = &H34
    MsgBox B2D2(bb())
 End Sub

 こんなテストプログラムで 返されるデータは 1234 という文字列です
 これは、パック型の内部データをそのまま文字列にしただけですよね?

 これを chg_str2byteという関数の入力データにしていますが、
 このchg_str2byteでは、何をしているのですか?
 このコードを提示していただかないと最終的な出力データは見えませんよね?

 見せてください

 ichinose

 


>これは、パック型の内部データをそのまま文字列にしただけですよね?

いや私が試したのはichinoseさんのコードだけで
bb(1) = &H00
bb(2) = &H05
だったり数値を変更し
結果を確認しただけです。

私の提示したものとは一切継承させてません。

Function B2D2(B1() As Byte) As String

Dim ArrySize As Long
Dim dSize As Long
Dim 符号 As Byte
Dim 上位桁 As Long
Dim s上位桁 As String
Dim 下位桁 As Long
Dim s下位桁 As String
Dim tmp As String

  ArrySize = UBound(B1)
  dSize = ArrySize * 2

  tmp = ""
  For i = 1 To ArrySize

    ArrySize = ArrySize

    上位桁 = Int(B1(i) / 16)
    s上位桁 = D2H(CInt(上位桁))

    下位桁 = B1(i) Mod 16
    s下位桁 = D2H(CInt(下位桁))

    tmp = tmp & CStr(s上位桁) & CStr(s下位桁)
  Next i
  tmp = Mid(tmp, 1, dSize)

  B2D2 = tmp

End Function

の中で
P型  dSize = ArrySize * 2 -1
B型  dSize = ArrySize * 2 
と変えているだけです。
提示希望コードは下記のコードになります。

Function chg_str2byte(uniStr As String, tmpByte() As Byte) As Long

Dim iSize As Long

  iSize = Len(uniStr)

  chg_str2byte = 1

  i = 0
  Do While (i < iSize)
    Select Case Mid(uniStr, i + 1, 1)
      Case "0"
        tmpByte(i + 1) = 240
      Case "1"
        tmpByte(i + 1) = 241
      Case "2"
        tmpByte(i + 1) = 242
      Case "3"
        tmpByte(i + 1) = 243
      Case "4"
        tmpByte(i + 1) = 244
      Case "5"
        tmpByte(i + 1) = 245
      Case "6"
        tmpByte(i + 1) = 246
      Case "7"
        tmpByte(i + 1) = 247
      Case "8"
        tmpByte(i + 1) = 248
      Case "9"
        tmpByte(i + 1) = 249
    End Select
    i = i + 1
  Loop

End Function

(フェンダー)


 >いや私が試したのはichinoseさんのコードだけで
 >私の提示したものとは一切継承させてません。

 これは、わかっています。

 ただ、これでフェンダーさんが提示された パック形式データ----->アンパック形式データへ変換
 というプログラムが私のほうで再現できました。

 >P型  dSize = ArrySize * 2 -1 
 >B型  dSize = ArrySize * 2 

 パック型用のコードは、

 >P型  dSize = ArrySize * 2 -1 

 このコードのままなのですね?

 これで正常に作動しているのなら、やはり、最後の4Bitは、符号又は、ゾーンなのですね!!

 見せていただいた chg_str2byteこれを使えば、2バイトバイナリデータ----->4バイトアンパックデータ
 の変換は、

 Sub test2()
    Dim bb(1 To 2) As Byte
    Dim rstr As String
    Dim rbyte() As Byte
    Dim ans As String
    Dim g0 As Long
    bb(1) = &H12
    bb(2) = &H34
    rstr = Format(bb(1) * 16 ^ 2 + bb(2), "0000")
    ReDim rbyte(1 To Len(rstr))
    Call chg_str2byte(rstr, rbyte())
    ans = ""
    For g0 = 1 To UBound(rbyte())
       ans = ans & Hex(rbyte(g0))
    Next
    MsgBox ans
 End Sub

 Function chg_str2byte(uniStr As String, tmpByte() As Byte) As Long

 Dim iSize As Long
 Dim i As Long
  iSize = Len(uniStr)

  chg_str2byte = 1

  i = 0
  Do While (i < iSize)
    Select Case Mid(uniStr, i + 1, 1)
      Case "0"
        tmpByte(i + 1) = 240
      Case "1"
        tmpByte(i + 1) = 241
      Case "2"
        tmpByte(i + 1) = 242
      Case "3"
        tmpByte(i + 1) = 243
      Case "4"
        tmpByte(i + 1) = 244
      Case "5"
        tmpByte(i + 1) = 245
      Case "6"
        tmpByte(i + 1) = 246
      Case "7"
        tmpByte(i + 1) = 247
      Case "8"
        tmpByte(i + 1) = 248
      Case "9"
        tmpByte(i + 1) = 249
    End Select
    i = i + 1
  Loop

 End Function

 これでも作動します。

 ichinose


ご回答ありがとうございます。

P型  dSize = ArrySize * 2 -1

 >このコードのままなのですね?

そうです。このままです。

PとBで切り替えてます。

>これでも作動します。

ichinoseさんのコードを加えて
バイナリ入出力はできるのですが
ボックスに値が表示されるだけで
入出力後の値は変わりませんね。
もう少し検証してみます。

(フェンダー)


 表示されるだけと言いますが、そのボックスの値は1234(16進数)を
 「バイナリ領域をゾーン領域に数値変換した部分を
 テキストにした数値」になっていませんか。
 上でのフェンダーさんの表現にしました。
 (cai)


なってないです。

(フェンダー)


 >ボックスに値が表示されるだけで
 >入出力後の値は変わりませんね。

 どういうことですか?
 具体例を出して、説明してください。

 それから

 >PとBで切り替えてます。

 Bの場合は、B2D2このFunctionは、必要ないですよね、これまでの話の流れでは・・・。

 ichinose


実行するとボックスに

FOFOFOFO
と表示されて入出力したデータは変わらないのです。

テストデータの中身にP型とB型が混在しているので

Pの場合→Function P2D2(B1() As Byte) As String
     dSize = ArrySize * 2-1

Bの場合→Function B2D2(B1() As Byte) As String

          dSize = ArrySize * 2

としています。

(フェンダー)

B2D2を指定しないとReDim tmpByte(1 To Len(tmpStr))が
有効範囲にありませんとなります。


 >FOFOFOFO
 >と表示されて入出力したデータは変わらないのです。
 入力データも

 FOFOFOFO ということですか?

 でも、これは、パック型データではありませんよ!!

 又、入力データにFOFOFOFO で パックデータ---->アンパックデータを実行しても
 結果は FOFOFOFO とはなりません
  

 意味が違うなら、コードや入力データをきちんと提示してください。
 フィードバックは、多少面倒でも繰り返し同じ事を表示していただけないと
 はっきり意図が伝わりません。隣にいるわけではないのですから・・・・。

 ichinose


 Sub test1()
    Dim bb(1 To 4) As Byte
    Dim tmpstr As String
    Dim tmpbyte() As Byte
    Dim ans As String
    Dim g0 As Long
    bb(1) = &HF0
    bb(2) = &HF0
    bb(3) = &HF0
    bb(4) = &HF0
    tmpstr = B2D2(bb())

    ReDim tmpbyte(1 To Len(tmpstr))
    Call chg_str2byte(tmpstr, tmpbyte())
    ans = ""
    For g0 = 1 To UBound(tmpbyte())
       ans = ans & Hex(tmpbyte(g0))
    Next
    MsgBox ans
 End Sub
 Function B2D2(B1() As Byte) As String
 Dim ArrySize As Long
 Dim dSize As Long
 Dim 符号 As Byte
 Dim 上位桁 As Long
 Dim s上位桁 As String
 Dim 下位桁 As Long
 Dim s下位桁 As String
 Dim tmp As String
  ArrySize = UBound(B1)
  dSize = ArrySize * 2 - 1
  tmp = ""
  For i = 1 To ArrySize
    ArrySize = ArrySize

    上位桁 = Int(B1(i) / 16)
    s上位桁 = D2H(CInt(上位桁))

    下位桁 = B1(i) Mod 16
    s下位桁 = D2H(CInt(下位桁))

    tmp = tmp & CStr(s上位桁) & CStr(s下位桁)
  Next i
  tmp = Mid(tmp, 1, dSize)

  B2D2 = tmp

 End Function
 Function chg_str2byte(uniStr As String, tmpbyte() As Byte) As Long

 Dim iSize As Long
 Dim i As Long
  iSize = Len(uniStr)

  chg_str2byte = 1

  i = 0
  Do While (i < iSize)
    Select Case Mid(uniStr, i + 1, 1)
      Case "0"
        tmpbyte(i + 1) = 240
      Case "1"
        tmpbyte(i + 1) = 241
      Case "2"
        tmpbyte(i + 1) = 242
      Case "3"
        tmpbyte(i + 1) = 243
      Case "4"
        tmpbyte(i + 1) = 244
      Case "5"
        tmpbyte(i + 1) = 245
      Case "6"
        tmpbyte(i + 1) = 246
      Case "7"
        tmpbyte(i + 1) = 247
      Case "8"
        tmpbyte(i + 1) = 248
      Case "9"
        tmpbyte(i + 1) = 249
    End Select
    i = i + 1
  Loop

 End Function
 Function D2H(itmp As Long) As String

 Select Case itmp
    Case 10
      D2H = "A"
    Case 11
      D2H = "B"
    Case 12
      D2H = "C"
    Case 13
      D2H = "D"
    Case 14
      D2H = "E"
    Case 15
      D2H = "F"
    Case Else
      D2H = CStr(itmp)
  End Select

 End Function

 test1は、F0F0F0F0 を入力データにしています。

 結果は、御自分で確認してください

 ichinose


test1は、F0F0F0F0 を入力データにしています。

結果は0F00F00F00となります。

ichinosesさんのコードを加えていないのがこんな感じです。
現在検証中です。

(フェンダー)


 >結果は0F00F00F00となります。
 そうですねえ、↑こうなりますねえ
 つまり、
 >FOFOFOFO 
 >と表示されて入出力したデータは変わらないのです。

 という現象が再現できません。これを説明して頂かないと・・・・、
 また、前回も記述しましたが、
 >FOFOFOFO 
 は、パック型データでも 2バイトのバイナリデータでもないですよね?
 違う形式のデータを 入力データとして、パック型(あるいは、2バイトのバイナリ)を前提にしている
 プログラムで処理しようとしても動かないのは、当然ですよね!!

 このことから、正しく入力ファイルのデータ形式を認識しているのか?

 ということが疑われます

 それから、
 >Sub Main()
 ここからプログラムの提示はして頂きましたが、これ、私がコピーしただけでは、
 作動しませんよね?
 できれば、今までの投稿を踏まえたコードを見たいのですが・・・。
 (もっとも、その時は、データや宣言していない変数の提示も必要ですが)

 > Get #InFN, InPOS, 国額
 >   Cells(iTitle, 5) = "国額"
 >   tmpStr = P2D2(国年加給金額)
 ここだけちょっと気になりました、これで正しいのですか?

 ichinose


すいません。
色々いじっていてFOFOFOFOの再現ができなくなってしまいました。

Get #InFN, InPOS, 国額
 >   Cells(iTitle, 5) = "国額"
 >   tmpStr = P2D2(国年加給金額)
 ここだけちょっと気になりました、これで正しいのですか?

ここは
Get #InFN, InPOS, 国額

  Cells(iTitle, 5) = "国額"
   tmpStr = P2D2(国額)
こうなります。

ここからは返事が遅くなります。

(フェンダー)


ここからは分かっていると思いますが
ご報告いたします。
書き忘れましたが、データを作成して頂いて
チェックする際、
セルに場所指定とハンドル名を記述しないと
動きません。
それとプロシージャは分ける必要はないと
お伝えしておきます。
フェンダー

 ん? 分かりません!!

 一連の投稿でずっと感じてきたこのかみ合わない感、これは、何かの認識が全く私とフェンダーさんとで
 全く違うからだ ということで、その認識の違いが何なのかを探ろうとしてきました。
 が、未だに分からないのです。

 Mainを以下の様にファイルの読み込み書き出しを削除して

 Sub Main()

 Dim InFileName As String
 Dim OutFileName As String

 Dim InFN As Long
 Dim OutFN As Long
 Dim InFileLen As Long
 Dim InPOS As Long
 Dim OutPOS As Long
 Dim tmpStr As String
 Dim tmpByte() As Byte

 Dim bb(1 To 2) As Byte
    Dim rstr As String
    Dim rbyte() As Byte
    Dim ans As String
    Dim g0 As Long
    bb(1) = &H12
    bb(2) = &H34
    rstr = Format(bb(1) * 16 ^ 2 + bb(2), "0000")
    ReDim rbyte(1 To Len(rstr))
    Call chg_str2byte(rstr, rbyte())
    ans = ""
    For g0 = 1 To UBound(rbyte())
       ans = ans & Hex(rbyte(g0))
    Next
    MsgBox ans
 End Sub

 これで実行すると、入力データである1234(16進数)が、
 F4F6F6F0 変換されますから、

 >データは変わりません。
 ではなく、データは変わっています。

 何を持って、何と何を比べて
 >データは変わりません。
 と仰っているのか分かりません。

 まさか

 削除したファイルの読み込みデータと書き出しデータが

  Dim bb(1 To 2) As Byte
    Dim rstr As String
    Dim rbyte() As Byte
    Dim ans As String
    Dim g0 As Long
    bb(1) = &H12
    bb(2) = &H34
    rstr = Format(bb(1) * 16 ^ 2 + bb(2), "0000")
    ReDim rbyte(1 To Len(rstr))
    Call chg_str2byte(rstr, rbyte())
    ans = ""
    For g0 = 1 To UBound(rbyte())
       ans = ans & Hex(rbyte(g0))
    Next
    MsgBox ans

 このコードを追加しても変わらない
 という意味ではないですよね!!
 そりゃあ、変わりませんよ!!

 上記の例で  2バイトのバイナリデータが 4バイトのアンパックデータに変換できる例をしましました。

 このロジックを

 InFileName = Cells(1, 2) 
 OutFileName = Cells(2, 2) 
 InFN = FreeFile 
 Open InFileName For Binary As #InFN 
 OutFN = FreeFile 
 Open OutFileName For Binary As #OutFN 

 InPOS = 1 
 OutPOS = 1 
 InFileLen = LOF(InFN) 
 iTitle = 6 'チェック用 エクセルシートのタイトルの行位置 
 ipos = 7 'チェック用 エクセルシートのデータの行位置・開始 
 Do While (InPOS < InFileLen) 

  Get #InFN, InPOS, 漢字桁数
    Cells(iTitle, 2) = "漢字桁数"
    tmpStr = B2D2(漢字桁数)
    Cells(ipos, 2) = tmpStr
    ReDim tmpByte(1 To Len(tmpStr))
    j = chg_str2byte(tmpStr, tmpByte)
  Put #OutFN, OutPOS, tmpByte
  InPOS = InPOS + 2
  OutPOS = OutPOS + 4

  ipos = ipos + 1
 Loop

 Close #InFN 
 Close #OutFN

 このコード内に応用しなければ、
 >2バイトのバイナリデータが 4バイトのアンパックデータに変換
 されませんよ!!
 で、それは、ご自分で考えてください というのが私の現時点での投稿方法なんです。
 丸ごとコードの提示は、ぎりぎりのところで避けたいのです。

 2バイトのバイナリデータが 4バイトのアンパックデータに変換
 の意味を考えながら検討してみてください。

 もし、

 >データは変わりません。
 の意味が別にあるならば、詳しく説明してください。

 ichinose


申し訳ありませんが、かみあってないようですね。
ボックスに正確な数値が出た時に、
最初にご報告はさせて頂いてます。
えっとですね。
過去の投稿を見ていただければ
分かると思いますが。
私は今の状況をコードを提示して説明しただけであって、
今検証してますしか伝えてなかったと
思いますが。
それで今までの、お世話になった
知識を生かして
検証結果をご報告する予定だったのです。

フェンダー


 >検証結果をご報告する予定だったのです。
 なるほど・・・、
 私は、

 >ichinoseさんのコードを加えて 
 >バイナリ入出力はできるのですが 
 >ボックスに値が表示されるだけで 
 >入出力後の値は変わりませんね。 
 >もう少し検証してみます。

 この 入出力後の値は変わりませんね。 の意味が分からなかったから
 質問したのです。でも、この理由は、何となくわかりました。

 私にとっては、この御質問、
 2バイトのバイナリデータ-------->4バイトのアンパック型に変換 という処理が
 1234(16進数)---------------->F4F6F6F0 が正しいのなら、

 私の回答投稿は、ある程度終わっています。

 では、御自身が仰ったように これをフェンダーさんが検証された結果を
 お待ちしています。

 ichinose


そもそも、最終的なバイナリからゾーン変換の知識を
理解して書き出せるロジックを作るのが
目的ですから。
長い間お世話になったので検証して、ご報告する予定ですが
すぐ作れて良い知らせが出来るかってとこです。
このままなかなか思うようにいかなくて
この場でご報告出来るか分かりませんが。
どうもありがとうございました。
フェンダー


 >最終的なバイナリからゾーン変換の知識を 
 >理解して書き出せるロジックを作るのが目的

 そのとおりです。特にこの 理解して が大事だと思っています。

 >すぐ作れて良い知らせが出来るかってとこです。

 よい知らせとは、なんでしょうか?

 「提示されたコードが理解でき、読み込み/書き込みを完成することが出来ました」

 という報告は、確かに よい知らせでしょうねえ!!

 でも、
 「まだ理解できません、具体例を出して自分の理解できないところを説明します」
 から、始まって、御自分が分からないところを記述していただくこと、
 自分が理解できないところを御自分の意見をいれながら、ポイントを絞って記述していただく事

 これもよい報告なのですよ!!この記述が出来れば、理解するに一歩前進なのですから。

 いまところ、それが見えない というかその記述がないのです。

 前投稿で・・・、

 >パック型10進数からゾーン10進数のプログラムは100%合ってます。 
 >なぜならテストに合格しているからです。

 と言う投稿がありました。

 又、その処理過程を担う大事な二つのFunmctionも最後に御提示いただきました。
 P2D2、chg_str2byte 

 理解の早道のために

 2バイトバイナリデータ------------>4バイトアンパックデータの変換に

 最終結果をだす処理に 敢えて、chg_str2byteを使いました。

    bb(1) = &H12
    bb(2) = &H34
    rstr = Format(bb(1) * 16 ^ 2 + bb(2), "0000")
    ReDim rbyte(1 To Len(rstr))
    Call chg_str2byte(rstr, rbyte())

 パックデータからアンパックデータの変換は、本来のコードに既に組み込まれていて、
 正常に作動しているのですよね?

 又、P2D2、chg_str2byte のコードは、御自分で作成したコードですか?

 御自分で作成していなくても、理解されていますか?

 理解されていれば、実コード(ファイルから読み込んで アンッパク変換し、別ファイル書き込む)に
 組み込むことは、調べていただければ、それほど難しいことではないと思っています。

 私が懸念していたのは、

 バイナリ------アンパックデータの変換のロジックです。
 これは、VBAのような高級言語からプログラミングを始めると、分かりにくいのです。
 なぜなら、VBAは、プログラムが型変換を自動で知らず知らずに行ってしまうからです。
 又、高級言語では、データの内部形式等知らないでもプログラミングはかなりの程度
 可能ですから・・。

 が、今回の問題は、少しですが、こういう知識が必要です。

 理解するのが大変なのは分かっているつもりです。

 が、分からない箇所の本質がこのスレッドでは見えませんでした。

 これも、ファイルに書き込む直前までのコードを提示した理由の一つです。

 わからないのなら、自分が分からない箇所を明確(例を出して)に記述することです。

 この記述をしていただければ、

 私がわかっていることならば、フェンダーさんに理解していただくまで
 説明する努力を今度は、私がしますよ!!

 時間はかかってもよいです。

 検証された結果を待っています。

 ichinose


私がわかっていることならば、フェンダーさんに理解していただくまで
 説明する努力を今度は、私がしますよ!!

 ご丁寧にどうもありがとうございます。

 >私の回答投稿は、ある程度終わっています。

 >では、御自身が仰ったように これをフェンダーさんが検証された結果を
 >お待ちしています。

 と前スレで一段落ついていると思います。
 また話が長くなってしまいますので
 とりあえず前スレで
 忠告していただいたことは
 ありがたく受けとめておきます。
 それと過去のコードの一部を
 削除させていただきますのでよろしくお願いいたします。
 どうもありがとうございました。

 (フェンダー)

 >と前スレで一段落ついていると思います。

 私の説明など要らないなら、それはそれでもかまいませんが、

 検証された結果は、見せてください。

 ichinose


 >で、それは、ご自分で考えてください というのが私の現時点での投稿方法なんです。
 丸ごとコードの提示は、ぎりぎりのところで避けたいのです。

 >私の説明など要らないなら、それはそれでもかまいませんが、

えっとですね。
まだ面倒見て頂くとしたら、何を質問したらよいのですか?
今後はコード内部の話になってきてしまうんですよね。
本来バイナリ進数からアンパック進数を理解して
結果が書きだせるコードを目的として投稿いたしました。

今回結果としてほしいのは
1234というバイナリ進数のデータをアンパック進数になる
計算方法は

1*16進数3剰  +  2*16進数2剰  +  3*16進数1剰   + 4 =4660(F4F6F6F0)

という事が分かりました。

そしてこのコードで結果が得られるという
サンプルコードも作成して頂きました。

Sub smp()

    Dim bb(1 To 2)
    Dim unp(1 To 4) As Byte
    Dim g0 As Long
    Dim ans As String
    bb(1) = &H12
    bb(2) = &H34
    Call cnvBtoUnp(bb(1), bb(2), unp())
    For g0 = 1 To 4
       ans = ans & Hex(unp(g0))
    Next
    MsgBox ans
 End Sub
 Sub cnvBtoUnp(ByVal upper As Byte, ByVal rower As Byte, oarray() As Byte)
    Dim cnstr As String
    Dim g0 As Long
    Dim g1 As Long
    g1 = 4
    cnstr = CStr(upper * 16^2 + rower)
    g1 = Len(cnstr)
    For g0 = 4 To 1 Step -1
       oarray(g0) = &HF0
       If g1 > 0 Then
          oarray(g0) = oarray(g0) + CInt(Mid(cnstr, g1, 1))
       End If
       g1 = g1 - 1
    Next
 End Sub

あとはこの計算方法を今のコードに加える開発作業になってきます。
ichinoseさんでしたら
VBAの知識が豊富であると考えますから
そうむずかしいことではないと
考えるかと思います。
私の場合、サンプルのコードを引っ張ってきたり
参考書を読みながらゆっくり
考えて、つまずいた部分は社内のプログラマーに仕上げてもらったりしました。
(現状今、聞けないので投稿したのですが・・・)
ですのでバイナリ進数からアンパック進数の概念が理解できたとしても
それをさっとコードに書き足すのはすぐには出来ないので時間が
かかるので検証しますとお伝えして
投稿を打ち切ったのですが・・・

現在JAVAとVBAを並行して利用してまして。
折角VBAのコードがあるという事で
投稿させて頂いたのです。

(フェンダー)


 すぐに結果を出せということはありませんので、
 しっかり理解して、コードに書き足す時間が出来た時に、
 その結果を教えて頂ければ結構です。
 (cai)

まぁ、土台は出来上がっているわけですので
バイナリ進数からアンパック進数の計算方法を
考えたうえで

Function B2D2(B1() As Byte) As String

Dim ArrySize As Long
Dim dSize As Long
Dim 符号 As Byte
Dim 上位桁 As Long
Dim s上位桁 As String
Dim

        tmpByte(i + 1) = 241
      Case 下位桁 As Long
Dim s下位桁 As String
Dim tmp As String

  ArrySize = UBound(B1)
  dSize = ArrySize * 2

  tmp = ""
  For i = 1 To ArrySize
'  For Each i In B1
    ArrySize = ArrySize

    上位桁 = Int(B1(i) / 16 )
    s上位桁 = D2H(CInt(上位桁))

    下位桁 = B1(i) Mod 16
    s下位桁 = D2H(CInt(下位桁))

    tmp = tmp & CStr(s上位桁) & CStr(s下位桁)
  Next i
  tmp = Mid(tmp, 1, dSize)
  If (debug_mode = 1) Then Debug.Print "*" & tmp & "*"

  B2D2 = tmp

End Function

のコードを変更(もしくは数値の追加)すれば、求めたい値が書きだせると思っているのですが。

(フェンダー)


 私のこの投稿の前二つの記述を拝見しました。最初の頃に比べると、
 だいぶ分かりやすい記述になっていますね。
 さて、本題です。
 Function B2D2(B1() As Byte) As String
 ↑このプログラムを何度も投稿していましすね。
 このインタフェースでプログラムをバイナリ変換用に
 作成してあげれば、便利なんでしょうね
 これは、ちょっと前の投稿から理由がわかりました。

 Function B2D2(B1() As Byte) As String

   このインターフェースで作れば、

   InFileName = Cells(1, 2) 
   OutFileName = Cells(2, 2) 
   InFN = FreeFile 
   Open InFileName For Binary As #InFN 
   OutFN = FreeFile 
   Open OutFileName For Binary As #OutFN 
   InPOS = 1 
   OutPOS = 1 
   InFileLen = LOF(InFN) 
   iTitle = 6 'チェック用 エクセルシートのタイトルの行位置 
   ipos = 7 'チェック用 エクセルシートのデータの行位置・開始 
   Do While (InPOS < InFileLen) 
      Get #InFN, InPOS, 漢字桁数
      Cells(iTitle, 2) = "漢字桁数"
      tmpStr = B2D2(漢字桁数)
      Cells(ipos, 2) = tmpStr
      ReDim tmpByte(1 To Len(tmpStr))
      j = chg_str2byte(tmpStr, tmpByte)
      Put #OutFN, OutPOS, tmpByte
      InPOS = InPOS + 2
      OutPOS = OutPOS + 4
      ipos = ipos + 1
   Loop
   Close #InFN 
   Close #OutFN

 このコードが変更なしで使えますからねえ。
 実は、このインターフェースにすれば、中身は、
 1行で済んでしまいます。
 でも私は、それはしたくなかったんです。

 それをすれば、フェンダーさんも仰った

 >最終的なバイナリからゾーン変換の知識を 
 >理解して書き出せるロジックを作る
 の理解が達成できずに終わってしまいそうだったからです。

 で、
 バイナリデータ------> アンパックデータ変換 
 のコードを複数記述しましたが、
 以下のコードを使うことで検討してください。たぶん、一番わかりやすいと判断しました、

 Sub test1()
    Dim bb(1 To 2) As Byte
    Dim tmpstr As String
    Dim tmpbyte() As Byte
    Dim ans As String
    Dim g0 As Long
    bb(1) = &H12
    bb(2) = &H3F
    tmpstr = Format(bb(1) * 16 ^ 2 + bb(2), "0000")
    ReDim tmpbyte(1 To Len(tmpstr))
    Call chg_str2byte(tmpstr, tmpbyte())
    ans = ""
    For g0 = 1 To UBound(tmpbyte())
       ans = ans & Hex(tmpbyte(g0))
    Next
    MsgBox ans
 End Sub
‘対比していただくコードとして、パック------アンパック変換をtest2とします。
 Sub test2()
    Dim bb(1 To 2) As Byte
    Dim tmpstr As String
    Dim tmpbyte() As Byte
    Dim ans As String
    Dim g0 As Long
    bb(1) = &H12
    bb(2) = &H3F
    tmpstr = P2D2(bb())

    ReDim tmpbyte(1 To Len(tmpstr))
    Call chg_str2byte(tmpstr, tmpbyte())
    ans = ""
    For g0 = 1 To UBound(tmpbyte())
       ans = ans & Hex(tmpbyte(g0))
    Next
    MsgBox ans
 End Sub

‘以下test1,test2の実行に必要なプロシジャー

 Function P2D2(B1() As Byte) As String
    Dim ArrySize As Long
    Dim dSize As Long
    Dim 符号 As Byte
    Dim 上位桁 As Long
    Dim s上位桁 As String
    Dim 下位桁 As Long
    Dim s下位桁 As String
    Dim tmp As String
    ArrySize = UBound(B1)
    dSize = ArrySize * 2 - 1
    tmp = ""
    For i = 1 To ArrySize
       ArrySize = ArrySize
       上位桁 = Int(B1(i) / 16)
       s上位桁 = D2H(CInt(上位桁))
       下位桁 = B1(i) Mod 16
       s下位桁 = D2H(CInt(下位桁))
       tmp = tmp & CStr(s上位桁) & CStr(s下位桁)
     Next i
     tmp = Mid(tmp, 1, dSize)
     P2D2 = tmp
 End Function
 Function chg_str2byte(uniStr As String, tmpbyte() As Byte) As Long
    Dim iSize As Long
    Dim i As Long
    iSize = Len(uniStr)
    chg_str2byte = 1
    i = 0
    Do While (i < iSize)
      Select Case Mid(uniStr, i + 1, 1)
        Case "0"
          tmpbyte(i + 1) = 240
        Case "1"
          tmpbyte(i + 1) = 241
        Case "2"
          tmpbyte(i + 1) = 242
        Case "3"
          tmpbyte(i + 1) = 243
        Case "4"
          tmpbyte(i + 1) = 244
        Case "5"
          tmpbyte(i + 1) = 245
        Case "6"
          tmpbyte(i + 1) = 246
        Case "7"
          tmpbyte(i + 1) = 247
        Case "8"
          tmpbyte(i + 1) = 248
        Case "9"
          tmpbyte(i + 1) = 249
      End Select
      i = i + 1
    Loop
 End Function
 Function D2H(itmp As Long) As String
 Select Case itmp
    Case 10
      D2H = "A"
    Case 11
      D2H = "B"
    Case 12
      D2H = "C"
    Case 13
      D2H = "D"
    Case 14
      D2H = "E"
    Case 15
      D2H = "F"
    Case Else
      D2H = CStr(itmp)
  End Select
 End Function

 test1 と test2を本当によく見比べてください。

 共通部分もあります。
 どこが共通なのか?そして、どこが違うのか?

 Function chg_str2byte(uniStr As String, tmpbyte() As Byte) As Long

 test1 と test2の 両方に使われているこのchg_str2byteというプロシジャー
 何をするプロシジャーなのか?
 このプロシジャーへの入力データである uniStrは、どんなデータなのか
 又、出力データであるtmpbyte()は、どんなデータなのか?

 これを理解することです。

 >1234というバイナリ進数のデータをアンパック進数になる 
 >計算方法は 

 >1*16進数3剰  +  2*16進数2剰  +  3*16進数1剰   + 4 =4660(F4F6F6F0)
 >という事が分かりました。
 今回の例ではパック型の対比もあるので バイナリデータを 123f にしましたが、考え方は同じで

 1*16進数3剰  +  2*16進数2剰  +  3*16進数1剰   + 15(F)=4671
 となりますが、test1では、どこでこの計算をしていると思いますか?

 tmpstr = Format(bb(1) * 16 ^ 2 + bb(2), "0000")
 この1行でそれをしています。結果は、文字列でだしています。

 試しに

 Sub test3()
    Dim bb(1 To 2) As Byte
    Dim tmpstr As String
    Dim tmpbyte() As Byte
    Dim ans As String
    Dim g0 As Long
    bb(1) = &H12
    bb(2) = &H3F
    tmpstr = Format(bb(1) * 16 ^ 2 + bb(2), "0000")
    MsgBox tmpstr
 End Sub

 これを実行して、tmpstrの値を表示して考えてください。

 このことを踏まえて、再度 test1 とtest2を見比べてください。

 そして、パックデータを読み込み、これをアンパックデータに変換しファイルに書き込む
 コードを見てください。消されてますが、
 P2D2を使い、Get  Putとしている箇所です。

 test1 とtest2は、見た目は 似ています。

 変換されたデータをどのように書き込み(Put)すればよいのか?

 見えてきませんか?

 よくよく、test1とtest2を良く見比べてください。

 ichinose


コメントありがとうございます。

実はバイナリ進数とアンパック進数の事を
理解したかったのと加えて
現在のコードに対して
どう考えないといけないのか
ほんとはこの部分を勉強したかったんですよね。
コードのアルゴリズムが長けているのであれば
プログラムの土台はすでに出来上がっているし
あとはバイナリとアンパックの関係を
理解すれば投稿しなくて済むものと思います。

高級言語でいう、人間の感覚・視点からチェックしたら
test1 test2の違いは

 tmpstr の部分になりますよね??

ですからtest1のtmpstr = Format(bb(1) * 16 ^ 2 + bb(2), "0000")
の部分がP2D2の部分に精通しているわけで
そう考えると
P2D2にある
For i = 1 To ArrySize

       ArrySize = ArrySize
       上位桁 = Int(B1(i) / 16)
       s上位桁 = D2H(CInt(上位桁))
       下位桁 = B1(i) Mod 16
       s下位桁 = D2H(CInt(下位桁))
       tmp = tmp & CStr(s上位桁) & CStr(s下位桁)
     Next i
の計算式をまず考えなければいけないという事でよろしいでしょうか??
ただ私はVBAの構文は慣れていないので
Format(bb(1) * 16 ^ 2 + bb(2), "0000")と同じように(移植)するには
どうすればよいのかという事になってきます。

例えばこういう場合mod関数じゃなくで違う構文に書き換えないと
だめですよってなるとすぐにはできない部分ではあります。

それとこのchg_str2byteのプロシージャにあるtmpbyte

これはVB(VBA)でしか通用しないロジックになりますよね?
私の記憶が確かなら社内のプログラマが言っており
これはほかの言語では通用しないと。
この部分が一番分かりにくい部分ではあります。
実際このあたりも少し再構築しなければいけないのかもしれないのですし
またuniStrの関数も
アスキー文字列を各国語文字列に変換するぐらいのことしかわかってません。

P2D2の上位桁 = Int(B1(i) / 16)の部分に関しては
   上位桁 = Int(B1(i) / 16 * 16)の変更でいいんじゃないでしょうか??

(フェンダー)


 >tmpstr の部分になりますよね??
 そうですね、ここですよね

 どこかは、違わなければ、

 test1は、バイナリ---->アンパック

 test2は、パック------>アンパック

 の変換とプログラムの入力データの型が違いますから、結果が変わってきません。

 >の計算式をまず考えなければいけないという事でよろしいでしょうか??
 P2D2内で行われている計算の意味が分からないなら、理解はしてください。

 >Format(bb(1) * 16 ^ 2 + bb(2), "0000")と同じように(移植)するには
 >どうすればよいのかという事になってきます。

 P2D2というプロシジャーは、完成されているのですよね?
 ですから、1行で書けるようにしてください ということではありませんよ!!

 test1とtest2の処理の違いは、

 P2D2で行われている処理と Format(bb(1) * 16 ^ 2 + bb(2), "0000")
 この計算の違いです、これが結果を変えているのです。
 この処理の違いがパックデータとバイナリの違いです。

 そして、この処理の出力データであるtmpstrという文字列型変数には、何が返されるのか?
 です。

 ここでちょっと、chg_str2byteの話に移りましょう

 >これはVB(VBA)でしか通用しないロジックになりますよね?
 これは、何を持って このように仰ったのでしょうかねえ?

 >アスキー文字列を各国語文字列に変換する
 アスキー文字列と言ってもここに入ってくるのは、アスキーの数字文字列だけですが、
 これをEBCDICコードの数字文字列に変換し、それぞれの数字を1バイト変数に入れています。

 文字列が "123"なら、F1F2F3という内部コードになるように変換しているのです。
 このF1F2F3は、EBCDICコードを使っているコンピュータでは、123と表示されます。

 ここでは、アンパックデータ=EBCDICコードの数字文字列と見なしているのだと思います。

 よって、chg_str2byteは、
 アスキーの数字文字列をEBCDICコードの数字文字列コードに変換し、変換したコードをバイト型変数の配列として出力しています。

 このバイト型配列を ファイルに書き込んでいますよね(例のPUTです)

 元に戻りましょう。

 先ほどの
 >この処理の出力データであるtmpstrという文字列型変数には、何が返されるのか?

 このtmpstrがchg_str2byteというプロシジャーの入力データになっているのですから、
 これは、アスキーの数字文字列ということになります。

 VBAでは、普通に数字文字列です。

 よって、

 tmpstr = Format(bb(1) * 16 ^ 2 + bb(2), "0000")

 は、2バイトバイナリデータを数字文字列に変換し、

 P2D2は、パックデータを数字文字列に変換していることになります。

 尚、

 tmpstr = Format(bb(1) * 16 ^ 2 + bb(2), "0000")

 このステートメントの

 Format(数字データ,"0000")

 ですが、数字データが必ず、4文字になるようにしています。

 数字データが1234なら   1234
 数字データが14なら     0014
 数字データが1なら      0001

 という文字列を返します。そうしないと4バイトのアンパックデータにならないので・・・。

 ここまでの記述で 不明な点があれば、投稿して下さい。

 ichinose

   


 >よって、chg_str2byteは、
  >アスキーの数字文字列をEBCDICコードの数字文字列コードに変換し、変換したコードをバイト型変数     >の配列として出力しています。

説明どうもありがとうございました。
一応再確認という事で勉強になります。

>これは、何を持って このように仰ったのでしょうかねえ?

256byteでの数式の配列を作成するコードは
他の言語では通用しないすなわちVBAでしか通用しないだったかなーと。

ですが、このような発言をされるという事は
おそらく私が間違って認識していただけかもしれません。

まずは
1*16進数3剰  +  2*16進数2剰  +  3*16進数1剰   + 4 =4660(F4F6F6F0)と
tmpstr = Format(bb(1) * 16 ^ 2 + bb(2), "0000")が同等のものと認識出来ないので
P2D2に置き換えて理解しようとすることが出来ないのだと思います。

tmpstr = Format(bb(1) * 16 ^ 2 + bb(2), "0000")は
解読すると
Format(文字列に変換)でP2D2でいう上位桁*16の2剰+下位桁に
になりますよね?
P2D2では移植しようとは思ってませんでしたけど
上位桁と下位桁を別々で計算して
最終的にtmp+上位桁+下位桁となってますが

下記のコードでテストすると
dSize = ArrySize * 2 - 1を
桁を削らないように
dSize = ArrySize * 2とします。
で、上位桁 = Int(B1(i) / 16)を
tmpstr = Format(bb(1) * 16 ^ 2 + bb(2), "0000")を考えて
上位桁 = Int(B1(i) / 16 * 16)すると
0050はF0F0F8F0と取得したかった文字列が返ってきました。
ですが他の数値になると
異なる数値が返ってきます。

冒頭で話した通り
1*16進数3剰  +  2*16進数2剰  +  3*16進数1剰   + 4 =4660(F4F6F6F0)が
この計算式
tmpstr = Format(bb(1) * 16 ^ 2 + bb(2), "0000")でなぜ結果が返ってくるのか?
精通してることが認識出来ない限り
作業は現状困難だと思ってます。
よって理解しようしても出来ないのが正直な回答です。

 Sub test2()
    Dim bb(1 To 2) As Byte
    Dim tmpstr As String
    Dim tmpbyte() As Byte
    Dim ans As String
    Dim g0 As Long
    bb(1) = &H00
    bb(2) = &H50
    tmpstr = P2D2(bb())
    ReDim tmpbyte(1 To Len(tmpstr))
    Call chg_str2byte(tmpstr, tmpbyte())
    ans = ""
    For g0 = 1 To UBound(tmpbyte())
       ans = ans & Hex(tmpbyte(g0))
    Next
    MsgBox ans
 End Sub

 Function P2D2(B1() As Byte) As String
    Dim ArrySize As Long
    Dim dSize As Long
    Dim 符号 As Byte
    Dim 上位桁 As Long
    Dim s上位桁 As String
    Dim 下位桁 As Long
    Dim s下位桁 As String
    Dim tmp As String
    ArrySize = UBound(B1)
    dSize = ArrySize * 2
    For i = 1 To ArrySize
       ArrySize = ArrySize
       上位桁 = Int(B1(i) / 16 * 16)
       s上位桁 = D2H(CInt(上位桁))
       下位桁 = B1(i) Mod 16
       s下位桁 = D2H(CInt(下位桁))
       tmp = tmp & CStr(s上位桁) & CStr(s下位桁)
     Next i
     tmp = Mid(tmp, 1, dSize)
     P2D2 = tmp
 End Function
 Function chg_str2byte(uniStr As String, tmpbyte() As Byte) As Long
    Dim iSize As Long
    Dim i As Long
    iSize = Len(uniStr)
    chg_str2byte = 1
    i = 0
    Do While (i < iSize)
      Select Case Mid(uniStr, i + 1, 1)
        Case "0"
          tmpbyte(i + 1) = 240
        Case "1"
          tmpbyte(i + 1) = 241
        Case "2"
          tmpbyte(i + 1) = 242
        Case "3"
          tmpbyte(i + 1) = 243
        Case "4"
          tmpbyte(i + 1) = 244
        Case "5"
          tmpbyte(i + 1) = 245
        Case "6"
          tmpbyte(i + 1) = 246
        Case "7"
          tmpbyte(i + 1) = 247
        Case "8"
          tmpbyte(i + 1) = 248
        Case "9"
          tmpbyte(i + 1) = 249
      End Select
      i = i + 1
    Loop
 End Function
 Function D2H(itmp As Long) As String
 Select Case itmp
    Case 10
      D2H = "A"
    Case 11
      D2H = "B"
    Case 12
      D2H = "C"
    Case 13
      D2H = "D"
    Case 14
      D2H = "E"
    Case 15
      D2H = "F"
    Case Else
      D2H = CStr(itmp)
  End Select
 End Function

(フェンダー)


 >1*16進数3剰  +  2*16進数2剰  +  3*16進数1剰   + 4 =4660(F4F6F6F0)と 
 >tmpstr = Format(bb(1) * 16 ^ 2 + bb(2), "0000")が同等のものと認識出来ないので 
 >P2D2に置き換えて理解しようとすることが出来ないのだと思います。

 いよいよ、ここですね

 tmpstr = Format(bb(1) * 16 ^ 2 + bb(2), "0000")

 この計算の意味です。

 例
 二桁の数字 12  と二桁数字 34 共に10進数です。
 この二つの数字を使って 1234という4桁数字を作る計算式を考えると・・・、

 12*10^2+34=1234

 ですよね!! 10進数ですから、桁を1桁上げるに10を掛け、桁を2桁上げるなら、10^2(つまり、100を掛ける)

 2進数の11(10進の3)の桁を上げて 110(10進の6)にするには、
 2進数ですから、11に2を掛けると 11が 110 になります。
 2進数の11を  1100に桁を上げるには 11に2^2を掛けると 1100になります。

 では、
 tmpstr = Format(bb(1) * 16 ^ 2 + bb(2), "0000")

 この計算に戻りましょう。
 bb(1)には、 &h12 という二桁の16進数が入っています。
 bb(2)には、 &h34 という二桁の16進数が入っています。

 二桁の16進数 &h12  と二桁16進数 &h34 。
 この二つの数字を使って &h1234という4桁16進数を作る計算式を考えると・・・、
 16進数 &h12 の桁を2桁上げるのですから、

 &h12*16^2+&h34 =&h1234    となります。
 (尚、&hは、16進数であると言う記号です)
 よって、bb(1) * 16 ^ 2 + bb(2) この計算式が出てくるんです

 さて、ここからがVBAの便利なところ

 VBAでは、16進で計算しようが、10進で計算しようが、はたまた 16進、10進混在で計算しようが、
 答えは、10進で表示されます。

 Sub その証拠()
    Dim bb(1 To 2) As Byte
    bb(1) = &H12
    bb(2) = &H34
    MsgBox bb(1) * 16 ^ 2 + bb(2)
    MsgBox &H12 * 16 ^ 2 + &H34
 End Sub

 つまり、
 1*16進数3剰  +  2*16進数2剰  +  3*16進数1剰   + 4 

 このような計算をして 16進から10進に変換する計算式を書かなくてもVBAが
 勝手にやってくれるのです。ちゃんちゃん!!

 既に10進に変換された数字をFormat(・・・)を使って、文字列にするので

 4660という文字列の数字が変数tmpstrに格納されます。

 これでいかがでしょうか?

 個々を理解したうえで尚且つ、B2D2をP2D2の改良で行いたいなら、
 これは、これで考えますが、上記が理解されたら、
 この方法で ファイル書き込みを行ってください。

 ichinose
   


コメントありがとうございます。
tmpstr = Format(bb(1) * 16 ^ 2 + bb(2), "0000")
の解説ありがとうございます。
進数系が知識が乏しいので、これは聞かないとわかりませんでした。

>個々を理解したうえで尚且つ、B2D2をP2D2の改良で行いたいなら、

 これは、これで考えますが、

tmpstr = Format(bb(1) * 16 ^ 2 + bb(2), "0000")の計算式が
分かれば見えてくるのかなって思ったのですが
そんな甘くはないようですね。

作成していただいたコードって
毎回4桁の数値を指定する構文ですよね
  bb(1) = &H00

    bb(2) = &H50

これがよくわからないのですが
P2D2のロジックがあるのにこれを利用しないで

 Get #InFN, InPOS, 漢字桁数
    Cells(iTitle, 2) = "漢字桁数"
    tmpStr = B2D2(漢字桁数)
    Cells(ipos, 2) = tmpStr
    ReDim tmpByte(1 To Len(tmpStr))
    j = chg_str2byte(tmpStr, tmpByte)
  Put #OutFN, OutPOS, tmpByte
  InPOS = InPOS + 2
  OutPOS = OutPOS + 4

バイナリ用のコードを作成して書き込むという事ですよね。

うーん。
これをコードにするには・・・・・
何がわからないのって言われると
何を質問すれば伝わるかなって答えになってしまいます。
ichinoseさんはもうほとんど答えを出してくれていると
思うのですが、
ここまで来ると逆に何を質問すればいいのか
コードで考えると分からなくなってきました。

もう少し考えてみます。

(フェンダー)


P2D2を使用せず

Get #InFN, InPOS, 漢字桁数

    Cells(iTitle, 2) = "漢字桁数"
    tmpStr = B2D2(漢字桁数)
    Cells(ipos, 2) = tmpStr
    ReDim tmpByte(1 To Len(tmpStr))
    j = chg_str2byte(tmpStr, tmpByte)
  Put #OutFN, OutPOS, tmpByte
  InPOS = InPOS + 2
  OutPOS = OutPOS + 4
の
tmpStr = B2D2(漢字桁数)を
呼び出さずに
Format(bb(1) * 16 ^ 2 + bb(2), "0000")のような
コードを
前スレの計算方法を理解しつつ
1行書き込む。
それなら1行で済んでしまう。

考え方の方向性としては合ってますでしょうか??

(フェンダー)


 >考え方の方向性としては合ってますでしょうか?
 昼間は、私も仕事なので、長く記述できませんが、

そういうことです。

 もう少しです。

 ichinose@私の会社には、キッコーマンの醤油さしがない


現状報告させていただきます。

'************************

  Get #InFN, InPOS, 漢字桁数
    Cells(iTitle, 2) = "漢字桁数"
    tmpStr = B2D2(漢字桁数)
    Cells(ipos, 2) = tmpStr
    ReDim tmpByte(1 To Len(tmpStr))
    j = chg_str2byte(tmpStr, tmpByte)
  Put #OutFN, OutPOS, tmpByte
  InPOS = InPOS + 2
  OutPOS = OutPOS + 4
'************************

だったコードを

'************************

  Get #InFN, InPOS, 氏名漢字桁数
    Cells(iTitle, 2) = "氏名漢字桁数"

     tmpStr = (氏名漢字桁数(1) * 16 ^ 2 + 氏名漢字桁数(2))
    Cells(ipos, 2) = tmpStr
    ReDim tmpByte(1 To Len(tmpStr))
    j = chg_str2byte(tmpStr, tmpByte)
  Put #OutFN, OutPOS, tmpByte
  InPOS = InPOS + 2
  OutPOS = OutPOS + 4
'************************

のように変更し
バイナリファイルを入出力しました。

いまのとこ
1234だったり先頭から0が付かない2バイトのデータは
F4F6F6F0と
正確な数値で書き出せるのですが
先頭から0が付く
2バイトデータは下記のようになります。

例 0005 → F8F0F0F0

で、過去スレで下記のように

 >tmpstr = Format(bb(1) * 16 ^ 2 + bb(2), "0000")

 このステートメントの

 Format(数字データ,"0000")

 ですが、数字データが必ず、4文字になるようにしています。

 数字データが1234なら   1234
 数字データが14なら     0014
 数字データが1なら      0001

 という文字列を返します。そうしないと4バイトのアンパックデータにならないので・・・。

と教えていただいたので

 tmpStr = (氏名漢字桁数(1) * 16 ^ 2 + 氏名漢字桁数(2))の部分に

"0000"を加えようとしているのですが

現状コンパイルエラーとなり検証中です。

Cells(ipos, 2) = tmpStrで、数値をチェックする限り

取得したい文字はすべて一致しました。

あとは数字データが必ず、4文字になるように出来れば

私の判断が間違ってなければ解決するかと思います。

(フェンダー)


解決いたしました。
先ほどまでFormat関数を使用しても0000が指定できなかったのですが
出来ました。
すべて4文字(4バイト)で取得することができました。
結果的に1行でしたがかなり理解するのに苦労しましたし
勉強になりました。
情報技術者の参考書でも読んで、今後も勉強したいと思います。
長い間どうもありがとうございました。

'************************

  Get #InFN, InPOS, 漢字桁数
    Cells(iTitle, 3) = "漢字桁数"

     tmpStr = Format(漢字桁数(1) * 16 ^ 2 + 漢字桁数(2), "0000")
    Cells(ipos, 3) = tmpStr
    ReDim tmpByte(1 To Len(tmpStr))
    j = chg_str2byte(tmpStr, tmpByte)
  Put #OutFN, OutPOS, tmpByte
  InPOS = InPOS + 2
  OutPOS = OutPOS + 4
'************************

(フェンダー)


お世話になります。
質問内容が解決したレスと変わってくるのですが、
 この質問内容からの
 延長線上の話になりますので、ご質問させていただきます。

 例えばDATデータが1レコード829バイトあり
 10件のデータ8290バイトのDATデータがあったとします。
 1レコードずつの先頭の
 4バイトずつを削除していきたいのですが、
 やり方が分かりません。
 すいませんがここで勉強させてください。
 どうぞよろしくお願いします。

 1レコード目→先頭4バイト削除
 2レコード目→先頭4バイト削除
 3レコード目→先頭4バイト削除
 4レコード目→先頭4バイト削除
 5レコード目→先頭4バイト削除
 6レコード目→先頭4バイト削除
 7レコード目→先頭4バイト削除
 8レコード目→先頭4バイト削除
 9レコード目→先頭4バイト削除
 10レコード目→先頭4バイト削除

 ↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓

 8290バイト→8250バイト

(フェンダー)


 手順としては・・・・、

 1 元ファイル(ここでいう1レコード829バイトのファイル)を開く

 2 新規ファイル(ここでいう1レコード825バイトのファイル)を開く

 3 元ファイルからデータを読み込む(Get)

 4 829バイトデータを825バイトデータに編集

 5 新規ファイルに編集したデータを書き込む(Put)

 6 3〜5を元データがなくなるまで繰り返し

 7 元ファイル 新規ファイルを閉じる

 8 元ファイルを削除

 9 新規ファイル名を 元ファイル名に変更する

 という手順です。具体例は、次投稿にて

 尚、今回のご質問は、新規投稿を行い、関連があるなら、このスレッドを
 リンクした方がよかったと思いますよ!!

 ichinose


 新規ブックの標準モジュールに以下のコードをコピーしてください

 '=======================================================================
 Option Explicit
 Type b26
   b(1 To 26) As Byte
 End Type
 Type b4to22
   b1(1 To 4) As Byte
   b2(1 To 22) As Byte
 End Type
 Const fl = "Sample.dat"
 '====================================================
 Sub サンプルデータの作成()
    Dim b As b26
    Dim g0 As Long
    Dim fno As Long
    For g0 = 1 To 26
       b.b(g0) = 64 + g0
    Next
    On Error Resume Next
    Kill ThisWorkbook.Path & "\" & fl
    On Error GoTo 0
    fno = FreeFile
    Open ThisWorkbook.Path & "\" & fl For Random As #fno Len = LenB(b)
    For g0 = 1 To 10
       Put #1, , b
    Next
    Close #fno
 End Sub
 '====================================================================
 Sub サンプルデータの表示()
    Dim b As b26
    Dim g0 As Long
    Dim fno As Long
    fno = FreeFile
    Open ThisWorkbook.Path & "\" & fl For Random As #fno Len = LenB(b)
    Get #fno, , b
    Do Until EOF(fno)
       MsgBox "第 " & g0 + 1 & "レコード  :  " & StrConv(b.b, vbUnicode)
       g0 = g0 + 1
       Get #1, , b
    Loop
    Close #fno
 End Sub
 '====================================================================
 Sub 変換()
    Dim b As b4to22
    Dim g0 As Long
    Dim fno1 As Long
    Dim fno2 As Long
    fno1 = FreeFile
    Dim a As String
    Open ThisWorkbook.Path & "\" & fl For Random As #fno1 Len = LenB(b)
    On Error Resume Next
    Kill ThisWorkbook.Path & "\" & Replace(fl, ".dat", ".tmp")
    On Error GoTo 0
    fno2 = FreeFile
    Open ThisWorkbook.Path & "\" & Replace(fl, ".dat", ".tmp") For Random As #fno2 Len = UBound(b.b2)
    g0 = 0
    Get #fno1, , b
    Do Until EOF(fno1)
       Put #fno2, , b.b2
       Get #fno1, , b
    Loop
    Close #fno1
    Close #fno2
    On Error Resume Next
    Kill ThisWorkbook.Path & "\" & fl
    Name ThisWorkbook.Path & "\" & Replace(fl, ".dat", ".tmp") As ThisWorkbook.Path & "\" & fl
 End Sub
 '====================================================================
 Sub 結果表示()
    Dim b(1 To 22) As Byte
    Dim g0 As Long
    Dim fno As Long
    fno = FreeFile
    Open ThisWorkbook.Path & "\" & fl For Random As #fno Len = UBound(b)
    Get #fno, , b
    Do Until EOF(fno)
       MsgBox "第 " & g0 + 1 & "レコード  :  " & StrConv(b, vbUnicode)
       g0 = g0 + 1
       Get #1, , b
    Loop
    Close #fno
 End Sub

コード説明

 まず、上記のコードをコピー後、一度適当な名前でブックを保存してください。

 保存後、サンプルデータの作成を実行してください。

 この「サンプルデータの作成」は、A〜Zの文字コードを10回書き込んだ簡単なデータです

 つまりA〜Z、26バイトのデータを10回書き込んだ260バイトのファイルです。

 サンプルデータの内容を「サンプルデータの表示」を実行して確認して下さい。

 10レコード分の A〜Z が表示されるはずです。

 この260バイトのサンプルファイル(Sample.dat)の26バイトのデータの先頭4バイト分を
 削除したファイルを作成するのが、「変換」 というプログラムです。

 結果は、A〜Zの内の先頭A〜Dを削除するのですから、E〜Zの22バイトのデータを10回
 書き込んだファイルになるはずです。

 再構築されたSample.dat 表示は、結果表示 というプログラムを実行して
 確認して下さい。E〜Zが10回表示されるはずです。

 上記は、1レコード26バイト、260バイトのファイルのレコードの先頭4バイトを
 削除して、1レコード22バイト、220バイトのファイルを作成した例です。

 上記コードを調べて、理解して、実際のファイルに応用してください。

 尚、ファイルに関する I/Oでは、本来はもっと細かいエラーチェックが
 必要です。今回は、省略していますが・・・。

 ichinose

 バイト削除の件、新規投稿させていただきました。
 引き続きこちらのレスでご質問ですが
 以前バイナリ進数からゾーン進数への
 数値表現変換を勉強させていただきました。
 前回は
 2バイトから4バイトへの変換でしたが
 今回は
 4バイトから8バイトへの変換でご質問させていただきます。

 1234(2バイトデータ)1*16進数3剰 + 2*16進数2剰 + 3*16進数1剰+ 4
   VBA計算式→  tmpStr = Format(bb(1) * 16 ^ 2 + bb(2), "0000")で
  VBAがバイナリ2バイトからゾーン4バイトへ
  自動的に計算してくれると教えていただきました。

 12345678と4バイトのバイナリ進数データがあったとします。
 4バイトから8バイトへの変換計算式でいうと
 1*16進数7剰 + 2*16進数6剰 + 3*16進数5剰+4*16進数4剰 
 +5*16進 数3剰 + 6*16進数2剰++ 7*16進数1剰+8
 の計算式になりますよね??
 VBAが自動的にしてくれる計算方式がよく分からないのですが・・・
 よろしくお願いします。

(フェンダー)


 こっちのスレッドにも質問があることを見逃していました。
 >tmpStr = Format(bb(1) * 16 ^ 2 + bb(2), "0000")で

 1バイトデータ二つをバイナリデータに見立てて10進数に変換する計算は、上記でしたね!!

 今度は、 1バイトデータ4つをバイナリデータに見立てて10進数に変換する計算は、

 1バイトのデータは、二桁の16進数が入っていると見なせますから、

    Dim bb(1 To 4) As Byte
    Dim tmpstr As String
    bb(1) = &H12
    bb(2) = &H34
    bb(3) = &H56
    bb(4) = &H78

 この四つ配列から、 &h12345678 という8桁の16進データを作るには?

 bb(1)のデータは、6桁桁上げするのですから、16^6を掛ける
 bb(2)のデータは、4桁桁上げするのですから、16^4を掛ける
 bb(3)のデータは、2桁桁上げするのですから、16^2を掛ける
 bb(4)のデータは、0桁桁上げするのですから、何も掛けない(正確には,16^0=1を掛ける)

 ということになります。

 10進数ならば、どうすればよいかをまず考えて、その規則を16進に当てはめていくと理解できます。

 よって、今回の例題だと

 Sub test2()
    Dim bb(1 To 4) As Byte
    Dim tmpstr As String
    bb(1) = &H12
    bb(2) = &H34
    bb(3) = &H56
    bb(4) = &H78
    tmpstr = Format(bb(1) * 16 ^ 6 + bb(2) * 16 ^ 4 + bb(3) * 16 ^ 2 + bb(4), "00000000")
    MsgBox tmpstr
 End Sub

 表示される値は、305419896 で9桁ですね!! これでは、8桁を越えてしまいます。

 これは、前回の2バイトバイナリデータのときも懸念されていたことですが

 実際には、&h12345678 このような数字は、4バイトバイナリデータとしては,入ってこないと
 思われます。&h5F5E0FF ここまでの数字しかしか入ってこないと思います。

 オフコンの言語には、10進の桁数で変数が定義できるものがありますからね!!

 以上です
 これも新規投稿を行い、このスレッドを
 リンクした方がよいと思いますよ!!  このスレ、長いでしょう?
 ichinose


コメント返信:

[ 一覧(最新更新順) ]


YukiWiki 1.6.7 Copyright (C) 2000,2001 by Hiroshi Yuki. Modified by kazu.