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

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

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

Re[6]: バイナリデータの途中から切り出して出力する方法


(過去ログ 147 を表示中)

[トピック内 11 記事 (1 - 11 表示)]  << 0 >>

■86230 / inTopicNo.1)  バイナリデータの途中から切り出して出力する方法
  
□投稿者/ へなちょこ (1回)-(2018/01/01(Mon) 19:46:08)

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

見よう見まねでVBのプログラムをやっている初心者です。
読み込んだファイルを分割して表にしたいのですが、以下のようなことはどうすればできますでしょうか?

あるファイルを読み込んだところ、バイナリで見ると以下のようになっていたとします。

00 00 04 02 00 00 00 00 11 11 11 11 22 22
00 00 06 04 00 00 00 00 33 33 33 33 33 33 44 44 44 44

・最初の2バイトは取り出さなくてよいデータ
・3バイト目は後続する「文字1」の文字数
・4バイト目は後続する「文字2」の文字数
・5〜8の4バイトは不要なデータ
・文字1のデータ(2バイト文字が入り、長さは可変(3バイト目の値で長さが分かる))
・文字2のデータ(2バイト文字が入り、長さは可変(4バイト目の値で長さが分かる))

この「文字1」「文字2」を取り出して、データテーブルに代入したいと思っています。

読み込んだファイルは、このような構造のバイナリデータの集合体なので、引き続き
同じような処理が必要です。
(上のバイナリの例ですと、「00 00 04 02 00 00 00 00 11 11 11 11 22 22」の後に
また同じ構造のデータがあるので、連続して同じ処理を行い、データテーブルの新しい
列に代入したいという意味です。)

初心者なため、どのように説明すれば分かりやすいのかも分からないため、不足している
情報がありましたら、お教えください。

年始のお忙しいところ恐縮ですが、ご教授いただければ幸いです。
よろしくお願いします。


引用返信 編集キー/
■86233 / inTopicNo.2)  Re[1]: バイナリデータの途中から切り出して出力する方法
□投稿者/ Azulean (913回)-(2018/01/01(Mon) 22:20:11)
No86230 (へなちょこ さん) に返信
> 初心者なため、どのように説明すれば分かりやすいのかも分からないため、不足している
> 情報がありましたら、お教えください。

その 2 バイト文字列のエンコードは何ですか?
Shift_JIS ですかね?

ぱっと見の印象として、BinaryReader でできそうな予感はあります。

【BinaryReader の想定】
1.ReadBytes で 2 バイト読み捨てる。
2.ReadByte で得た値を CInt で Integer 型にして、文字1の文字数を得る。
3.ReadByte で得た値を CInt で Integer 型にして、文字2の文字数を得る。
4.ReadBytes で 4 バイト読み捨てる。
5.ReadBytes で2で得た文字数× 2 バイトのデータを読み込み、Encoding クラスを使って String 型に変換する。→文字1
6.ReadBytes で3で得た文字数× 2 バイトのデータを読み込み、Encoding クラスを使って String 型に変換する。→文字2
7.ファイルの終端まで1〜6を繰り返す。

文字列にする部分は以下の記事を参照。
https://dobon.net/vb/dotnet/string/getencoding.html
引用返信 編集キー/
■86234 / inTopicNo.3)  Re[2]: バイナリデータの途中から切り出して出力する方法
□投稿者/ へなちょこ (2回)-(2018/01/01(Mon) 22:40:21)
早速のご回答、誠にありがとうございます!

>その 2 バイト文字列のエンコードは何ですか?
>Shift_JIS ですかね?

文字列のエンコードは「UTF-16」です。

回答を拝見して、「BinaryReaderを使えば出来るんだ」ということは分かったのですが、
初心者なため、それをどう書けばよいか分かっていないので、WEB検索などをしてみて
使い方を勉強して、試してみようと思います。

うまく書けて、やりたいことが出来ましたら報告いたします。
まずは、ありがとうございます!
引用返信 編集キー/
■86235 / inTopicNo.4)  Re[1]: バイナリデータの途中から切り出して出力する方法
□投稿者/ 魔界の仮面弁士 (1525回)-(2018/01/01(Mon) 23:20:41)
No86230 (へなちょこ さん) に返信
> また同じ構造のデータがあるので、連続して同じ処理を行い、データテーブルの新しい
> 列に代入したいという意味です。)

