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

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

新規登録して質問してみよう
ただいま回答率
85.31%
.NET Core

.NET Coreは、マネージソフトウェアフレームワークでオープンソースで実装されています。クロスプラットフォームを前提に考えられており、Windows/Mac/Linuxで動くアプリケーションを作成することが可能です。

C#

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

Q&A

1回答

2919閲覧

C#でByte配列を特定のByte数ごとに切り出してBigIntegerに変換した後、Byte配列を復元したいが、なぜか最後の277Byteだけ値が変わってしまう

poekura

総合スコア1

.NET Core

.NET Coreは、マネージソフトウェアフレームワークでオープンソースで実装されています。クロスプラットフォームを前提に考えられており、Windows/Mac/Linuxで動くアプリケーションを作成することが可能です。

C#

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

0グッド

0クリップ

投稿2023/05/03 07:25

編集2023/05/03 08:12

実現したいこと

好きな長さのByte配列

256byteずつ切り出してそれぞれBigIntegerに変換しBigIntegerの配列を作成

BigIntegerの配列から元のByte配列を復元

発生している問題

C#のコンソールアプリケーションを作っています。
上記のことを実装中に、ほとんどはうまく変換、逆変換されもとのByte配列に戻ってくれるのですが、最後の277Byteのみなぜか全く違う値になってしまいます。
色々考えてみたのですが原因を見つけられません。
Byte配列→BigInteger配列の変換、BigInteger配列→Byte配列の変換のソースコードは以下の通りです。
(あまり質問したことがないためわかりづらい部分等あったら申し訳ありません。。。)

該当のソースコード

変数名説明
byArray(byte配列)与えられるByte配列
bintArray(BigInteger配列)Byte配列から変換してできるBigInteger配列
nowbyArray(byte配列)切り出したByte配列
C(int)切り出す値の長さ、今回は256

C#

1//バイナリ配列→BigInteger配列変換 2 public static BigInteger[] by2bint(byte[] byArray) 3 { 4 int C = 256; 5 6 BigInteger[] bintArray = new BigInteger[0]; 7 for(int i=0; i<(double)byArray.Length/C; i++) 8 { 9 byte[] nowbyArray; 10 //byArrayから256ずつ切り出していくと最後に256以下のByteが余るので、その時だけ切り出すByte数を調節しています 11 if (byArray.Length < (i + 1) * C) 12 { 13 nowbyArray = new byte[byArray.Length - i * C]; 14 Array.Copy(byArray, i * C, nowbyArray, 0, nowbyArray.Length) 15 } 16 else 17 { 18 nowbyArray = new byte[C]; 19 Array.Copy(byArray, i * C, nowbyArray, 0, C); 20 } 21 22 Array.Resize(ref bintArray, bintArray.Length + 1); 23 bintArray[i] = new BigInteger(nowbyArray, true, true); 24 } 25 26 27 return bintArray; 28 } 29 30 31 //BigInteger配列→バイナリ配列変換 32 public static byte[] bint2by(BigInteger[] bintArray) 33 { 34 int C = 256; 35 36 byte[] byArray = new byte[0]; 37 byte[] nowbyArray; 38 int x = 0; 39 foreach(BigInteger bint in bintArray) 40 { 41 nowbyArray = bint.ToByteArray(true, true); 42 43 int l = byArray.Length;//この時点でのbyArrayの長さを保存して、byArrayの要素数を増やした部分に書き込めるようにしています 44 45 Array.Resize(ref byArray, byArray.Length + nowbyArray.Length); 46 Array.Copy(nowbyArray, 0, byArray, l, nowbyArray.Length); 47 x++; 48 } 49 50 return byArray; 51 }

補足情報(FW/ツールのバージョンなど)

.net core 3.1で、System.Numericsを使用しています。

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

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

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

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

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

退会済みユーザー

退会済みユーザー

2023/05/03 07:38

> int C = 256; これは何ですか? その先もそういう調子ではないかと思ってしまいます・・・
poekura

