C# と VB.NET の質問掲示板

わんくま同盟

ASP.NET、C++/CLI、Java 何でもどうぞ

C# と VB.NET の入門サイト

ツリー一括表示

バイナリデータを読んで、日付などの形にする方法について /へなちょこ (18/01/03(Wed) 00:23) #86240
Re[1]: バイナリデータを読んで、日付などの形にする方法につ.. /Azulean (18/01/03(Wed) 00:44) #86241
│└ Re[2]: バイナリデータを読んで、日付などの形にする方法につ.. /へなちょこ (18/01/03(Wed) 14:06) #86242
│  └ Re[3]: バイナリデータを読んで、日付などの形にする方法につ.. /Azulean (18/01/03(Wed) 14:37) #86243
│    └ Re[4]: バイナリデータを読んで、日付などの形にする方法につ.. /へなちょこ (18/01/03(Wed) 17:39) #86245
│      ├ Re[5]: バイナリデータを読んで、日付などの形にする方法につ.. /Azulean (18/01/03(Wed) 19:36) #86247
│      │└ Re[6]: バイナリデータを読んで、日付などの形にする方法につ.. /へなちょこ (18/01/03(Wed) 23:51) #86248
│      └ Re[5]: バイナリデータを読んで、日付などの形にする方法につ.. /魔界の仮面弁士 (18/01/05(Fri) 16:27) #86252 解決済み
│        └ Re[6]: バイナリデータを読んで、日付などの形にする方法につ.. /へなちょこ (18/01/09(Tue) 21:19) #86264
Re[1]: バイナリデータを読んで、日付などの形にする方法につ.. /Jitta (18/01/03(Wed) 17:36) #86244
  └ Re[2]: バイナリデータを読んで、日付などの形にする方法につ.. /?????? (18/01/03(Wed) 23:52) #86249 解決済み


親記事 / ▼[ 86241 ] ▼[ 86244 ]
■86240 / 親階層)  バイナリデータを読んで、日付などの形にする方法について
□投稿者/ へなちょこ (5回)-(2018/01/03(Wed) 00:23:56)

分類:[VB.NET/VB2005 以降] 

すみません、たびたび質問をさせて頂きます。

前の質問のご回答で、以下のコードを教えていただきました。
(一部分だけ書かせてもらいます)

reader.ReadBytes(2) '最初の2バイトは読み捨て
Dim len1 As Integer = 2 * reader.ReadByte() '文字1のバイト数
Dim len2 As Integer = 2 * reader.ReadByte() '文字2のバイト数
reader.ReadBytes(4) '次の4バイトは読み捨て
Dim bin1 As Byte() = reader.ReadBytes(len1) '文字列1のデータ
Dim bin2 As Byte() = reader.ReadBytes(len2) '文字列2のデータ
Dim txt1 As String = enc.GetString(bin1)
Dim txt2 As String = enc.GetString(bin2)

この中で、文字1のバイト数として1バイトのデータを読んでInteger型の変数len1に代入
その後、Byte()にしてString型にするという流れを行っていると思うのですが、
このような処理として、以下の事がしたい場合は、どのように書けばよいのか、教えて
頂けないでしょうか?
色々と試してみたところ、「型が変換できません」のエラーが出てしまい、訳が分からなく
なってます....

・8バイト分のバイナリデータを読んで、日付の形にする。(「FILETIME 構造体」という
形のデータだと思います。)
・2バイト分のバイナリデータを読んで、Integer型にする。(10進数の値にしたいと思ってます。)

すみませんが、よろしくお願いします。
[ □ Tree ] 返信 編集キー/

▲[ 86240 ] / ▼[ 86242 ]
■86241 / 1階層)  Re[1]: バイナリデータを読んで、日付などの形にする方法について
□投稿者/ Azulean (914回)-(2018/01/03(Wed) 00:44:46)
No86240 (へなちょこ さん) に返信
> このような処理として、以下の事がしたい場合は、どのように書けばよいのか、教えて
> 頂けないでしょうか?

どういった状況・目的で取り組んでいるのかわかりませんが、C 言語のデータ構造の読み方を独学でやりきるのは難しいでしょうし、質問が続いてしまうので、近場に頼れる人がいるならその人に教えてもらった方が良いと思いますよ。