とりあえず VB2017 向けのコードを書いてみました。

「ファイルが見つからなかった場合」や「ファイルレイアウトが破損していた場合」の
エラー対処は省略しています。


Imports System.IO
Module Module1

 Sub Main()
  For Each entry In EnumerateTuple("Sample.dat", System.Text.Encoding.Unicode)
   MsgBox(entry.Hena & vbCrLf & entry.Choco)
  Next
 End Sub

 Public Iterator Function EnumerateTuple(fileName As String, enc As System.Text.Encoding) As IEnumerable(Of (Hena As String, Choco As String))
  Using stm As New FileStream(fileName, FileMode.Open), reader As New BinaryReader(stm)
   Do While stm.Position < stm.Length
    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(bin1)
    Yield (txt1, txt2) '文字列1, 文字列2 のタプルを順次返却
   Loop
  End Using
 End Function

End Module
引用返信 編集キー/
■86236 / inTopicNo.5)  Re[2]: バイナリデータの途中から切り出して出力する方法
□投稿者/ へなちょこ (3回)-(2018/01/02(Tue) 12:53:45)
ありがとうございます!

文字数や文字列を取り出すところは、「こうすれば良いのかぁ」と納得させていただきました!
ご教授ありがとうございます!

ただ、私の勉強不足でコードをそのまま張り付けた際のエラーがよく分からない状態です。
「タプル」というのを始めて見たのが原因です.....。
「モジュール」も分かっていない始末です.....。
ちょっと情報収集して勉強します。

ちなみに、エラーが出てるのは以下の2か所です。
>  Sub Main()
>   For Each entry In EnumerateTuple("Sample.dat", System.Text.Encoding.Unicode)
>    MsgBox(entry.Hena & vbCrLf & entry.Choco)
>   Next
>  End Sub
>

「定義、またはインポートされてない」というエラー表示


>     Yield (txt1, txt2) '文字列1, 文字列2 のタプルを順次返却

こちらも同じ

引用返信 編集キー/
■86237 / inTopicNo.6)  Re[3]: バイナリデータの途中から切り出して出力する方法
□投稿者/ 魔界の仮面弁士 (1526回)-(2018/01/02(Tue) 20:44:33)
2018/01/02(Tue) 21:53:23 編集(投稿者)

No86236 (へなちょこ さん) に返信
>> とりあえず VB2017 向けのコードを書いてみました。
>
> ただ、私の勉強不足でコードをそのまま張り付けた際のエラーがよく分からない状態です。

VB2017 以降でないと使用できない構文を利用しています。
お使いのバージョンは何でしょうか?


> Public Iterator Function EnumerateTuple(

「Iterator」や「Yield」を使うには、VB2012 以降のバージョンが必要です。


> As IEnumerable(Of (Hena As String, Choco As String))

Visual Basic のタプル構文を使うには、VB2017 以降のバージョンが必要です。

System.Tuple クラスを使うには .NET 4.0 以降が必要です。

System.ValueTuple 構造体を使うには .NET 4.7 以降を採用するか、
もしくは nuget から System.ValueTuple を参照する必要があります。


ひとまず、VB2005 で動くと思われる程度にまでコードを書き換えてみました。
(これでも 2002/2003 だと動かないのですが)

Sub Main()
 For Each entry As String() In EnumerateTuple("Sample.dat", System.Text.Encoding.Unicode)
  MsgBox(entry(0) & vbCrLf & entry(1))
 Next
End Sub

Public Function EnumerateTuple(fileName As String, enc As System.Text.Encoding) As List(Of String())
 Dim list As New List(Of String())()
 Using stm As New FileStream(fileName, FileMode.Open), reader As New BinaryReader(stm)
  Do While stm.Position < stm.Length
   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(bin1)
   list.Add(New String() {txt1, txt2})
  Loop
 End Using
 Return list
End Function
引用返信 編集キー/
■86238 / inTopicNo.7)  Re[4]: バイナリデータの途中から切り出して出力する方法
□投稿者/ へなちょこ (4回)-(2018/01/02(Tue) 22:35:37)
ありがとうございます!!
また、お手数をお掛けしてしまい申し訳ありません!!