2023/05/03 08:13

最初に今回切り出すbyte配列の要素数を指定しています(今回であれば256です) また、変数の表に表記ミスがあったため修正しました
ozwk

2023/05/03 08:39

> 最後の277Byte 277byte より少ない入力はどうなりますか?
退会済みユーザー

退会済みユーザー

2023/05/03 10:10 編集

何で 256 でなくて 256 なのですか?
otn

2023/05/03 09:27

> これは何ですか? その先もそういう調子ではないかと思ってしまいます・・・ コメントの意味が伝わってないようなので、横から解説すると、 「半角文字と全角文字の違いに無頓着な人の書いたコードは最初から信用できない」 という意味でしょう。 質問文のコードは手入力じゃなくてコピペしましょう。
Zuishin

2023/05/03 09:55

超絶テクニックが色々使われているので読むのに苦労します。 もっと普通に組めば別の結果が得られるかもしれません。 まず int を double にして int と比較しているところから脳が拒否反応を示し始めます。
poekura

2023/05/03 10:57

>コメントの意味が伝わってないようなので(略) あーすいません、その部分、実は元は違う表記だったのですがそうするより普通に書いたほうがいいということをこちらにコピペした後で気づいて書き換えたんですよね。今使っているフォントが全角数字と半角数字を全く同じように表示してしまうので気が付きませんでした。 >超絶テクニックが(略) 完全に独学なもので、なにが普通かわからないんですよね int を doubleにして intと比較、というのもこれ以外にいい方法を思いつかなかったんです。(割り算の答えを小数にしたかっただけなのですが)
poekura

2023/05/03 11:01

>277byte より少ない入力はどうなりますか? 確認したところ、全体が全く違う値になっていました
Zuishin

2023/05/03 11:15

byArray.Length が 384 だった場合、384 / 256 = 1.5 です。 i < 1.5 の間ループするということは、i は 0 か 1 の二つの値を取り、二回ループします。 i が 1 の場合、byArray.Length - i * C は 384 - 256 = 128 になります。 一回目のループで 0 から 255 までのデータを使っているので、二回目のループで 128 からのデータを使うと重複します。 一方、復元の方は重複を考慮に入れていません。
YAmaGNZ

2023/05/03 11:15

これ復元できるのですか? Byte[] a = {0,1} これすら復元できないのでは?
otn

2023/05/03 11:20

> こちらにコピペした後で気づいて書き換えたんですよね。 正しいやり方は、元プログラムの方を 256 に書き直した上で、再度実行して同じ結果になることを確認した上で、元プログラムを改変せずコピペです。 まあ、ちゃんとコピペしてない質問者は多数いるし、半角でキーインしていれば、誰も改変に気づかなかった所なので、別にこのままで良いですけど。
Zuishin

2023/05/03 11:22

嘘を書きました。 私の書いたのは読み間違えなので無視してください。
poekura

2023/05/03 11:36 編集

>>Zuishin byArray.Length - i * C はあくまでコピーする数を扱っています。 実際には下の行のArray.CopyでbyArrayの i * C 個目からの部分を nowbyArrayの0個目からの部分へbyArray.Length - i * C (= nowbyArray.Length)個コピーしているので重複は起こらないかと。 byArray.Length が 384 だった場合でいうと、一回目(i = 0)はbyArrayの 0 * C = 0個目から256個nowbyArrayにコピーして、二回目(i = 1)はbyArrayの 1 * C = 256個目からbyArray.Length - i * C、つまり384 - 256 = 128個nowbyArrayへコピーします。
poekura

2023/05/03 11:39

>>Zuishin ああ、ちょうどすれ違いになってしまったみたいですね。わかりました。
Zuishin

2023/05/03 11:57 編集