> 色々と試してみたところ、「型が変換できません」のエラーが出てしまい、訳が分からなく
> なってます....

プログラミングは適当に試してもうまくいきません。
このコードでなぜそれができるのか? どういった仕組みなのか? を考察することも必要ですし、時にはいろいろと調べまくってキーワードをなんとか見つけるということも必要かと思います。
(VB.NET だけでうまく見つからないなら、C# 側のキーワードでも探してみて、どういったメソッドが使えそうか、探ってみるとかも必要かもしれませんが…)


> ・2バイト分のバイナリデータを読んで、Integer型にする。(10進数の値にしたいと思ってます。)

我々が数学などで扱う、普通の 10 進数では、1 桁で 0 〜 9 の 10 通りの表現ができ、1234 という数値は 1 * 1000 + 2 * 100 + 3 * 10 + 4 という数式で表現できます。

コンピューターの世界では 1 バイトが 8 ビットで構成されているので、256 パターンの数値表現となります。
256 になると、1つ上の桁=上位バイトに繰り上がり、[01] [00] となるわけです。

また、Intel 系 CPU ではリトルエンディアンが主流なので、下位バイトから順番に書き込む・読み込むことが一般的です。
よって、2 バイトで表現される数値は、以下のような計算で求まります。

Dim result As Integer = CInt(1 バイト目) + CInt(2 バイト目) * 256


> ・8バイト分のバイナリデータを読んで、日付の形にする。(「FILETIME 構造体」という
> 形のデータだと思います。)

こっちの話は「知っているか否か」が鍵になりますね。
FILETIME 構造体は以下のように 4 バイトの型が 2 つ並んだ構造体です。
https://msdn.microsoft.com/ja-jp/library/windows/desktop/ms724284

特にアラインメントなどもなく、8 バイトの連続したデータであることが正しいのであれば、BinaryReader.ReadInt64 で Long 型として読み込み、DateTime.FromFileTime メソッドで DateTime 型に変換すれば楽でしょうね。
[ 親 86240 / □ Tree ] 返信 編集キー/

▲[ 86241 ] / ▼[ 86243 ]
■86242 / 2階層)  Re[2]: バイナリデータを読んで、日付などの形にする方法について
□投稿者/ へなちょこ (6回)-(2018/01/03(Wed) 14:06:11)
ご回答ありがとうございます!

> どういった状況・目的で取り組んでいるのかわかりませんが、C 言語のデータ構造の読み方を独学でやりきるのは難しいでしょうし、質問が続いてしまうので、近場に頼れる人がいるならその人に教えてもらった方が良いと思いますよ。

おっしゃる通りです。近場に頼れる人がいれば、聞くことができるのですが、残念ながら該当する人がいない状態です.....。そのため、こちらで助けを求めさせていただいている次第です....
「C 言語のデータ構造の読み方」が分かっていないことが分かりました。ありがとうございます。


