バイナリファイルの上書き、追加、保存をするには??【.NET】

解決


π+  2004-09-25 07:16:15  No: 116503

こんにちは><質問があります。Byte型配列に読み込んだファイルを編集して保存をするのはどうしたらいいのでしょうか。
−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−
Dim Buffer() as Byte
Dim DataFile As New System.IO.FileStream("c:\read.txt", IO.FileMode.Open)
Dim Size as Integer = CInt(DataFile.Length)
reDim Buffer(Size -1)
DataFile.Read(Buffer, 0, Size-1)
−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−

ファイル(c:\read.txt)の中身は:" my name is WINDOWSxp."です。
これをバイナリで表すと:"20-6D-79-20-6E-61-6D-65-20-69-73-20-57-49-4E-44-4F-57-53-78-70-00"になりました。

read.txtの"WINDOWS"の文字だけを、配列を使って"******"アスタリスク(バイナリ値:2A)で伏せたい書き換えたいのですが、

−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−
        For i = 0 To FileSize - 1
            Select Case Buffer(i)
                Case 87, 73, 78, 68, 79, 87, 73  ←W,I,N,D,O,W,Sの10進数(57 49 4E 44 4F 57 53)
                    Buffer(i) = 42     ←*の10進数
            End Select
        Next
−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−

として
" my name is *******xp."という結果を得たいんですけど、上のようにBuffer(i) = 42とすると保存できないのでしょうか。また、ファイルの上書きというのは、一旦配列に格納してそれを編集して出力して保存するという流れでいいのでしょうか?保存には、Writeをつかうみたいですがヘルプを見ても使い方がわからないんです><;お忙しい中すみません><;


π+  2004-09-25 09:08:47  No: 116504