とにかく 256 づつに区切ることができればいいのであれば、Chunk を使い、Select で BigInteger に変換し、ToArray すると見通しよくなります。 .NET Core 3.1 だと Chunk はないので、InteractiveExtensions を導入して Buffer を使うか、自作します。 すると条件分岐も無くなり、by2bint が一行に収まります。 あるいは for を for (i = 0; i < byArray.Length; i += C) のようにし、List<BigInteger> に Add する形にすると見やすくなります。 これは Math.Min を使えば条件分岐不要です。 戻り値が配列でないといけないのであれば、最後に ToArray で配列にできます。 あるいは Span<T> を使い、256 毎に Slice すれば、ややこしい計算も条件分岐も排除できます。
guest

回答1

0

YAmaGNZさんも指摘されていますが、頭にゼロが来ていませんか?
前後で配列サイズはあっていますか?

使用できる最も少ないバイト数を使用して、この BigInteger の値をバイト配列として返します。

BigInteger.ToByteArray メソッド (System.Numerics) | Microsoft Learn

先頭ゼロさえ来なければあってそうな気もしますが、そうでもないなら再現する(短めの)データが欲しいですね^^;

.NET Core3.1は既にサポート切れですので、.NET7を(C#8.0に落として)使用しました^^
.NET および .NET Core オフィシャル サポート ポリシー

cs

1using System; 2using System.Linq; 3using System.Numerics; 4 5 6namespace Qrxl0vycu5kh9f9 7{ 8 class Program 9 { 10 static void Main() 11 { 12 { 13 // ゼロから始まると短くなってしまう 14 var a = new byte[] { 0, 1, 2, 3 }; 15 var b = new BigInteger(a, true, true); 16 var c = b.ToByteArray(true, true); 17 Console.WriteLine(string.Join(", ", c)); // 1, 2, 3 18 } 19 20 21 { 22 // チャンク先頭にゼロが来るので3byte減った 23 var a = Enumerable.Range(0, 533).Select(i => (byte)(i % 256)).ToArray(); 24 var b = bint2by(by2bint(a)); 25 Print(a, b); // a.Length:533 b.Length:530 SequenceEqual:False 26 } 27 { 28 // 1個目チャンクの先頭ゼロで1byte減った 29 var a = Enumerable.Range(0, 533).Select(i => (byte)(i % 200)).ToArray(); 30 var b = bint2by(by2bint(a)); 31 Print(a, b); // a.Length:533 b.Length:532 SequenceEqual:False 32 } 33 { 34 // チャンク先頭にゼロが来なければあってそう? 35 var a = Enumerable.Range(1, 533).Select(i => (byte)(i % 256)).ToArray(); 36 var b = bint2by(by2bint(a)); 37 Print(a, b); // a.Length:533 b.Length:533 SequenceEqual:True 38 } 39 40 41 try 42 { 43 // 最終チャンクに先頭ゼロが来ると(トータルサイズを知らないと)戻しようがない 44 var a = Enumerable.Range(0, 533).Select(i => (byte)(i % 256)).ToArray(); 45 var b = BigIntArray2ByteArray(ByteArray2BigIntArray(a)); 46 Print(a, b); 47 } 48 catch (Exception e) 49 { 50 Console.WriteLine($"{e.Message}\n"); // 復元不可能なsourceです 51 } 52 { 53 // 最終チャンク以外の先頭ゼロは分割サイズを知っていれば戻せるが... 54 var a = Enumerable.Range(0, 533).Select(i => (byte)(i % 200)).ToArray(); 55 var b = BigIntArray2ByteArray(ByteArray2BigIntArray(a)); 56 Print(a, b); // a.Length:533 b.Length:533 SequenceEqual:True 57 } 58 { 59 var a = Enumerable.Range(1, 533).Select(i => (byte)(i % 256)).ToArray(); 60 var b = BigIntArray2ByteArray(ByteArray2BigIntArray(a)); 61 Print(a, b); // a.Length:533 b.Length:533 SequenceEqual:True 62 } 63 } 64 65 static BigInteger[] by2bint(byte[] byArray) 66 { 67 var C = 256; 68 var bintArray = new BigInteger[0]; 69 70 for (var i = 0; i < (double)byArray.Length / C; i++) 71 { 72 byte[] nowbyArray; 73 if (byArray.Length < (i + 1) * C) 74 { 75 nowbyArray = new byte[byArray.Length - i * C]; 76 Array.Copy(byArray, i * C, nowbyArray, 0, nowbyArray.Length); 77 } 78 else 79 { 80 nowbyArray = new byte[C]; 81 Array.Copy(byArray, i * C, nowbyArray, 0, C); 82 } 83 84 Array.Resize(ref bintArray, bintArray.Length + 1); 85 bintArray[i] = new BigInteger(nowbyArray, true, true); 86 } 87 88 return bintArray; 89 } 90 static byte[] bint2by(BigInteger[] bintArray) 91 { 92 var byArray = new byte[0]; 93 foreach (var bint in bintArray) 94 { 95 var nowbyArray = bint.ToByteArray(true, true); 96 var l = byArray.Length; 97 98 Array.Resize(ref byArray, byArray.Length + nowbyArray.Length); 99 Array.Copy(nowbyArray, 0, byArray, l, nowbyArray.Length); 100 } 101 102 return byArray; 103 } 104 105 106 107 static BigInteger[] ByteArray2BigIntArray(byte[] source, int size = 256) 108 { 109 if (source == null) throw new ArgumentNullException(nameof(source)); 110 if (size < 1) throw new ArgumentOutOfRangeException(nameof(size)); 111 112 if (source.Length == 0) return Array.Empty<BigInteger>(); 113 114 115 var dest = new BigInteger[(source.Length + size - 1) / size]; 116 117 var span = source.AsSpan(); 118 for (var i = 0; i < dest.Length - 1; i++) 119 { 120 dest[i] = new BigInteger(span[..size], true, true); 121 span = span[size..]; 122 } 123 124 if (span[0] == 0) throw new ArgumentException("復元不可能なsourceです"); 125 126 dest[^1] = new BigInteger(span, true, true); 127 128 return dest; 129 130 131 // 先頭ゼロを無条件で弾いていいなら戻すのも楽なのだが... 132 //return source.Chunk(size).Select(x => 133 //{ 134 // if (x[0] == 0) throw new ArgumentException("チャンク先頭がゼロです"); 135 // return new BigInteger(x, true, true); 136 //}).ToArray(); 137 } 138 static byte[] BigIntArray2ByteArray(BigInteger[] source, int size = 256) 139 { 140 if (source == null) throw new ArgumentNullException(nameof(source)); 141 if (size < 1) throw new ArgumentOutOfRangeException(nameof(size)); 142 143 if (source.Length == 0) return Array.Empty<byte>(); 144 145 146 var dest = new byte[source.Length * size]; 147 var length = 0; 148 for (var i = 0; i < source.Length; i++) 149 { 150 var array = source[i].ToByteArray(true, true); 151 if (size < array.Length) throw new ArgumentException("sourceがsizeより大きいです"); 152 153 if (i < source.Length - 1) 154 { 155 Array.Copy(array, 0, dest, length + size - array.Length, array.Length); 156 length += size; 157 } 158 else 159 { 160 Array.Copy(array, 0, dest, length, array.Length); 161 length += array.Length; 162 } 163 } 164 Array.Resize(ref dest, length); 165 166 return dest; 167 168 169 // 先頭ゼロがないのであれば1行なのだが... 170 //return source.SelectMany(x => x.ToByteArray(true, true)).ToArray(); 171 } 172 173 174 static void Print(byte[] a, byte[] b) 175 => Console.WriteLine($"a.Length:{a.Length} b.Length:{b.Length} SequenceEqual:{a.SequenceEqual(b)}\n"); 176 } 177}

投稿2023/05/03 22:06

編集2023/05/03 22:14
TN8001

総合スコア10022

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

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

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

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

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

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

ただいまの回答率
85.31%

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

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

質問する

関連した質問