改めて教えて頂いたコードを使わせていただきましたら、うまくできました!

以下、いただいた質問の回答です。

> VB2017 以降でないと使用できない構文を利用しています。
> お使いのバージョンは何でしょうか?

使っているのは、VB2017です。
なので、うまくいかないのは私の能力不足です.....。
繰り返しになりますが、お手数をお掛けしました。

ひとまず、やりたいと思っていた処理ができるコードを教えていただけたので、「解決済み」とさせていただきます。
ただ、実は最終的に必要なコードがすべて出来ている訳ではないので、また質問をしてしまうかもしれません。
その際は、またご教授いただければ幸いです。

まずはお礼まで。
非常に助かりました!
ありがとうございました!!

解決済み
引用返信 編集キー/
■86250 / inTopicNo.8)  Re[5]: バイナリデータの途中から切り出して出力する方法
□投稿者/ 魔界の仮面弁士 (1527回)-(2018/01/05(Fri) 13:46:44)
解決済みのようですが、一応補足として。

No86238 (へなちょこ さん) に返信
> 使っているのは、VB2017です。

先の No86235 のコードは、プロジェクトを作成する際に
 [新しいプロジェクトの作成]
  [Visual Basic]
   [Windows クラシック デスクトップ]
    [コンソール アプリ (.NET Framework)] (.NET Framework 4.7.1)
を選択した場合のものです。
(上記以外にも利用可能な組み合わせがあります)



モジュールをご存じなかったという事は、今回はおそらく、
コンソールアプリ以外……たとえば、
  [Windows フォーム アプリケーション (.NET Framework)]
あたりを選択しており、かつ、.NET Framework のバージョンとして
『.NET Framework 4.7 未満』を選んでいたのであろうと想像します。

※ターゲット フレームワークが .NET Framework 4.7 もしくは 4.7.1 に
 なっていた場合には No86235 のコードをそのまま利用することができます。


もし、どのフレームワークを選択していたのか忘れてしまっている場合には、
ソリューション エクスプローラーの [My Project」をダブルクリックして、
「アプリケーション」タブの「ターゲット フレームワーク」の値を確認してみてください。

※ソリューション エクスプローラーが見つからない場合は、
 [表示]メニューを開くと、ソリューション エクスプローラーが見つかります。


プロジェクトの .NET Framework のバージョンが 4.7 未満だった場合には、
ターゲット フレームワークを 4.7 / 4.7.1 に上げることで、
No86235 のコードが動作するようになります。

そしてこれが、 No86237 の回答で
>>> System.ValueTuple 構造体を使うには .NET 4.7 以降を採用するか、
>>> もしくは nuget から System.ValueTuple を参照する必要があります。
と述べた部分に当たります。



もしも何らかの理由で、お使いの ターゲット フレームワークを
.NET Framework 4 / 4.5 / 4.5.1 / 4.5.2 / 4.6 / 4.6.1 / 4.6.2
のいずれかにしておきたい場合には、追加の参照設定を施すことで動作するようになります。

具体的には、メニューの [プロジェクト]-[nuget パッケージの管理] を開き、
「参照」タブをクリックしてから、検索ボックスに「System.ValueTuple」を入力して、
検索でヒットした System.ValueType を選択して『インストール』ボタンを押すことで、
必要なライブラリがプロジェクトに取り込まれ、先の
>>> 「定義、またはインポートされてない」というエラー表示
が回避され、 No86235 のコードが動くようになるはずです。

これが No86237 の回答で
>> System.Tuple クラスを使うには .NET 4.0 以降が必要です。
と述べた部分に当たります。


しかし、ターゲットフレームワークが .NET Framework 2 / 3.0 / 3.5 であった場合には、
そもそもタプルが利用できません。ターゲットフレームワークを 4.7 以降に変更するか、
No86237 で紹介した、タプルを使わないコーディングを選択してみてください。
解決済み
引用返信 編集キー/
■86251 / inTopicNo.9)  Re[5]: バイナリデータの途中から切り出して出力する方法
□投稿者/ 魔界の仮面弁士 (1528回)-(2018/01/05(Fri) 14:48:07)
情報過多になるかもしれませんが、さらに補足。