> プログラミングは適当に試してもうまくいきません。
> このコードでなぜそれができるのか? どういった仕組みなのか? を考察することも必要ですし、時にはいろいろと調べまくってキーワードをなんとか見つけるということも必要かと思います。
> (VB.NET だけでうまく見つからないなら、C# 側のキーワードでも探してみて、どういったメソッドが使えそうか、探ってみるとかも必要かもしれませんが…)

おっしゃるとおりです。
どうやら調べて出てきた情報が理解できないレベルなのが問題だと思います....
使えそうなメソッドの情報が見つかっても、それをどう使えばよいのかで悩んでしまうので....
ただ、今までに教えて頂いたことが元になって、色々と学ぶことができました。

> よって、2 バイトで表現される数値は、以下のような計算で求まります。
>
> Dim result As Integer = CInt(1 バイト目) + CInt(2 バイト目) * 256

ご丁寧に説明していただき、ありがとうございます!
教えて頂いた通りに試したところ、目的のデータが取得できました!ありがとうございます!

> 特にアラインメントなどもなく、8 バイトの連続したデータであることが正しいのであれば、BinaryReader.ReadInt64 で Long 型として読み込み、DateTime.FromFileTime メソッドで DateTime 型に変換すれば楽でしょうね。

こちらもご教授ありがとうございます!
記載して頂いた情報を元に検索をして調べたのですが、「BinaryReader.ReadInt64 で Long 型として読み込み」というのは出来て、以下のような値を入手できるのは確認しました。

バイナリデータ
50 EC 1D 47 C4 70 D2 01 24

Long型で取得した値
131291327456210000

最終的に表示したい実際の日時
2017/1/17 13:19:05

このLong型で取得した値を「DateTime.FromFileTime メソッドで DateTime 型に変換」でよいと思うのですが、以下のサイトの「使用例」をそのまま使うと、変な表示になってしまいます。

https://msdn.microsoft.com/ja-jp/library/system.datetime.fromfiletime(v=vs.110).aspx

サイトの「使用例」のFunction FileAgeに、引数としてLong型で取得した値をしていすると、以下の結果がでます。
出力結果→「350.15:21:09.8697527」

すみませんが、どうすれば「最終的に表示したい実際の日時」の形にできるか教えて頂けないでしょうか?

[ 親 86240 / □ Tree ] 返信 編集キー/

▲[ 86242 ] / ▼[ 86245 ]
■86243 / 3階層)  Re[3]: バイナリデータを読んで、日付などの形にする方法について
□投稿者/ Azulean (915回)-(2018/01/03(Wed) 14:37:57)
No86242 (へなちょこ さん) に返信
> すみませんが、どうすれば「最終的に表示したい実際の日時」の形にできるか教えて頂けないでしょうか?

書いても良いですが、一旦考えてもらおうと思って逆質問します。

・TimeSpan 型って何かご存知ですか? →知らない状態であればこの機会に調べてください。
・使用例の FileAge 関数はどういった処理をしていると考えていますか?読み解けませんか?
・"FileAge" という英語から来ている命名から意味を想像できませんか?
[ 親 86240 / □ Tree ] 返信 編集キー/

▲[ 86243 ] / ▼[ 86247 ] ▼[ 86252 ]
■86245 / 4階層)  Re[4]: バイナリデータを読んで、日付などの形にする方法について
□投稿者/ へなちょこ (7回)-(2018/01/03(Wed) 17:39:13)
ご回答ありがとうございます。

> ・TimeSpan 型って何かご存知ですか? →知らない状態であればこの機会に調べてください。

すみません、知りませんでした....
調べてみたのですが、「1時間」といった時間の表現をするものと捕らえました。

> ・使用例の FileAge 関数はどういった処理をしていると考えていますか?読み解けませんか?
> ・"FileAge" という英語から来ている命名から意味を想像できませんか?

TimeSpan 型のことを考えて使用例を見たところ、以下の事をしていると考えました。
・「現在の時間」と「ファイル作成時間」を取得後、その差を計算して、ファイルが出来てからどれくらい経過しているかを求めている。

間違っているでしょうか?

上記のことを参考に、以下のようにコードを書いたら、うまく日時情報が取れました!
*「getTime」には、Long型にした8バイトの情報が入っています。

Public Function TimeCal(ByVal getTime As Long) As System.DateTime
Try
Dim Time1 As System.DateTime
Time1 = System.DateTime.FromFileTime(getTime)
Return Time1
Catch exp As ArgumentOutOfRangeException
Throw
End Try
End Function

コード1行目の最後が「System.TimeSpan」だったため、「350.15:21:09.8697527」のような時間が出たのだと思っています。
これで大丈夫だと思っているのですが、おかしな所はありますでしょうか?
[ 親 86240 / □ Tree ] 返信 編集キー/

▲[ 86245 ] / ▼[ 86248 ]
■86247 / 5階層)  Re[5]: バイナリデータを読んで、日付などの形にする方法について
□投稿者/ Azulean (916回)-(2018/01/03(Wed) 19:36:41)
No86245 (へなちょこ さん) に返信
>>・TimeSpan 型って何かご存知ですか? →知らない状態であればこの機会に調べてください。
>
> 調べてみたのですが、「1時間」といった時間の表現をするものと捕らえました。

