Initialiser un tableau byte à une certaine valeur, autre que la valeur par défaut null? [dupliquer]

cette question a déjà une réponse ici:

  • Quel est l'équivalent de memset en C#? 13 Réponses

je suis occupé à réécrire un vieux projet qui a été fait en C++, en C#.

Ma tâche est de réécrire le programme pour qu'il fonctionne comme proche de la original que possible.

pendant un tas de manipulation de fichier le développeur précédent qui a écrit ce programme crée une structure contenant une tonne de champs qui correspondent au format défini dans lequel un fichier doit être écrit, de sorte que tout ce travail est déjà fait pour moi.

Ces champs sont tous des tableaux d'octets. Ce que le code C++ fait ensuite est d'utiliser memset pour définir cette structure entière à tous les caractères des espaces ( 0x20 ). Une seule ligne de code. Facile.

C'est très important car l'utilitaire auquel ce fichier est éventuellement destiné attend le fichier dans ce format. Ce que j'ai dû faire est de changer cette structure en classe en C#, mais je ne peux pas trouver un moyen d'initialiser facilement chacun de ces tableaux octets à tous les caractères de l'espace.

ce que j'ai fini par avoir à faire est ceci dans la classe constructeur:

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

cela fonctionne très bien, mais je suis sûr qu'il doit y avoir un moyen plus simple de le faire. Lorsque le tableau est défini à UserCode = new byte[6] dans le constructeur le tableau d'octets est automatiquement initialisé aux valeurs null par défaut. N'y a-t-il pas moyen que je puisse faire en sorte qu'il devienne tous les espaces sur déclaration, de sorte que lorsque j'appelle mon constructeur de classe qu'il est initialisé tout de suite comme ceci? Ou une fonction memset ?

128
demandé sur ROMANIA_engineer 2011-05-27 13:08:10

13 réponses

pour les petits tableaux utiliser la syntaxe d'initialisation de tableau:

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

pour les tableaux plus grands utiliser une boucle standard for . C'est la façon la plus lisible et efficace de le faire:

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

bien sûr, si vous avez besoin de faire beaucoup de vous pouvez créer une méthode d'assistance pour aider à garder votre code concis:

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;
}
148
répondu LukeH 2011-05-27 09:36:55

pour créer le tableau en premier lieu:

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

remplacer <number of elements> par la taille de réseau désirée.

78
répondu Thorsten Dittmar 2011-05-27 09:13:22

vous pouvez utiliser énumérable.Je répète.()

tableau de 100 éléments initialisés à 0x20:

byte[] arr1 = Enumerable.Repeat(0x20,100).ToArray();
30
répondu Yochai Timmer 2011-05-27 09:18:21
var array = Encoding.ASCII.GetBytes(new string(' ', 100));
28
répondu Yuriy Rozhovetskiy 2011-05-27 09:27:56

si vous devez initialiser un petit tableau vous pouvez utiliser:

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

si vous avez un tableau plus grand, alors vous pouvez utiliser:

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

ce qui est simple, et facile pour le prochain gars/fille à lire. Et sera assez rapide 99,9% du temps. (Sera normalement la BestOption™)

cependant si vous avez vraiment besoin de super vitesse, appeler à la méthode memset optimisée, en utilisant P / invoke, est pour vous: (Ici enveloppé dans une classe de nice à utiliser)

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;
    }
}

Utilisation:

byte[] oneofManyBigArrays =  Superfast.InitByteArray(0x20,700000);
9
répondu DarcyThomas 2018-05-01 16:45:33

les gars avant moi vous ont donné votre réponse. Je veux juste souligner votre mauvais usage de chaque boucle. Vous voyez, puisque vous devez augmenter l'indice standard "pour boucle" serait non seulement plus compact, mais aussi plus efficace ("foreach" fait beaucoup de choses sous le capot):

for (int index = 0; index < UserCode.Length; ++index)
{
    UserCode[index] = 0x20;
}
4
répondu gwiazdorrr 2011-05-27 09:21:36

la manière la plus rapide de faire ceci est d'utiliser l'api:

bR = 0xFF;

RtlFillMemory (pBuffer, nFileLen, bR);

utilisant un pointeur vers un tampon, la longueur à écrire, et l'octet encodé. Je pense que le moyen le plus rapide de le faire en code géré (beaucoup plus lent), est de créer un petit bloc d'octets initialisés, puis d'utiliser un tampon.Blockcopy à les écrire sur le tableau d'octets dans une boucle. J'ai fait ça ensemble mais je ne l'ai pas testé, mais toi l'idée:

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
répondu JGU 2013-10-09 21:33:04

juste pour développer ma réponse une façon plus nette de faire cela plusieurs fois serait probablement:

PopulateByteArray(UserCode, 0x20);

qui appelle:

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

cela a l'avantage d'une belle efficace pour boucle (mention à la réponse de gwiazdorrr) ainsi que d'un beau coup de pouce si elle est utilisée beaucoup. Et beaucoup de mroe à un coup d'oeil lisible que l'énumération que je pense personnellement. :)

3
répondu Chris 2011-05-27 09:37:28

vous pouvez utiliser un initialiseur de collection :

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

Cela fonctionnera mieux que Repeat si les valeurs ne sont pas identiques.

2
répondu Oded 2011-05-27 09:14:51

cette fonction est beaucoup plus rapide qu'une boucle for pour remplir un tableau.

Le Tableau.La commande de copie est une fonction de copie de mémoire très rapide. Cette fonction tire avantage de cela en appelant le tableau à plusieurs reprises.Copier la commande et doubler la taille de ce que nous copions jusqu'à ce que le tableau soit plein.

j'en parle sur mon blog à http://coding.grax.com/2013/06/fast-array-fill-function-revisited.html

Notez que ceci serait facile à transformer en une méthode d'extension en ajoutant simplement le mot "this" aux déclarations de méthode i.e. 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
répondu Grax 2013-06-14 10:49:30

C'est une version plus rapide du code de la post marqué comme réponse.

tous les benchmarks que j'ai réalisés montrent qu'un simple pour boucle qui contient seulement quelque chose comme un remplissage de tableau est typiquement deux fois plus rapide si elle décrémente versus si elle est incrémentée.

aussi, la propriété de longueur de tableau est déjà passée comme paramètre de sorte qu'il n'a pas besoin d'être récupéré à partir les propriétés de la matrice. Il doit également être calculé à l'avance et affecté à une variable locale. Les calculs de bornes de boucle qui impliquent un accessor de propriété calculeront de nouveau la valeur des bornes avant chaque itération de la boucle.

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
répondu deegee 2013-06-21 19:22:33

vous pouvez accélérer l'initialisation et simplifier le code en utilisant la classe parallèle (.NET 4 et plus récente):

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

bien sûr, vous pouvez créer le tableau en même temps:

public static byte[] CreateSpecialByteArray(int length, byte value)
{
    var byteArray = new byte[length];
    Parallel.For(0, length, i => byteArray[i] = value);
    return byteArray;
}
1
répondu slfan 2016-08-26 12:13:28