No86238 (へなちょこ さん) に返信
>>> 「タプル」というのを始めて見たのが原因です.....。

タプル(tuple、順組、組)は比較的新しい言語機能なので、
発行年の古い書籍等などでは触れられていないかもしれません。

英語で言うところの倍数詞に当たるものと説明されることもあります。
1-タプルが Single
2-タプルが Double
3-タプルが Triple
VB においては、名前の無い構造体型のようなものをイメージすると良いかも。


具体例で言うと…たとえば
  Dim Sei As String = "福澤"
  Dim Mei As String = "諭吉"
という 2 つのデータがあった場合、姓と名は別々に管理するよりも
一組のデータとして取り扱ったほうが分かりやすいですよね。

そこでたとえば、Class あるいは Structure を自作して
 Dim data As New MyStructure() 'MyStructure は自作のデータ型
 data.Sei = "福澤"
 data.Mei = "諭吉"
のようにしてまとめる様にするわけです。

これにより、「姓」と「名」という関連性のある 2 つのデータを
data という 1 つの変数にまとめることができます。


ところが、局所的にしか使わない変数のために、わざわざクラスや構造体を
用意するのは煩わしいこともあります。そこで、特に型名をつける必要が無いものに対しては、
タプル、すなわち System.Tuple および System.ValueTuple を利用できるようになりました。

.NET 4 以降では、「1〜8 個の要素を組み合わせたデータ」を「タプル」として
  'Dim data As New Tuple(Of String, String)("福澤", "諭吉")
  Dim data = Tuple.Create("福澤", "諭吉")
  MsgBox(data.Item1)
  MsgBox(data.Item2)
のようにして利用することができます。

しかしこの手法では、メンバー名が Item1〜Item8 と固定的であったため、
VB2017 以降においては言語機能を拡張して
  Dim data = (Sei:="福澤", Mei:="諭吉")
  MsgBox(data.Sei)
  MsgBox(data.Mei)
のように、メンバー名を指定できるタプル構文が追加されています。


なお、これと似たようなことは、「匿名型」でも実現できます。
匿名型は VB2008 以降で追加された構文であり、たとえば下記のように書きます。

  Dim data = New With {.Sei = "福澤", .Mei = "諭吉"}
  MsgBox(data.Sei)
  MsgBox(data.Mei)


しかし匿名型は、ローカル変数として使う分には良いのですが、
「メソッドの戻り値」や「引数」として使うのには向いていません。

  '型名を明示できないので、As Object にするしか無い
  Function Sample() As Object
    Return New With {.Sei = "福澤", .Mei = "諭吉"}
  End Function


タプルを使えば、戻り値や引数として使うことができるというメリットがあります。

  Function Sample() As (Sei As String, Mei As String)
    Return ("福澤", "諭吉")
  End Function




> 「モジュール」も分かっていない始末です.....。

VB6 などでは良く使われていた機能ですが、今となっては、どちらかといえば
限定的な場面(たとえば下記の URL)でしか使われない機能なので、
初心者のうちは、習得すべき優先度としては、やや低いと思います。
https://code.msdn.microsoft.com/windowsdesktop/9-0a98f2cd


ただ、新規プロジェクトの作成時に「コンソール アプリケーション」を選んだ場合には
自動的に Module が組み込まれますので、先の例ではそのまま貼り付けていました。
(なお、Module を使わずとも、コンソール アプリケーションを作ることはできます)



なお、VB.NET で「モジュール」というと Module ステートメントの事を指します。
https://code.msdn.microsoft.com/windowsdesktop/14-Visual-Basic-Module-2c407099

ところが上記の URL では、
≫ 「モジュール」 (標準モジュール) と呼ばれるもの
という表記が使われています。

これは .NET 以前の VB(VBA / VB6 など)では、VB.NET の Module に相当する機能が
「標準モジュール」と呼ばれていたためです。