>>・使用例の FileAge 関数はどういった処理をしていると考えていますか?読み解けませんか?
>>・"FileAge" という英語から来ている命名から意味を想像できませんか?
>
> TimeSpan 型のことを考えて使用例を見たところ、以下の事をしていると考えました。
> ・「現在の時間」と「ファイル作成時間」を取得後、その差を計算して、ファイルが出来てからどれくらい経過しているかを求めている。
>
> 間違っているでしょうか?

合っています。
age っていうのが年齢とか、そういった意味のある言葉なので、ファイルが作られてからの経過時間という意図で命名しているのでしょうね。


> コード1行目の最後が「System.TimeSpan」だったため、「350.15:21:09.8697527」のような時間が出たのだと思っています。

それだけではないですよね?
Return するものを変更したはずです。

その「1行目の最後」は「戻り値の型」を示すだけであり、実際にはメソッド(関数)の中の「何を返す(戻す)か?」の部分が重要です。
そちらを書き落としていると、認識のずれにつながるので、差分を書き漏らさないようにしましょう。
[ 親 86240 / □ Tree ] 返信 編集キー/

▲[ 86247 ] / 返信無し
■86248 / 6階層)  Re[6]: バイナリデータを読んで、日付などの形にする方法について
□投稿者/ へなちょこ (8回)-(2018/01/03(Wed) 23:51:48)
> 合っています。
> age っていうのが年齢とか、そういった意味のある言葉なので、ファイルが作られてからの経過時間という意図で命名しているのでしょうね。

認識があっていて良かったです!
指摘して頂き、ありがとうございました。
お陰で勉強になりました!

> それだけではないですよね?
> Return するものを変更したはずです。
>
> その「1行目の最後」は「戻り値の型」を示すだけであり、実際にはメソッド(関数)の中の「何を返す(戻す)か?」の部分が重要です。
> そちらを書き落としていると、認識のずれにつながるので、差分を書き漏らさないようにしましょう。

なるほどですね。
今後は「何を返すのか?」をキチンと意識して、コードを読むようにします。

やりたいと思っていたことができましたので、この質問は「解決済み」とさせていただきます。

非常に助かりました!
ありがとうございました!
[ 親 86240 / □ Tree ] 返信 編集キー/

▲[ 86245 ] / ▼[ 86264 ]
■86252 / 5階層)  Re[5]: バイナリデータを読んで、日付などの形にする方法について
□投稿者/ 魔界の仮面弁士 (1529回)-(2018/01/05(Fri) 16:27:34)
No86245 (へなちょこ さん) に返信
> Public Function TimeCal(ByVal getTime As Long) As System.DateTime
>  Try
>   Dim Time1 As System.DateTime
>   Time1 = System.DateTime.FromFileTime(getTime)
>   Return Time1
>  Catch exp As ArgumentOutOfRangeException
>   Throw
>  End Try
> End Function

動作的には間違いというわけではないのですが、
このコードについて、少しだけ指摘させてください。


実は今回提示頂いたコードというのは、実質的には下記と同等です。

 Public Function TimeCal(ByVal getTime As Long) As Date
  Return Date.FromFileTime(getTime)
 End Function


参考にしたコードを真似ただけでしょうから、上記の書き方になるのも致し方ないのですが、
実際に使う際には、必ずしも例外処理は必要ではありませんし、今回のように
わざわざ Function 化するほどの必要性も無さそうです。

ゆえに今回は、Function を作らずとも FromFileTime メソッドを直接呼び出すだけで十分かと思います。

  'x = TimeCal(longValue)
  x = Date.FromFileTime(longValue)



以下、上記の書き換えについての補足。


> Public Function TimeCal(ByVal getTime As Long) As System.DateTime

As Long と As System.Int64 は同じ意味ですし、
As Date と AS System.DateTime も同じ意味です。

Integer や Long や Date は、Visual Basic の組み込み型(プリミティブな型)で、
Int32 や Int64 や DateTime は、.NET Framework で用意された共通言語ランタイムの型名です。

どちらを使っても良いのですが、引数が前者表記、戻り値が後者表記で
混在されているのが気にかかりました。

