実現したいこと
好きな長さの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を使用しています。
> int C = 256;
これは何ですか? その先もそういう調子ではないかと思ってしまいます・・・
最初に今回切り出すbyte配列の要素数を指定しています(今回であれば256です)
また、変数の表に表記ミスがあったため修正しました
> 最後の277Byte
277byte より少ない入力はどうなりますか?
何で 256 でなくて 256 なのですか?
> これは何ですか? その先もそういう調子ではないかと思ってしまいます・・・
コメントの意味が伝わってないようなので、横から解説すると、
「半角文字と全角文字の違いに無頓着な人の書いたコードは最初から信用できない」
という意味でしょう。
質問文のコードは手入力じゃなくてコピペしましょう。
超絶テクニックが色々使われているので読むのに苦労します。
もっと普通に組めば別の結果が得られるかもしれません。
まず int を double にして int と比較しているところから脳が拒否反応を示し始めます。
>コメントの意味が伝わってないようなので(略)
あーすいません、その部分、実は元は違う表記だったのですがそうするより普通に書いたほうがいいということをこちらにコピペした後で気づいて書き換えたんですよね。今使っているフォントが全角数字と半角数字を全く同じように表示してしまうので気が付きませんでした。
>超絶テクニックが(略)
完全に独学なもので、なにが普通かわからないんですよね
int を doubleにして intと比較、というのもこれ以外にいい方法を思いつかなかったんです。(割り算の答えを小数にしたかっただけなのですが)
>277byte より少ない入力はどうなりますか?
確認したところ、全体が全く違う値になっていました
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 からのデータを使うと重複します。
一方、復元の方は重複を考慮に入れていません。
これ復元できるのですか?
Byte[] a = {0,1}
これすら復元できないのでは?
> こちらにコピペした後で気づいて書き換えたんですよね。
正しいやり方は、元プログラムの方を 256 に書き直した上で、再度実行して同じ結果になることを確認した上で、元プログラムを改変せずコピペです。
まあ、ちゃんとコピペしてない質問者は多数いるし、半角でキーインしていれば、誰も改変に気づかなかった所なので、別にこのままで良いですけど。
嘘を書きました。
私の書いたのは読み間違えなので無視してください。
>>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へコピーします。
>>Zuishin
ああ、ちょうどすれ違いになってしまったみたいですね。わかりました。
とにかく 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 すれば、ややこしい計算も条件分岐も排除できます。