VB4〜VB6 の頃は *.vb ファイルが無く、
  標準モジュール (VB.NET で言う所の Module Module1 などにあたる)の *.bas ファイル
  クラス モジュール (VB.NET で言う所の Class Class1 などにあたる)の *.cls ファイル
  フォーム モジュール (VB.NET で言う所の Class Form1 などにあたる)の *.frm ファイル
のように、複数の「モジュール」が存在していましたので、それらを区別するために
「標準モジュール」という用語が使われていたことによる名残です。


それと .NET Framework において「モジュール」というと、.netmodule というファイルの事を
指す場合がごく稀にあります。主に C++ 向けであり、VB / C# で使われることはまず無いですけれどね。
https://code.msdn.microsoft.com/windowsdesktop/18-netmodule-c96b3411
https://msdn.microsoft.com/ja-jp/library/aa719660.aspx
https://msdn.microsoft.com/ja-jp/library/0zyh2sf2.aspx
解決済み
引用返信 編集キー/
■86262 / inTopicNo.10)  Re[6]: バイナリデータの途中から切り出して出力する方法
□投稿者/ へなちょこ (9回)-(2018/01/09(Tue) 21:01:40)
No86250 (魔界の仮面弁士 さん) に返信

返信が大変遅れてしまい、失礼しました!
追加で記載して頂いたことに気づいておりませんでした。

> モジュールをご存じなかったという事は、今回はおそらく、
> コンソールアプリ以外……たとえば、
>   [Windows フォーム アプリケーション (.NET Framework)]
> あたりを選択しており、かつ、.NET Framework のバージョンとして
> 『.NET Framework 4.7 未満』を選んでいたのであろうと想像します。

確認したところ、おっしゃる通り、[Windows フォーム アプリケーション (.NET Framework)]で、
.NET Framework 4.6.1を選んでいました。
この辺の「どれを選べばどうなるか」も勉強しなければいけないと思いました。


> もしも何らかの理由で、お使いの ターゲット フレームワークを
> .NET Framework 4 / 4.5 / 4.5.1 / 4.5.2 / 4.6 / 4.6.1 / 4.6.2
> のいずれかにしておきたい場合には、追加の参照設定を施すことで動作するようになります。
>
> 具体的には、メニューの [プロジェクト]-[nuget パッケージの管理] を開き、
> 「参照」タブをクリックしてから、検索ボックスに「System.ValueTuple」を入力して、
> 検索でヒットした System.ValueType を選択して『インストール』ボタンを押すことで、
> 必要なライブラリがプロジェクトに取り込まれ、先の
> >>> 「定義、またはインポートされてない」というエラー表示
> が回避され、 No86235 のコードが動くようになるはずです。

.NET Frameworkのバージョンは、特に固定する理由はないので、ちょっと試してみます。
と言うか、バージョンの違いによって気を付けなければいけないことが何かが分かっていない
ので、そこから考えてみます。
引用返信 編集キー/
■86263 / inTopicNo.11)  Re[6]: バイナリデータの途中から切り出して出力する方法
□投稿者/ へなちょこ (10回)-(2018/01/09(Tue) 21:09:04)
No86251 (魔界の仮面弁士 さん) に返信

こちらも返信が遅れてしまい、失礼しました!

> タプル(tuple、順組、組)は比較的新しい言語機能なので、
> 発行年の古い書籍等などでは触れられていないかもしれません。

なるほど、そうなんですね。
情報が少ないと感じたのは、それが原因の一つなんですね。

タプルの説明ありがとうございます!
「配列みたいなものなのかな?」と思っていたので、説明を拝見して、ぼんやりと分かったような気がします。
キチンと分かるためには、自分で使って覚えないとダメそうです....

モジュールの説明もありがとうございます!
こちらも理解できていないので、追々、理解したいと思います。

> 初心者のうちは、習得すべき優先度としては、やや低いと思います。

了解しました。
まずはもっと優先度の高い項目から勉強していきます。

ありがとうございました!

引用返信 編集キー/


トピック内ページ移動 / << 0 >>

このトピックに書きこむ

過去ログには書き込み不可

管理者用

- Child Tree -