web-dev-qa-db-fra.com

Initialiser un tableau d'octets sur une certaine valeur, autre que le null par défaut?

Je suis en train de réécrire un ancien projet qui était fait en C++, en C #.

Ma tâche est de réécrire le programme afin qu'il fonctionne le plus près possible de l'original.

Lors d'une série de manipulations de fichiers, le développeur précédent qui a écrit ce programme crée une structure contenant une tonne de champs correspondant au format défini dans lequel un fichier doit être écrit. Tout ce travail est donc déjà fait pour moi.

Ces champs sont tous des tableaux d'octets. Le code C++ utilise ensuite memset pour définir la structure entière sur tous les espaces (0x20). Une ligne de code. Facile.

Ceci est très important car l'utilitaire auquel ce fichier est destiné est attendu dans ce format. Ce que j'ai dû faire, c'est modifier cette structure pour une classe en C #, mais je ne trouve pas le moyen d'initialiser facilement chacun de ces tableaux d'octets sur tous les espaces.

Ce que j'ai fini par devoir faire est ceci dans le constructeur de classe:

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

Cela fonctionne bien, mais je suis sûr qu'il doit y avoir un moyen plus simple de le faire. Lorsque le tableau est défini sur 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 aucun moyen de faire en sorte que tous les espaces soient déclarés, de sorte que, lorsque j'appelle le constructeur de ma classe, celle-ci soit immédiatement initialisée ainsi? Ou une fonction semblable à memset-?

146
DeVil

Pour les petits tableaux, utilisez la syntaxe d'initialisation de tableau:

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

Pour les tableaux plus grands, utilisez une boucle standard for. C'est le moyen le plus lisible et le plus 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 devez le faire souvent, vous pouvez créer une méthode d'assistance pour que votre code soit 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;
}
173
LukeH

Utilisez ceci pour créer le tableau en premier lieu:

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

Remplacez <number of elements> par la taille de tableau souhaitée.

87
Thorsten Dittmar

Vous pouvez utiliser Enumerable.Repeat ()

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

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

Si vous avez besoin d'initialiser un petit tableau, vous pouvez utiliser:

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

Si vous avez un plus grand tableau, vous pouvez utiliser:

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

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

Cependant, si vous avez vraiment besoin de super rapidité, appeler la méthode optimisée memset, en utilisant P/invoke, est pour vous: (Ici, encapsulé dans une classe agréable à 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;
    }
}

Usage:

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

Les gars avant moi vous ont donné votre réponse. Je veux juste souligner votre mauvaise utilisation de la boucle foreach. Vous voyez, puisque vous devez incrémenter la norme d'index "for loop" serait non seulement plus compacte, mais aussi plus efficace ("foreach" fait beaucoup de choses sous le capot):

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

Le moyen le plus rapide de faire est d’utiliser l’API:

bR = 0xFF;

RtlFillMemory (pBuffer, nFileLen, bR);

utilisant un pointeur vers un tampon, la longueur à écrire et l'octet codé. Je pense que le moyen le plus rapide de le faire dans le code managé (beaucoup plus lent) est de créer un petit bloc d'octets initialisés, puis d'utiliser Buffer.Blockcopy pour les écrire dans le tableau d'octets dans une boucle. J'ai jeté cela ensemble mais je ne l'ai pas testé, mais vous voyez 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
JGU

Juste pour développer ma réponse, une façon plus simple de le faire 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 présente l’avantage d’une boucle Nice efficient for (mention de la réponse de gwiazdorrr) et d’un appel sympa à la recherche s’il est très utilisé. Et beaucoup plus facile à lire que l’énumération, je pense personnellement. :)

3
Chris

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
Oded

Ceci est une version plus rapide du code de la publication marquée comme la réponse.

Tous les tests que j'ai effectués montrent qu'un simple pour la boucle qui ne contient que quelque chose comme un remplissage de tableau est généralement deux fois plus rapide s'il est décrémenté par rapport à s'il incrémente .

De plus, la propriété array array est déjà passée en tant que paramètre, il n'est donc pas nécessaire de la récupérer à partir des propriétés du tableau. Il devrait également être précalculé et attribué à une variable locale. Les calculs de limites de boucle qui impliquent un accesseur de propriété vont recalculer la valeur des limites 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
deegee

Cette fonction est bien plus rapide qu'une boucle for pour remplir un tableau.

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

J'en discute sur mon blog à l'adresse http://coding.grax.com/2013/06/fast-array-fill-function-revisited.html

Notez qu'il serait facile de transformer une méthode d'extension en ajoutant simplement le mot "this" aux déclarations de méthode, c'est-à-dire 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

Vous pouvez accélérer l'initialisation et simplifier le code en utilisant la classe Parallel (.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
slfan