意図的に使い分けているのであれば良いのですが、もしかしたら
Date 型の存在をご存じなかったのでは…ということで紹介しておきます。


左側が .NET Framework で対応するデータ型で、
右側が Visual Basic が組み込みで持っているデータ型の名前です。
ここでは右側(組み込み型)のアルファベット順に列挙してみました。

System.Boolean 構造体  / Boolean 型 (ブール型)
System.Byte 構造体  / Byte 型 (バイト型)
System.Char 構造体  / Char 型 (文字型)
System.DateTime 構造体 / Date 型 (日付型)
System.Decimal 構造体  / Decimal 型 (10 進型)
System.Double 構造体  / Double 型 (倍精度浮動小数点数型)
System.Int32 構造体  / Integer 型 (整数型)
System.Int64 構造体  / Long 型 (長整数型)
System.Object クラス  / Object 型 (オブジェクト型)
System.SByte 構造体  / SByte 型
System.Int16 構造体  / Short 型
System.Single 構造体  / Short 型 (単精度浮動小数点型)
System.String クラス  / String 型 (文字列型)
System.UInt32 構造体  / UInteger 型
System.UInt64 構造体  / ULong 型
System.UInt16 構造体  / UShort 型



これを踏まえた上で、
>   Dim Time1 As System.DateTime
>   Time1 = System.DateTime.FromFileTime(getTime)
>   Return Time1
で使われている System.DateTime を Date 表記に書き換えると、
   Dim Time1 As Date
   Time1 = Date.FromFileTime(getTime)
   Return Time1
のようになります。(ただの別名表記なので、どちらでも結果は一緒です)

また、必ずしも変換結果を変数に受け取る必要は無いので、上記 3 行を
  Return Date.FromFileTime(getTime)
と一行にまとめて書くこともできます。コーディングに慣れるまでは
変数に一度受けておいた方が、処理の流れを追いやすいかも知れませんけれどね。


>  Catch exp As ArgumentOutOfRangeException
>   Throw
>  End Try
今回使用した FromFileTime メソッドが ArgumentOutOfRangeException の例外を発生させるケースは
 ・引数の値が小さすぎる(getTime が 0 未満だった場合)
 ・引数の値が大きすぎる(getTime が 2650467744000000000 以上の場合)
に限られます。

つまり、最小値と最大値は下記の範囲になります。
 Dim 最小値 = Date.FromFileTime(0L)
 Dim 最大値 = Date.FromFileTime(2_650_467_743_999_999_999L)


この範囲を超えた場合は、例外処理によって Catch 句に入るわけですが、
先のコードでは、Catch 句で「単に Throw しているだけ」なので、
実際にはこのような例外処理は、ほとんど意味が無かったりします。
解決済み
[ 親 86240 / □ Tree ] 返信 編集キー/

▲[ 86252 ] / 返信無し
■86264 / 6階層)  Re[6]: バイナリデータを読んで、日付などの形にする方法について
□投稿者/ へなちょこ (11回)-(2018/01/09(Tue) 21:19:27)
No86252 (魔界の仮面弁士 さん) に返信

こちらも返信が遅れて申し訳ございません!

> ゆえに今回は、Function を作らずとも FromFileTime メソッドを直接呼び出すだけで十分かと思います。
>
>   'x = TimeCal(longValue)
>   x = Date.FromFileTime(longValue)

なるほど。こんなシンプルなもので十分だったんですね。
確かに、ご説明いただいた通りですね。

> 意図的に使い分けているのであれば良いのですが、もしかしたら
> Date 型の存在をご存じなかったのでは…ということで紹介しておきます。

そういうことなんですね!
全然分かっていませんでした!
「なんで、これには「System.」が付いているんだろう?」って思っていたので、
ご説明いただき、大変助かりました!

> これを踏まえた上で、
>>  Dim Time1 As System.DateTime
>>  Time1 = System.DateTime.FromFileTime(getTime)
>>  Return Time1
> で使われている System.DateTime を Date 表記に書き換えると、
>    Dim Time1 As Date
>    Time1 = Date.FromFileTime(getTime)
>    Return Time1
> のようになります。(ただの別名表記なので、どちらでも結果は一緒です)
>
> また、必ずしも変換結果を変数に受け取る必要は無いので、上記 3 行を
>   Return Date.FromFileTime(getTime)
> と一行にまとめて書くこともできます。コーディングに慣れるまでは
> 変数に一度受けておいた方が、処理の流れを追いやすいかも知れませんけれどね。

