web-dev-qa-db-ja.com

バイト配列をデフォルトのnull以外の特定の値に初期化しますか?

私はC++で行われた古いプロジェクトをC#に書き直すことに忙しいです。

私の仕事は、プログラムができるだけオリジナルに近い機能を果たすようにプログラムを書き直すことです。

ファイル処理の束の間に、このプログラムを書いた前の開発者は、ファイルが書かれなければならないセットフォーマットに対応するたくさんのフィールドを含む構造体を作成します。

これらのフィールドはすべてバイト配列です。その後、C++コードは、この構造全体をすべての空白文字に設定するためにmemsetを使用します(0x20)。 1行のコード簡単です。

このファイルが最終的に使用するユーティリティがこの形式のファイルを期待しているので、これが非常に重要です。私がしなければならなかったのは、この構造体をC#のクラスに変更することですが、これらのバイト配列をすべてすべてのスペース文字に簡単に初期化する方法を見つけることはできません。

私がやらなければならなかったことは、クラスコンストラクタでこれです。

//Initialize all of the variables to spaces.
int index = 0;
foreach (byte b in UserCode)
{
    UserCode[index] = 0x20;
    index++;
}

これはうまくいきますが、もっと簡単な方法があるはずです。コンストラクタで配列がUserCode = new byte[6]に設定されている場合、バイト配列は自動的にデフォルトのnull値に初期化されます。宣言時にすべてのスペースになるようにする方法はありません。そのため、クラスのコンストラクタを呼び出すと、このように直接初期化されます。それともmemset-like関数?

139
DeVil

小さな配列には配列初期化構文を使います。

var sevenItems = new byte[] { 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20 };

より大きな配列には標準のforループを使います。これが最も読みやすく効率的な方法です。

var sevenThousandItems = new byte[7000];
for (int i = 0; i < sevenThousandItems.Length; i++)
{
    sevenThousandItems[i] = 0x20;
}

もちろん、これをたくさん行う必要がある場合は、コードを簡潔にしておくためのヘルパーメソッドを作成します。

byte[] sevenItems = CreateSpecialByteArray(7);
byte[] sevenThousandItems = CreateSpecialByteArray(7000);

// ...

public static byte[] CreateSpecialByteArray(int length)
{
    var arr = new byte[length];
    for (int i = 0; i < arr.Length; i++)
    {
        arr[i] = 0x20;
    }
    return arr;
}
164
LukeH

これを使用して、最初に配列を作成します。

byte[] array = Enumerable.Repeat((byte)0x20, <number of elements>).ToArray();

<number of elements>を目的の配列サイズに置き換えます。

84

あなたは Enumerable.Repeat() を使うことができます

0x20に初期化された100項目の配列:

byte[] arr1 = Enumerable.Repeat(0x20,100).ToArray();
30
Yochai Timmer
var array = Encoding.ASCII.GetBytes(new string(' ', 100));
28

小さな配列を初期化する必要がある場合は、次のものを使用できます。

byte[] smallArray = new byte[] { 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20 };

もしあなたがもっと大きな配列を持っているなら、あなたはそれを使うことができる:

byte[] bitBiggerArray Enumerable.Repeat(0x20, 7000).ToArray();

これは単純で、そして次の男/女にとって読むのは簡単です。そして十分に速い99.9%の時間になります。 (通常はBestOption™になります)

しかし、本当に超高速が必要な場合は、P/invokeを使用して最適化されたmemsetメソッドを呼び出すことがあなたのためです。(ここでNiceを使用するクラスにまとめました)

public static class Superfast
{
    [DllImport("msvcrt.dll",
              EntryPoint = "memset",
              CallingConvention = CallingConvention.Cdecl,
              SetLastError = false)]
    private static extern IntPtr MemSet(IntPtr dest, int c, int count);

    //If you need super speed, calling out to M$ memset optimized method using P/invoke
    public static byte[] InitByteArray(byte fillWith, int size)
    {
        byte[] arrayBytes = new byte[size];
        GCHandle gch = GCHandle.Alloc(arrayBytes, GCHandleType.Pinned);
        MemSet(gch.AddrOfPinnedObject(), fillWith, arrayBytes.Length);
        return arrayBytes;
    }
}

使用法:

byte[] oneofManyBigArrays =  Superfast.InitByteArray(0x20,700000);
9
DarcyThomas
4
PompolutZ

私の前の人たちがあなたに答えてくれました。私はあなたのforeachループの誤用を指摘したいだけです。インデックス標準を増やす必要があるため、 "for loop"はよりコンパクトになるだけでなく、より効率的になります( "foreach"は内部で多くのことを行います)。

for (int index = 0; index < UserCode.Length; ++index)
{
    UserCode[index] = 0x20;
}
4
gwiazdorrr

これをする最も速い方法はapiを使うことです:

bR = 0xFF。

