tagCANDY CGI VBレスキュー(花ちゃん) の Visual Basic 2010 用 掲示板(VB.NET 掲示板) [ツリー表示へ]   [Home]
一括表示(VB.NET VB2005)
タイトルバイト配列の表示
記事No10037
投稿日: 2010/04/02(Fri) 01:27
投稿者ふつうのこ
jpg画像をバイト配列として読み取って、表示させたいのですが
コンパイラに怒られまくりでうまくいきません。

バイナリエディタで読み込めばいいのですが自分の力で。

書いたコードは以下です。
    Dim fStm As New IO.FileStream("C:\test.jpg", IO.FileMode.Open)
    Dim File As New IO.BinaryReader(fStm)
    Dim binary() As Byte

    binary = File.ReadBytes(fStm.Length)
    textbox1.text = (binary)

多分バイト配列は読めてると思うのですが、表示がうまくいきません。
どこが悪いのでしょうか?

最終的にはpictureBoxにこの絵を表示したいのですが、
    PictureBox1.Image = バイナリ配列
として表示できるのでしょうか?

[ツリー表示へ]
タイトルRe: バイト配列の表示
記事No10038
投稿日: 2010/04/02(Fri) 09:32
投稿者魔界の仮面弁士
質問時には毎回、環境を明記するようにしてください。
内容によっては、それによって答えが変わってくる事もあります。

たとえば VB.NET 2003 と VB 2005 では、言語機能に大きな差異がありますし、
.NET Framework 1.0 と .NET Framework 4 とでは、使用できるクラスも異なります。


> jpg画像をバイト配列として読み取って、表示させたいのですが
> コンパイラに怒られまくりでうまくいきません。
この場合、クリアすべき課題は 3 点あります。

(1) ファイルを、バイト配列変数に読み込ませる処理。
(2) TextBox へ表示させるために、(1)で読み込んだデータを文字列表現に変換する処理。
(3) PictureBox へ表示させるために、(1)で読み込んだデータを Image クラスに読み込ませる処理。

