質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.48%
C#

C#はマルチパラダイムプログラミング言語の1つで、命令形・宣言型・関数型・ジェネリック型・コンポーネント指向・オブジェクティブ指向のプログラミング開発すべてに対応しています。

Q&A

4回答

1477閲覧

C#でbyte型の保存について

momiji0210

総合スコア60

C#

C#はマルチパラダイムプログラミング言語の1つで、命令形・宣言型・関数型・ジェネリック型・コンポーネント指向・オブジェクティブ指向のプログラミング開発すべてに対応しています。

0グッド

0クリップ

投稿2022/01/13 06:07

編集2022/01/13 06:07

byte型での値の取り扱いの勉強をしております。
何となくそれっぽい機能ができたのですが、
List型のようにサイズが可変のものを保存する書き方がわかりませんでした。

下記みたいなソースコードの場合listValはどのように変換して保存、取り出しすればよいのでしょうか。

SizeOfとGetBufferで取得した値が違うのですが、Byte型に変換する前にGetBufferで取得できる値を計算することは可能でしょうか。
ここら辺でとれる値がなぜ違うのかよく理解できておりません。

C#

1 public struct DATA { 2 public int saveDataVersion; 3 public int intVal; 4 public float floatVal; 5 public string stringVal; 6 public List<int> listVal; // これを保存したい 7 } 8 9DATA data; 10int saveDataSize = 256; 11int size = System.Runtime.InteropServices.Marshal.SizeOf(data); 12Log("saveDataSize = " + size); // 24が取得 13 14using (MemoryStream stream = new MemoryStream(saveDataSize)) { 15 BinaryWriter writer = new BinaryWriter(stream); 16 writer.Write(test.data.saveDataVersion); 17 writer.Write(test.data.intVal); 18 writer.Write(test.data.floatVal); 19 writer.Write(test.data.stringVal); 20 stream.Close(); 21 data = stream.GetBuffer(); 22 Log(data.Length); // 256が取得 23}

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

退会済みユーザー

退会済みユーザー

2022/01/13 06:20

保存というのはどこにどういう形で行うのか分かりませんが、byte 型でなくても JSON 文字列とかでも良いのですか?
退会済みユーザー

退会済みユーザー

2022/01/13 07:04 編集

> int size = System.Runtime.InteropServices.Marshal.SizeOf(data); この Marshal.SizeOf はマネージド型を含んでいるから失敗して例外になると思いますけど。本当に24取得出来てるんですか?単にオブジェクトをファイルに保存したいだけであれば、既存のシリアライザを活用した方が良いと思いますが、絶対に生のバイナリデータで保存したいんでしょうか?
退会済みユーザー

退会済みユーザー

2022/01/14 01:24

そもそも何がしたかったのでしょうか? 目的を果たすのに役に立った回答があればそれにベストアンサーを付けて、このスレッドはクローズ願います。 期待する回答が無くて、質問を続けたい場合はその限りではないですが、であれば質問欄を編集して「そもそも何がしたかったのか」の情報を追記して、まだそれに対する回答を募集中であることを書いてください。
退会済みユーザー

退会済みユーザー

2022/01/14 02:32 編集

SurferOnWwwさんの言うように、ファイルに書き出す為には目的が重要になります。 例えば、BMPやPNG等の画像フォーマットのように決まった仕様があり、仕様に従って正確にバイト単位で書き出す必要があるのと、単純に自分のプログラムで使用しているオブジェクトの内容を保存したいだけで、ファイルフォーマットは何でもいい場合では、話が全然変わってきます。 後者の場合、プログラム書く前に、自身の用途に適したファイルフォーマットの選定から入る事になります。XMLやJSONのようにメジャーなフォーマットなら、出力方法はWeb上で検索するとすぐに出てくる筈です。独自のバイナリフォーマットで出力した場合、互換性を維持するのが困難になるので、あまりお勧めしません。
guest

回答4

0

自分用の独自の保存コード(シリアライザ)を作りたいのかと想定します。
可変長のデータを保存する場合はルールを決めて保存します。

書かれたコードだと string は本来可変ですが標準ライブラリが自動で可変長を 長さ+データ というルールで保存してくれています。例えば "abc" をBinaryWriterでシリアライズすると [03,'a','b','c'] となっていると思います。stringの場合はライブラリがやってくれますがそれ以外は自分で実装する必要があります。

この方法だと元に戻す(デシリアライズ)時にここに 長さ+文字列 があると事前に知っていないと戻せません。
また、長さ部分は1バイト表現(255文字まで)でよいのか、データの並び順はビッグエンディアンかリトルエンディアンか、など考えることは多いです。
そのため、これらを汎用化させたシリアライザーというライブラリがいくつもあります。jsonもその1種ですが
バイナリー形式での保存に興味があるのであれば通信系なら「BER符号化方式(タグ+長さ+データ)」、ライブラリなら「MessagePack」、「ProtoBuf」あたりを調べてみるといいでしょう。

投稿2022/01/14 02:00

編集2022/01/14 02:20
hqf00342

総合スコア273

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

0

List<int> を含めると、Marshal.SizeOf は異常終了するので、たぶんコメントアウトしているんでしょうね。

(1) 構造体のサイズについて

C#

1public struct DATA 2{ 3 public int saveDataVersion; 4 public int intVal; 5 public float floatVal; 6 public string stringVal; 7}

おそらく 64ビット環境だと思いますが、int, float は4バイト、string はクラスなので 64 ビットのポインタが構造体に格納されています。
ただし、ポインタは 64 ビット境界に置かれる(アライメントといいます)ので空き領域が 4 バイト出来ます。
4バイト(int) + 4バイト(int) + 4バイト(float) + 4バイト(空き領域) + 8バイト(string) = 24バイト

(2) MemoryStream.GetBuffer について

GetBuffer は、MemoryStrem の中に確保したバイト配列を取り出すだけなので、実際の長さとは違います。
長さは Length プロパティで取得してください。

(3) バイト配列に格納するには

構造体を Serializable でマークすると BinaryFormatter を使うことができます。

C#

1using System; 2using System.Collections.Generic; 3using System.IO; 4using System.Runtime.Serialization.Formatters.Binary; 5 6[Serializable] 7public struct DATA 8{ 9 public int saveDataVersion; 10 public int intVal; 11 public float floatVal; 12 public string stringVal; 13 public List<int> listVal; 14} 15 16var data = new DATA { 17 saveDataVersion = 1, 18 intVal = 2, 19 floatVal = 3.0f, 20 stringVal = "ABC", 21 listVal = new List<int> { 1, 2, 3, 4, 5, 6, 7 } 22}; 23 24var formatter = new BinaryFormatter(); 25using (MemoryStream stream = new MemoryStream(1024)) { 26 formatter.Serialize(stream, data); // 構造体を保存 27 long size = stream.Length; // 保存された長さを取得 28 29 stream.Position = 0; // ポインタを戻す 30 DATA tmp = (DATA)formatter.Deserialize(stream); // 構造体を取り出す 31}

(4) あらかじめ長さを計算できるか

int, float は4バイトですが、string や List は複雑なので、.NET のソース見て研究してください。

BinaryWriter.Write(string)
https://referencesource.microsoft.com/#mscorlib/system/io/binarywriter.cs,166b0572d9c907b3

List<T>
https://referencesource.microsoft.com/#mscorlib/system/collections/generic/list.cs

投稿2022/01/13 08:28

編集2022/01/13 08:46
KOZ6.0

総合スコア2626

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

momiji0210

2022/01/13 09:38

メチャクチャ丁寧にありがとうございます。なるほど・・・。どうしても計算が合わなかったのですが空き領域なるものが存在するのですね! Serializable についても知らなかったので助かります。 諸々自分の環境でも試してみます。
guest

0

質問のコメント、

保存というのはどこにどういう形で行うのか分かりませんが、byte 型でなくても JSON 文字列とかでも良いのですか?

に返事がないのでそれではダメなのかもしれませんが、JSON 文字列案を書いておきます。

以下のような Newtonsoft.Json を使って DATA オブジェクトを JSON 文字列にシリアライズすると、

using System; using System.Collections.Generic; using Newtonsoft.Json; namespace ConsoleApp1 { internal class Program { static void Main(string[] args) { var data = new DATA { saveDataVersion = 1, intVal = 2, floatVal = 3.0f, stringVal = "ABC", listVal = new List<int> { 1, 2, 3, 4, 5, 6, 7 } }; var jsonString = JsonConvert.SerializeObject(data, Formatting.Indented); Console.WriteLine(jsonString); var deserialized = JsonConvert.DeserializeObject<DATA>(jsonString); } } public struct DATA { public int saveDataVersion; public int intVal; public float floatVal; public string stringVal; public List<int> listVal; } }

結果は以下のようになります。(注: 見やすくするため、SerializeObject メソッドの第 2 引数に Formatting.Indented を設定しています)

{ "saveDataVersion": 1, "intVal": 2, "floatVal": 3.0, "stringVal": "ABC", "listVal": [ 1, 2, 3, 4, 5, 6, 7 ] }

上のコードにも書きましたが、JSON 文字列をデシリアライズして元の DATA オブジェクトを得ることもできます。

投稿2022/01/13 07:25

退会済みユーザー

退会済みユーザー

総合スコア0

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

momiji0210

2022/01/13 09:37

ご回答ありがとうございます!JSONについては過去実装したことがあり何となくかけそうでした。 今回、byte型という使ったことがないものだったのと、 通信APIの関係でbyte型でのやりとりできない環境があることも知りました。 そういった事情もあり勉強したくなったんですよね! ご丁寧にありがとうございました。
退会済みユーザー

退会済みユーザー

2022/01/14 01:15

そもそも何がしたかったのでしょうか? JSON 文字列にするのでは、「そもそも何がしたかったのか」の目的を果たすのに役に立たなかったですか?
guest

0

そのリストのサイズが取れるでしょうから、そのサイズ分保存すればいいです
提示のコードではlistValの保存のコードはないですね

#んで、C# シリアライズ、ってのでぐぐってみよう

投稿2022/01/13 07:13

編集2022/01/13 07:15
y_waiwai

総合スコア87774

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

まだベストアンサーが選ばれていません

会員登録して回答してみよう

アカウントをお持ちの方は

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.48%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問