RtlFillMemory(pBuffer、nFileLen、bR);

バッファへのポインタ、書き込む長さ、およびエンコードされたバイトを使用します。マネージコードでこれを実行する最も速い方法は(はるかに遅い)、初期化済みバイトの小さなブロックを作成してから、Buffer.Blockcopyを使用してループ内のバイト配列に書き込むことです。私はこれを一緒に投げました、しかしそれをテストしませんでした、しかしあなたは考えを得ます:

long size = GetFileSize(FileName);
// zero byte
const int blocksize = 1024;
// 1's array
byte[] ntemp = new byte[blocksize];
byte[] nbyte = new byte[size];
// init 1's array
for (int i = 0; i < blocksize; i++)
    ntemp[i] = 0xff;

// get dimensions
int blocks = (int)(size / blocksize);
int remainder = (int)(size - (blocks * blocksize));
int count = 0;

// copy to the buffer
do
{
    Buffer.BlockCopy(ntemp, 0, nbyte, blocksize * count, blocksize);
    count++;
} while (count < blocks);

// copy remaining bytes
Buffer.BlockCopy(ntemp, 0, nbyte, blocksize * count, remainder);
4
JGU

私の答えを拡大するには、これを複数回実行するためのより適切な方法を考えます。

PopulateByteArray(UserCode, 0x20);

どの呼び出し:

public static void PopulateByteArray(byte[] byteArray, byte value)
{
    for (int i = 0; i < byteArray.Length; i++)
    {
        byteArray[i] = value;
    }
}

これは、Niceの効率的なforループ(gwiazdorrrの回答を参照してください)と、よく使用されている場合はNiceのきちんとした呼び出しの利点があります。そして、私が個人的に考える列挙型のものより一目で読めるたくさんのmroe。 :)

3
Chris

コレクション初期化子 を使うことができます

UserCode = new byte[]{0x20,0x20,0x20,0x20,0x20,0x20};

値が同一でない場合、これは Repeat よりもうまく機能します。

2
Oded

これは、回答としてマークされた投稿からのコードのより速いバージョンです。

私が実行したすべてのベンチマークは、配列の塗りつぶしのようなものだけを含む単純なforループであることを示していますインクリメントしている場合と比べてデクリメントしている場合は、通常2倍の速さです。

また、配列のLengthプロパティはすでにパラメータとして渡されているため、配列のプロパティから取得する必要はありません。また、事前に計算してローカル変数に割り当てる必要があります。 プロパティアクセサを含むループ境界の計算では、ループが繰り返されるたびに境界の値が再計算されます。

public static byte[] CreateSpecialByteArray(int length)
{
    byte[] array = new byte[length];

    int len = length - 1;

    for (int i = len; i >= 0; i--)
    {
        array[i] = 0x20;
    }

    return array;
}
2
deegee

この関数は、配列を埋めるためのforループよりはるかに高速です。

Array.Copyコマンドは非常に高速なメモリコピー機能です。この関数は、Array.Copyコマンドを繰り返し呼び出し、配列がいっぱいになるまでコピーするサイズを2倍にすることで、この機能を利用します。

私はこれを私のブログで議論します http://coding.grax.com/2013/06/fast-array-fill-function-revisited.html

これは、単語 "this"をメソッド宣言に追加するだけで拡張メソッドにするのは簡単です。つまりpublic static void ArrayFill<T>(this T[] arrayToFill ...です。

public static void ArrayFill<T>(T[] arrayToFill, T fillValue)
{
    // if called with a single value, wrap the value in an array and call the main function
    ArrayFill(arrayToFill, new T[] { fillValue });
}

public static void ArrayFill<T>(T[] arrayToFill, T[] fillValue)
{
    if (fillValue.Length >= arrayToFill.Length)
    {
        throw new ArgumentException("fillValue array length must be smaller than length of arrayToFill");
    }

    // set the initial array value
    Array.Copy(fillValue, arrayToFill, fillValue.Length);

    int arrayToFillHalfLength = arrayToFill.Length / 2;

    for (int i = fillValue.Length; i < arrayToFill.Length; i *= 2)
    {
        int copyLength = i;
        if (i > arrayToFillHalfLength)
        {
            copyLength = arrayToFill.Length - i;
        }

        Array.Copy(arrayToFill, 0, arrayToFill, i, copyLength);
    }
}
2
Grax

Parallelクラス(.NET 4以降)を使用すると、初期化を高速化してコードを単純化できます。

public static void PopulateByteArray(byte[] byteArray, byte value)
{
    Parallel.For(0, byteArray.Length, i => byteArray[i] = value);
}

もちろん、同時に配列を作成することもできます。

public static byte[] CreateSpecialByteArray(int length, byte value)
{
    var byteArray = new byte[length];
    Parallel.For(0, length, i => byteArray[i] = value);
    return byteArray;
}
1
slfan