なるほど。
どちらでも結果は一緒であっても、混乱しないように統一した方が良いですね。

>> Catch exp As ArgumentOutOfRangeException
>>  Throw
>> End Try
> 今回使用した FromFileTime メソッドが ArgumentOutOfRangeException の例外を発生させるケースは
>  ・引数の値が小さすぎる(getTime が 0 未満だった場合)
>  ・引数の値が大きすぎる(getTime が 2650467744000000000 以上の場合)
> に限られます。
>
> つまり、最小値と最大値は下記の範囲になります。
>  Dim 最小値 = Date.FromFileTime(0L)
>  Dim 最大値 = Date.FromFileTime(2_650_467_743_999_999_999L)
>
>
> この範囲を超えた場合は、例外処理によって Catch 句に入るわけですが、
> 先のコードでは、Catch 句で「単に Throw しているだけ」なので、
> 実際にはこのような例外処理は、ほとんど意味が無かったりします。

例外処理に関しては、これから整理していかなければいけないと思っていたので、考え方が分かり、
大変参考になりました!
他の箇所に関しても、どのような例外があるか考えてみます。

ご丁寧な説明、誠にありがとうございました!
また何か質問してしまうかもしれませんが、その際はご教授いただければ幸いです。
よろしくお願いします。
[ 親 86240 / □ Tree ] 返信 編集キー/

▲[ 86240 ] / ▼[ 86249 ]
■86244 / 1階層)  Re[1]: バイナリデータを読んで、日付などの形にする方法について
□投稿者/ Jitta (350回)-(2018/01/03(Wed) 17:36:42)
No86240 (へなちょこ さん) に返信

 プログラムというのは、pro: 前もって、gram: 書かれたもの、です。
前もって書かれたもの、つまり、する予定のことをあらかじめ書いておくことです。
何を書くのか。コンピュータに対する命令です。
「コンピュータに対する」を、「人」に置き換えてみてもいいでしょう。
「人に、何をするか、伝える」というのが、プログラム。
それをコンピュータが理解できる言語で表したものがプログラムコードです。
ということで、プログラムコードを書く前にプログラムを作りましょう。
すなわち、何を依頼しているのか、理解しましょう。

> reader.ReadBytes(2) '最初の2バイトは読み捨て
> Dim len1 As Integer = 2 * reader.ReadByte() '文字1のバイト数
> Dim len2 As Integer = 2 * reader.ReadByte() '文字2のバイト数
> この中で、文字1のバイト数として1バイトのデータを読んでInteger型の変数len1に代入
> その後、Byte()にしてString型にするという流れを行っていると思うのですが、

 len1 には、「1バイトのデータを読んで代入」ではなく、「1バイトのデータを読んで、2倍して、代入」しています。ここで、「2倍」している意図は何でしょうか。
 ところで、1行の中にコメントを押し込んでいますが、最近、特に .NET Framework でのコードでは、そういうことは推奨されていません。コメントは1行ごとに「何をしているか」ではなく、プログラム(することの流れ)の中で、「この時点で何を実現するか」を書くようにします。「何をするか」は頻繁に変わりますが、「何をしなければならないか」は、仕様変更に連動するだけです。つまり、仕様をコメントとして書いていけば、コメントとコードの違いがバグとして見えるようになります。


> 色々と試してみたところ、「型が変換できません」のエラーが出てしまい、訳が分からなく
> なってます....

 エラーが出たときは、どの行なのかというのが重要な情報です。
[ 親 86240 / □ Tree ] 返信 編集キー/

▲[ 86244 ] / 返信無し
■86249 / 2階層)  Re[2]: バイナリデータを読んで、日付などの形にする方法について
□投稿者/ ?????? (1回)-(2018/01/03(Wed) 23:52:29)
解決
解決済み
[ 親 86240 / □ Tree ] 返信 編集キー/


管理者用

- Child Tree -