なお、これには別解もあって、
 (3') (バイト配列からではなく)画像ファイルを PictureBox へ表示させる。
という実装も考えられます。


> 書いたコードは以下です。

まず (1) については、System.IO.File.ReadAllBytes(String) を使った方が手っ取り早いでしょう。
もちろん、BinaryReader + FileStream を使っても良いのですが、その場合、
ファイルを開いた後に、閉じる処理も必要となります(提示されたコードには閉じる処理が抜けています)。
# なお、System.IO.File.ReadAllBytes を使うには、.NET 2.0 (VB2005)以降が必要です。


次に (2) ですが、これはどのように表示させたいのかによって異なります。

画像をテキストアートで表現したいという意味だとすれば、コードは複雑化しますが、
単に、"FF-D8-FF-E0-……" のような 16 進数表現を行いたいだけであれば、
System.BitConverter.ToString(Byte())を使うのが簡単でしょう。


最後の (3) については、System.Drawing.Image.FromStream(Stream) を使えます。
この場合、必要なデータはバイト配列ではなくストリームとなりますので、
 (A案) バイト配列を MemoryStream に転記して、それを Image.FromStream で読み込む。
 (B案) 元ファイルをバイト配列に読み込ませず、FileStream を Image.FromStream に渡す。
などとします。ファイルを開きっぱなしにしないためにも、A案の方が良いと思います。


なお (3') の場合は、PictureBox1.LoadAsync("C:\test.jpg") と記述できます。
LoadAsync メソッドの代わりに Load メソッドを使ったりするのも手です。

[ツリー表示へ]
タイトルRe^2: バイト配列の表示
記事No10042
投稿日: 2010/04/02(Fri) 19:53
投稿者ふつうのこ
出来ました!(^^)
早速の回答ありがとうございます。
環境はWin2000+VB2005です。
XPもあるのですが、Win2000のノートを貰い、寝そべってやれるので
こちらばかり使っています。(^^;)

ファイルから直接表示出来れば簡単なんですが、画像をSQLServer2005に
保存して、そこから読み出したいのです。
jpgファイルとしてSQL Serverに保存できればいいのですが、
無理なんだろうと思ってバイト配列で保存することにしました。
jpgファイルなどバイナリファイルを直接指定して保存、
なんていうことが出来るのでしょうか?
パスを保存してもいいのですが、環境が変わったとき表示出来なくなるので、
バイト配列を読み取ってPictureBoxに表示したかったのです。
データ型はverbinary型でいいですよね?
image型にしようかと思いましたが、無くなるという事でしたので。

ヘルプにはReadAllBytes の事なんて全然載ってませんでしたから
もうちょっと分かるように書いてほしいです。
BitConverter.ToString のことにしても、PictureBox が、Imageクラス
を必要とする事も…。
とにかくヘルプは不親切過ぎます。

最後にちょっと思ったのですが、binary() as Byte とbinary as Byte()
ってどう使い分ければいいのでしょう?
同じようにも思えるし…。

[ツリー表示へ]
タイトルRe^3: バイト配列の表示
記事No10043
投稿日: 2010/04/02(Fri) 22:24
投稿者魔界の仮面弁士
> データ型はverbinary型でいいですよね?
varbinary では無く?


> ヘルプにはReadAllBytes の事なんて全然載ってませんでしたから
載っていますよ。My.Computer.FileSystem.ReadAllBytes にしても
System.IO.File.ReadAllBytes にしても。
http://msdn.microsoft.com/ja-jp/library/microsoft.visualbasic.fileio.filesystem.readallbytes.aspx
http://msdn.microsoft.com/ja-jp/library/system.io.file.readallbytes.aspx

また、HowTo として「Visual Basic でバイナリ ファイルを読み取る」という
記事も用意されています。
http://msdn.microsoft.com/ja-jp/library/9tk3bdxw.aspx


> もうちょっと分かるように書いてほしいです。
それは具体的に言うと、どのページをどのように書いて欲しいということでしょうか?
あるいは、どうすれば目的の情報にたどり着きやすいと考えておられますか?

上記 URL などに示した MSDN ライブラリでは、右上のリンクからフィードバックを
行う事ができますので、是非ともふつうのこさんの望む内容を投稿してみてください。
ここで呟くよりも、Microsoft の担当者に検討してもらえる可能性は高いと思います。


> とにかくヘルプは不親切過ぎます。
もっとも不親切と感じた点はどこでしょうか? どのようにして欲しいのでしょうか?
どのようなユーザー向けに記述して欲しいのでしょうか? そういった具体的な現場の声
(要望や提案等)を届けたいのであれば、下記のフォーラムを利用する事もできます。
http://social.msdn.microsoft.com/Forums/ja-JP/vddocumentsja/threads

先の MSDN サイトからの投稿だと、一方向的な報告で終わってしまいますが、
上記のコミュニティであれば、やりとりが一方向的では無く、Microsoft 社員や
他のユーザーも含めての意見交換ができるので、より建設的かと思います。


> 最後にちょっと思ったのですが、binary() as Byte とbinary as Byte()
> ってどう使い分ければいいのでしょう?
どちらも同じ意味です。複数の表現方法があるというだけで、.NET 的には
まったく同じ結果にコンパイルされますので、無理して使い分ける必要はありません。

強いて言えば、「Byte 型の配列変数」という表現と、「Byte配列型の変数」という表現の
どちらがシックリくるかで、どちらを使うか決めてしまえば良いのではないでしょうか。


以下余談:


歴史的な事情からいえば、Visual Basic では『Dim x() As Byte』の表現の方が一般的です。

元々、BASIC 言語では "DIM" とは、配列を宣言するためのステートメントでした。
(配列の事を、英語では dimension と呼びます)
http://ja.wikibooks.org/wiki/BASIC#.E9.85.8D.E5.88.97_DIM

そして当時は、配列を『DIM A(10)』のように記述していたため、
BASIC の流れを受け継ぐ Visual Basic も、それに似た記述となりました。


一方、As Byte() な書き方が登場したのは、Visual Basic 6.0 からです。
これは、関数(Function プロシージャ)の戻り値として使うために、
 Function GetBinary() As Byte()
のような表現が必要となったためです。

ただし VB6 当時は、変数宣言時に As Byte() 表現を使う事はできませんでした。
Dim 等でも As Byte() 形式で宣言できるようになったのは、
Visual Basic 7.0 (VB.NET 2002)からとなります。


なお、VB.NET と比較される事の多い C# においては、配列を作る際には
データ型の方に括弧を付けて
 byte[] x;
のように記述します。そのため、C# も併用するユーザーにとっては、
Dim x() As Byte よりも Dim x As Byte() の方が好まれるケースもあります。


もっとも、C# でも固定長配列を作る場合には、
 fixed byte x[3];
のように、変数側に括弧を付けることになるのですけれどね。(^^;

[ツリー表示へ]
タイトルRe^4: バイト配列の表示
記事No10044
投稿日: 2010/04/03(Sat) 03:26
投稿者ふつうのこ
丁寧なご回答ありがとうございます。

やってみたところ16進数表記ではダメみたいです。
Converter.Tostring()を使って変換した文字列を10進表記に出来ないでしょうか?
ループで回して変換する以外無いでしょうか?
せっかく教えてもらったのにすみません。

私の言ってた事は、そりゃ何処かに書いてはあるんでしょうけど、あんな膨大な文書の中から
何も知らずに探すのは無理じゃないですか。
偶然見つけられる事はあっても、見つけるのに1日じゃすまないと思います。
機能別の一覧を別に設けるとかすればいいのでは?

私がしようとした事は特殊な事でもなんでもなく、ごくありふれた誰もがやる事だと思いますので
プログラミングの達人の方ならそういう事例はすぐ思いつくでしょうから。
ヘルプを見るのは困ったから見るのであって、見ても全然役に立たないのはヘルプと
言えないと思います。

偉そうな事を言うようですが、私はそう思うのです。

[ツリー表示へ]
タイトルRe^5: バイト配列の表示
記事No10046
投稿日: 2010/04/03(Sat) 17:54
投稿者魔界の仮面弁士
> やってみたところ16進数表記ではダメみたいです。
具体的には、どの点において駄目という意味でしょうか?

・バイナリデータの表示にテキストボックスを使おうとした点。
・テキストを16進数として表記しようとした私の案。
・16進数変換に Conveter クラスを用いた私の回答。
・テキストからバイナリを復元するところで躓いてしまった。
・テキストからバイナリを復元することまではできたが、処理速度が遅くて使えない。
・テキストからバイナリを復元することはできたが、記述が冗長的になってしまった。


> Converter.Tostring()を使って変換した文字列を10進表記に出来ないでしょうか?
その「10進数表記」の文字列を生成する目的は何でしょうか?
理由を説明してもらえれば、別の解決策を提示できるかもしれません。

もしかしたら、そもそも16進数表記にする事自体が選定ミスであり、
最初から10進数表記の文字列にすべきだったのかも知れませんし。

そもそも、画像データの格納先は、SQL Server の varbinary 型なのですよね。
それを PictureBox だけではなく、TextBox にも表現させている理由も分かりません。


> ループで回して変換する以外無いでしょうか?
ループを使えば処理できたという意味でしょうか?
ループで都合が悪い理由が分かりませんが、ループを使いたくないのであれば、
Array.ForEach や Array.ConvertAll を使って変換するという手もあります。
(目的が分からないので、適切な回答になっているかどうかは分かりませんが…)

なお、10進数(あるいは16進数)で表記するという話は無かったことにして、
  バイナリ → 文字列(形式は不問) → バイナリ
という変換/復元を行いたいだけであれば、メールへのファイル添付などに使われている
「Base64」という技術を用いるという手もあります。
この場合には、ConvertクラスのToBase64String/FromBase64String メソッドを使えます。

なお、バイナリ/テキスト変換には、Base64 以外にも ish や uuencode など、
幾つかの形式があります。(VB.NET から使うなら、Base64 の方が手軽でしょうけれどね)

あるいは下記のように、SQL Server 側で変換させるという方法も考えられます。
データ量が少ない場合であれば有効かと。
http://blogs.msdn.com/sqltips/archive/2008/07/02/converting-from-hex-string-to-varbinary-and-vice-versa.aspx

その他、バイナリデータと一緒にファイル名や画像サイズなど、複数の情報も一緒に保持して
文字列化したいのであれば、XmlSerializer クラスを用いるという選択肢もあります。
もっとも、今回は SQL Server に格納するようなので、その必要は無いとは思いますけれどね。
http://dobon.net/vb/dotnet/file/xmlserializer.html


> あんな膨大な文書の中から何も知らずに探すのは無理じゃないですか。
是非、どのように改善すべきかを、具体的に検討・提案してみてください。
"逆引きサンプルコード" や "10行でズバリ!" なども、ユーザーの声から生まれてきた物ですし、
Visual Studio 2010 の新しいヘルプ ビューワーシステムにしても、そうした検討の結果です。
http://msdn.microsoft.com/ja-jp/cc974601.aspx
http://msdn.microsoft.com/ja-jp/ff363212.aspx


> あんな膨大な文書の中から
つまり、先の「もうちょっと分かるように書いてほしい」といっていた言葉は、
MSDN の内容に読み取りにくい記事があった、という意味というよりは、むしろ
目的の情報にたどり着けない/探しにくい、という意味だったのですね。


> 何も知らずに探すのは無理じゃないですか。
ヘルプを完読できるかといえば、それはほぼ不可能でしょうしね。
なので検索時間を短くするために、google や bing を併用したり、検索キーワードを
検討するなど、ある程度のテクニックや日頃の情報収集は必要になってくると思います。

[MSDN 検索術 〜検索テクニックを実例付きで解説!〜] … 検索サイトの活用術について実例付きで紹介
http://msdn.microsoft.com/ja-jp/kensaku.aspx

[MSDNの羅針盤] … 相当古い記事なので、現状に即さない部分もあるかも知れません
http://web.archive.org/web/20051029104919/www.galliver.co.jp/map/index.html


> 機能別の一覧を別に設けるとかすればいいのでは?
そもそも .NET のクラスは、名前空間によってある程度体系づけられていますが、
それとは別に、「機能」という括りでまとめられた資料が必要という事ですね。

ここでいう機能というのがどの範囲を指しているのか分からなかったのですが、
どのような一覧なのか、何か具体的な例を挙げることはできますか?
また、その一覧自体が結局また「膨大な文書」になってしまう事は無いのでしょうか?


> 偶然見つけられる事はあっても、見つけるのに1日じゃすまないと思います。
何も知らずに探すのは無理だとしても、以前見た(≠読んだ)ことがある記事であれば、
探し当てるのも容易となりますので、普段から目を通している人だと、検索も早いようです。


> ヘルプを見るのは困ったから見るのであって、見ても全然役に立たないのはヘルプと
> 言えないと思います。
MSDN の文書には、Visual Studio の開発環境(IDE)のヘルプとしての意味もありますが、
クラスの仕様解説書としての意味合いもあります。また、チュートリアルなどの入門者向けの
読み物も含まれていますし、サンプル等もあります。目的に応じて、それらを読み分けてみてください。

たとえば国語辞典は、「この言葉の意味は何だろう?」という時には役に立ちますが、
「こういう時には何と表現すれば良いのだろう?」という目的には使いにくいですよね。
そういう際には、逆引き辞典や文例集の類を利用するかと思いますが、それと同じことかと。

[ツリー表示へ]
タイトルRe^4: バイト配列の表示
記事No10045
投稿日: 2010/04/03(Sat) 04:34
投稿者ふつうのこ
あぁ、ダメでした。
ループの力技で10進に変換してやってみたのですが、"スタックが制限に達しました"
のSQLエラーが…。
4KB程の画像なんですがSQL文で登録は無理みたいです。
データセット使うしかないんでしょうか…。

[ツリー表示へ]
タイトルRe^5: バイト配列の表示
記事No10047
投稿日: 2010/04/03(Sat) 18:02
投稿者魔界の仮面弁士
> ループの力技で10進に変換してやってみたのですが、"スタックが制限に達しました"
> のSQLエラーが…。

どのように記述したのかを、『具体的』に提示していただけないでしょうか?

[ツリー表示へ]
タイトルRe^4: バイト配列の表示
記事No10048
投稿日: 2010/04/03(Sat) 23:30
投稿者ふつうのこ
完成しました!(^^)
今まで付き合ってくださりほんっとに感謝します。

実はSQLエラーが出た時、諦めてパスの格納にしようと思ってたんです。
ヘルプの事で偉そうな事を言ったから心象悪くしちゃってもうレスが
付かないと思ってましたから。
でもレスしてくださって、とことんやってみようと決心しました。

完成するまで付き合ってくださいねってお願いしようとしたんですが、
その前に報告する事を再確認しなきゃならないなって思い再びPCに
向かったところ、また熱中しちゃって。
で、完成しちゃった訳です。

今まで付き合ってくださりありがとうございました。

こんなに付き合ってくださったのですから、ちゃんと報告する義務がありますよね。
恥をさらすようですが、言います。
長くなるかもしれませんが…。

まずDBへのバイナリデータの登録にデータセットを使うサンプルはWebに
あったんですが、データセットの使い方はあまり分かってなくて、
SQLを発行して登録、取得するやり方で行こうと思いました。
そのためにはバイナリデータをテキストとして取得しなければならないので、
その質問がこのスレッドの始まりです。
だからTextBoxに表示するっていうのは只の演習でテストだったのです。

バイナリをテキストに変換出来て、次はSQLServerへの登録です。
 Insert Into …Values(…,…,"FF-D8-FF-E0-…")
ってやると、"String型をByte型に変換できません"って怒られたので、
それじゃあってことで、Convert(varbinary, "FF-D8-…")としました。
すると"FFはなんたらかんたら"って怒られたので、16進表記はダメなんだ?
って思って、試しに別のvarbinary型のDBを作ってテストしてみると
 Insert Into TestTable Values(1, Convert(varbinary, "255"))
のSQL文はちゃんと通ります。
 Insert Into TestTable Values(1, Convert(varbinary, "255-216-255"))
のSQL文もちゃんと通ります。
そっか、16進を10進に直さなきゃならないのかと思って質問したのが
前の前の質問です。

その時は真夜中でしたのでレスしてもらう間も惜しく、自分で10進に変換して
本番ソフトで試してみました。
すると、"スタックが制限に達しました"のエラーが…。
もう目の前真っ暗で、もう諦めよう…パスを入れよう…。
いや、データセットの方法はあるけどあまり分かってないしなぁ…などと
思いつつ、床に入ったのでした。
その時の報告が、前の投稿です。

もう諦めよう、パスを入れようと思ってここを覗いてみると、仮面弁士さんから
レスが付いてるじゃありませんか。
もう諦めようと思っていた心がだんだんとしぼんでいき、とことんやってやるぅ!
って思いに変わっていったのです。

で、報告の確認の為に再びPCに向かうと、あれこれやっている内に
またのめり込んでいったんです。

いろいろやってましたがダメで、SQLServerのヘルプでConvertをよく確認してみると、
"16進の前には0xが必要です"ってしてあります。
そうだ、忘れてた、C言語系のにも必要だし付けないと。
それからテストで作ったDBでいろいろ試しているうちに、0xFFD8FFE0…って表記で
いい事が分かったんです。
それが分かれば後は一直線でした。
途中でConvertがあるとダメな事でつまづきましたが、完成したって訳です。

ヘルプにバイナリデータの登録法がサンプルでちょこっとでも書いてあれば
こんなに苦労する事も無かったでしょうに。(^^;)
ヘルプが不親切って思うのはこういうところなんです。

とにかくありがとうございました。
長々と書いてしまいましたが、これにて失礼します。

[ツリー表示へ]
タイトルRe^5: バイト配列の表示
記事No10049
投稿日: 2010/04/05(Mon) 15:23
投稿者YuO
> SQLを発行して登録、取得するやり方で行こうと思いました。
> そのためにはバイナリデータをテキストとして取得しなければならないので、
> その質問がこのスレッドの始まりです。

バイナリデータの取り扱いを,テキストでおこなう必要はありませんよ。
# 内部ではテキストになっているかもしれませんが。

Dim command = New SqlCommand("INSERT INTO ... VALUES ( .... @B )", connection, transaction)
command.Parameters.Add(New SqlParameter("@B", data)) ' data : Byte配列
command.ExecuteNonQuery()
のように,パラメータ化する必要はありますが。


> ヘルプにバイナリデータの登録法がサンプルでちょこっとでも書いてあれば
> こんなに苦労する事も無かったでしょうに。(^^;)

MSDN: ファイルからの画像の挿入 (ADO.NET)
http://msdn.microsoft.com/ja-jp/library/4f5s1we0.aspx

[ツリー表示へ]
タイトルRe^6: バイト配列の表示
記事No10052
投稿日: 2010/04/09(Fri) 22:31
投稿者ふつうのこ
アドバイスありがとうございます。
返事が遅くなってごめんなさい。

テキストでもデータセットと同じような事が出来るんですね。
これなら直す部分も少なくて済みそうです。
データセットを使いたくなかったのは、直す部分が多すぎるから、っていう理由も正直あったんです。(^^;)

実際ちょっと大きい画像データ(と言っても70KB程度)の場合、12〜3分かかって使い物にならない
っていうのはあったんです。
実際使うデータは 10KB程なので何とか使い物にはなってますが。(それでも3〜40秒かかります)

70KBのデータの時に計ったんですが、BitConverter.Tostringが終わるまで約1分強ですので、
ループで10分以上かかっている計算になります。
約23000回のループってこんなに時間がかかるものなんでしょうか?
これでも Stringを StringBuilderに変えてみたんですが。

バイナリエディタで読むと100KBのデータでも一瞬で読み込んで表示してくれます。
バイト配列をハイフン無しで、FFD8FFE0…という風に表示してくれる関数って無いのでしょうか?
バイナリエディタはなぜあんなに早いのでしょうか?

[ツリー表示へ]