Dim outFile As New System.IO.FileStream("d:\create.txt", System.IO.FileMode.Create, System.IO.FileAccess.Write)
新規に書き込むのはできました(>o<")!!嬉しい(>.<)


raki  URL  2004-09-25 19:18:01  No: 116505

π+さんはじめまして。
ファイルの上書きの流れは上記の通りで問題ありません。

>上のようにBuffer(i) = 42とすると保存できないのでしょうか。

保存できないというのはファイルに書き込めないということなのでしょうか?
上記のコードをほぼ流用しましたが、正常に変換・上書きできました。
以下にそのコードを記します。

Dim Buffer() As Byte
Dim DataFile As New System.IO.FileStream("c:\read.txt", IO.FileMode.Open)
Dim Size As Integer = CInt(DataFile.Length)

ReDim Buffer(Size - 1)
DataFile.Read(Buffer, 0, Size)
'DataFile.Read(Buffer, 0, Size - 1)  ' これでは最後の1バイトが読み込まれません

For i As Integer = 0 To UBound(Buffer)
    Select Case Buffer(i)
        Case &H57, &H49, &H4E, &H44, &H4F, &H57, &H53
            Buffer(i) = &H2A
    End Select
Next

' 書き込み位置を先頭に戻す(これが無いとファイルに追記されてしまう)
DataFile.Seek(0, IO.SeekOrigin.Begin)
' 書き込み
DataFile.Write(Buffer, 0, Size)
' ファイルを閉じる
DataFile.Close()


π+  2004-09-25 23:42:09  No: 116506

rakiさん始めまして!ありがとうございますm(。。)m!!
>'DataFile.Read(Buffer, 0, Size - 1)  ' これでは最後の1バイトが読み込まれません
すみません、勘違いしていました><

>保存できないというのはファイルに書き込めないということなのでしょうか?

Buffer(i) = 42などとしてTextbox1に表示するなどして、変更は確認できたんですが、保存ができなかったんです(書き込み方を知らなかったんです)><;Writeもいろいろためしてみましたが昨日ついに新規で書き込むことができました。データの上書きもrakiさんのおかげでわかりました!!

ここで疑問に思うことがあるのですが・・・、

−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−
        Dim Buffer() As Byte
        Dim i As Integer
        ReDim Buffer(10)

        【【【【  MsgBox(UBound(Buffer))  】】】】

        For i = 0 To UBound(Buffer) Step 1
            Buffer(i) = 255
        Next

        【【【【  MsgBox(UBound(Buffer))  】】】】

        Dim Mozi As String = "12345"
        Buffer = System.Text.Encoding.Default.GetBytes(Mozi)

        【【【【  MsgBox(UBound(Buffer))  】】】】
−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−

一つ目のメッセージボックスでは、配列の大きさは10
二つ目のメッセージボックスでも、配列の大きさは10
ところが・・
三つ目のメッセージボックスだと、配列の大きさは6になってしまいます。

        Buffer = System.Text.Encoding.Default.GetBytes(Mozi)

ここで、配列が変わってしまっていると思うのですが、変わらないようにするためにはどのようにすればいいのでしょうか><ご教授おねがいします><


魔界の仮面弁士  2004-09-26 04:31:17  No: 116507

# えぇと。πさんとπ+さんって、同一人物なのですよね?
http://madia.world.coocan.jp/cgi-bin/VBBBS2/wwwlng.cgi?print+200409/04090068.txt

> 【【【【  MsgBox(UBound(Buffer))  】】】】
VB独自の「UBound関数」(Microsoft.VisualBasic.Information.UBound メソッド)を
使う替わりに、標準的な「GetUpperBoundメソッド」を用いる事も出来ますね。

  'MessageBox.Show(Buffer.Length.ToString())
  MessageBox.Show(Buffer.GetUpperBound(0).ToString())

> 配列の大きさは10
配列の大きさは 11 では無いでしょうか?
添字の最大値が10というだけで。

蛇足ですが、下記のようなコードを使うと、下限値が 0 以外の配列を作成することが可能です。
もっとも、0以外で始まる配列を作っても、メリットはあまりありませんけれども。

  Dim Buffer() As Byte
  Dim Ary As Array

  '下限値 = 3, サイズ = 10。つまり、(3〜12) な配列を作成
  Ary = Array.CreateInstance(GetType(Byte), New Integer() {10}, New Integer() {3})
  MessageBox.Show(String.Format("下限={0},上限={1},サイズ={2}", LBound(Ary), UBound(Ary), Ary.Length))

  '下限値 = 0, サイズ = 10。つまり、(0〜9) な配列を作成
  Ary = Array.CreateInstance(GetType(Byte), New Integer() {10}, New Integer() {0})
  MessageBox.Show(String.Format("下限={0},上限={1},サイズ={2}", LBound(Ary), UBound(Ary), Ary.Length))

  'おまけ。VBの標準的な配列型の形式に変換。
  Buffer = CType(Ary, Byte())

>   Buffer = System.Text.Encoding.Default.GetBytes(Mozi)
> ここで、配列が変わってしまっていると思うのですが、

配列自体を変えたくないのであれば、
   System.Text.Encoding.Default.GetBytes(Mozi).CopyTo(Buffer, 0)
のように書く事で、Buffer 自身は変えず、Buffer の内容だけを
変更する事が、(一応は)可能です。

ただしこの場合、事前に Buffer に十分な領域を(ReDimなどで)用意しておく
必要がありますので、その点に関しては注意するようにしてください。

もしも、Buffer に十分な領域が用意されていなかった場合には、
実行時にエラー (ArgumentException例外) が発生してしまいますから…。

ただ、個人的には、上記のような書き方はお奨めしません。
変換後の配列のサイズは、エンコーディングによって大きく左右されるため、
エンコードによっては、必要な領域を事前に判断する事が難しいからです。
# まぁ、日本語環境であれば、通常、.Default は Shift_JIS になりますけれどね。

たとえば UTF-8 などであれば、「半角英数字は、1文字あたり1バイト」
「ギリシャ語なら、1文字あたり2バイト」「漢字は、1文字あたり3バイト」などと
文字種からサイズを判定できるのですが、エンコーディングによっては、
バイト数の判定に、より複雑な計算が必要となってしまいます。

たとえば iso-2022-jp の場合、
   With System.Text.Encoding.GetEncoding("iso-2022-jp")
       MessageBox.Show(.GetBytes("あ1い").Length.ToString())
       MessageBox.Show(.GetBytes("あい1").Length.ToString())
   End With
上記を実行していただくと分かりますが、同じ文字種が使われていても、
データの並び方によっては、必要なバイト数が異なってくるのです。

ではどうするかというと、一度、中間的な配列変数に
   WorkBuffer = System.Text.Encoding.Default.GetBytes(Mozi)
のように受け取り、この WorkBuffer のサイズを判断した上で、
   WorkBuffer.CopyTo(Buffer, 0)
などのように書きます。これなら、サイズが不足している場合には
コピー処理を中止したり、あるいはより大きな配列を使うなどする事で、
エラーの発生を回避することができるわけです。

> 変わらないようにするためにはどのようにすればいいのでしょうか
確かに、変化してしまうと困る時はありますね。

  Dim DataFile As New System.IO.FileStream("c:\test.txt", IO.FileMode.Open)
  Dim X(0) As Byte

  'この A には、X への参照を渡しています。
  'これらは同じインスタンスを共有しているため、
  'X の内容を書き変えると、A の内容も書き替わります。
  Dim A() As Byte = X

  'この B には、X の内容をコピーして渡しています。
  'B と X は別のインスタンスを参照しているため、
  'X の内容が変化しても、B の内容は変化しません。
  Dim B() As Byte = CType(X.Clone(), Byte())

  DataFile.Read(X, 0, 1)
  DataFile.Close()

  If X Is A Then
     MessageBox.Show("A は X と同じです。")
  Else
     MessageBox.Show("A と X とは異なります。")
  End If
  If X Is B Then
     MessageBox.Show("B は X と同じです。")
  Else
     MessageBox.Show("B と X とは異なります。")
  End If


魔界の仮面弁士  2004-09-26 05:04:22  No: 116508

> read.txtの"WINDOWS"の文字だけを、配列を使って"******"アスタリスク(バイナリ値:2A)で

それなら、バイナリで扱わなくても、String として処理できますよ。

  '元データをStringとして読み込む。
  Dim SJIS As System.Text.Encoding = SJIS.GetEncoding("Shift_JIS")
  Dim InputFile As New System.IO.StreamReader("C:\read.txt", SJIS)
  Dim SourceData As String = InputFile.ReadToEnd()
  InputFile.Close()

  'WINDOWS を ***** に置き換える。
  Dim ResultData As String
  ResultData = Replace(SourceData, "WINDOWS", "*******")

  '元のファイルの内容を書き換える。(別ファイルを指定してもOK)
  Dim OutputFile As New System.IO.StreamWriter("C:\read.txt", False, SJIS)
  OutputFile.Write(ResultData)
  OutputFile.Close()

>  For i = 0 To FileSize - 1

その前の部分には、『Dim Size as Integer = CInt(DataFile.Length)』と
書かれていますよね。なぜここでは、「FileSize」ではなく「Size」なのでしょうか?

>  Case 87, 73, 78, 68, 79, 87, 73  ←W,I,N,D,O,W,Sの10進数(57 49 4E 44 4F 57 53)
値が間違ってますよ。(^_^;)  これでは WINDOWN です。

その書き方に従って書くなら、
   Case 87, 73, 78, 68, 79, 87, 83
ですよね。

> For i = 0 To FileSize - 1
>     Select Case Buffer(i)
>         Case 87, 73, 78, 68, 79, 87, 73  ←W,I,N,D,O,W,Sの10進数(57 49 4E 44 4F 57 53)
>             Buffer(i) = 42         ←*の10進数
>     End Select
> Next
データの並びを考慮しなくて良いのでしょうか?
その手法だと、「MY WINDOWS.」→「MY *******.」のような変換だけではなく、
「YOUR DOS.」→「Y*UR ***.」とも変換されてしまいますよ。


π+(π)  2004-09-26 11:03:30  No: 116509

ただいまです!あ、、、師匠!!!!こんばんはです★レスありがとうございます!!本当助かります><;

こちら神奈川は雨が降ってきました★それと、πとπ+は同一人物ですよ^^!師匠に教えて頂いて、ほんのちょっぴりわかるようになってきたので、+をつけました^^vπ++からπ.NETになったりして(笑)はい、すみません、調子にのりました。

>配列の大きさは 11 では無いでしょうか?
そうでした!!UBoundって添え字の最大値でしたね><!ご指摘の通りです!1違っただけでエラーになってしまう処理もありますからね!気をつけます★

>蛇足ですが、下記のようなコードを使うと、下限値が 0 以外の配列を作成することが可能です。
えっ、すごい・・!何に使うかわからないですけど、、すごい・・!想像もつかないです><初心者はだまって+の配列使います^^

>  'おまけ。VBの標準的な配列型の形式に変換。
>  Buffer = CType(Ary, Byte())
ここここここ、こんな使い方もあるんですね・・!いったいどこからそのような知識を身につけるんですか><だって、調べ方が悪いのかもしれないですけど、ヘルプを見ても表現が難しくてわからないことが多いし、ネットを調べてもなかなかたどり着かないし、、う〜ん><すごすぎます><;

>System.Text.Encoding.Default.GetBytes(Mozi).CopyTo(Buffer, 0)
.CopyToにまではたどり着いたんですけど、、
System.Text.Encoding.Default.GetBytes(Mozi)の後に.をつけなかったです・・。ドットを付けるといろいろ出てくるじゃないですか、それについて怪しいのを調べたりするくらいしかできないんですよ><でも以前に比べたら大分進歩しました^^;

>変換後の配列のサイズは、エンコーディングによって大きく左右される
>データの並び方によっては、必要なバイト数が異なってくる

そおなんですよ><!!これも悩んでたひとつでした。いろいろ難しいことに当たって悩んで悩んで、、やっぱ答えは一つで、またそこで理解して、、また壁にぶち当たって、、なんかこう、突き進んでる感がたまらないんですけどね(笑)すみません、また調子にのりました^^;

>WorkBuffer = System.Text.Encoding.Default.GetBytes(Mozi)
こんなテクニックが!!!!!これを見たとき感動しました☆
しかもそのあと、
>WorkBuffer.CopyTo(Buffer, 0)
いやぁ、、クールですね><!!プログラムっておもしろい><!!

>この A には、X への参照を渡しています。
>  'これらは同じインスタンスを共有しているため、
>  'X の内容を書き変えると、A の内容も書き替わります。
>  Dim A() As Byte = X
>  'この B には、X の内容をコピーして渡しています。
>  'B と X は別のインスタンスを参照しているため、
>  'X の内容が変化しても、B の内容は変化しません。
>  Dim B() As Byte = CType(X.Clone(), Byte())

師匠!!教科書を読んでるみたいに、それ以上にわかりやすいです><
普通ここまで書いてないですもんね><さすがMVPです!!

>その前の部分には、『Dim Size as Integer = CInt(DataFile.Length)』と書かれていますよね。なぜここでは、「FileSize」ではなく「Size」なのでしょうか?
あっ、、間違っていました。実はですね・・・。.NETで書いてから、コードを覚えようと思って、コピペじゃなくて打ちながら書き込んでるんですよ^^;だから.NETで書いたものとゴッチャになってしまったんです^^;

>値が間違ってますよ。(^_^;)  これでは WINDOWN です。
ご指摘の通りでした^^;ミスです^^;

>データの並びを考慮しなくて良いのでしょうか?
お気遣いありがとうございます!!そおですね、確かにあの処理はWやIなどがあったらそれらも*になってしまいますよね!でもバイナリで上書きできることが試したかったんです><ありがとうございます。
でも、
>ResultData = Replace(SourceData, "WINDOWS", "*******")
Stringでやるとこんな簡単なんですね^^!!参考にさせてもらいます!!

みなさまのおかげで解決いたしました><!!深く深くお礼申し上げます><!!
まだ右も左もわからない若造ですが、どうぞよろしくお願いします!!!


π+  2004-09-26 11:16:46  No: 116510

あっ。。解決押すのわすれてしまいました><^^v


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

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






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