Инициализировать массив байтов до определенного значения, кроме значения по умолчанию null?

Я занят переписанием старого проекта, который был выполнен на С++, на С#.

Моя задача - переписать программу так, чтобы она работала как можно ближе к оригиналу.

Во время обработки файлов предыдущий разработчик, который написал эту программу, создает структуру, содержащую тонну полей, которые соответствуют заданному формату, в который должен быть записан файл, поэтому все, что работает, уже сделано для меня.

Эти поля представляют собой все байтовые массивы. То, что делает код С++, это использовать memset, чтобы установить всю эту структуру для всех пробелов символов (0x20). Одна строка кода. Легко.

Это очень важно, поскольку утилита, к которой в конечном итоге идет этот файл, ожидает файл в этом формате. То, что мне нужно было сделать, это изменить эту структуру на класс в С#, но я не могу найти способ легко инициализировать каждый из этих массивов байтов ко всем символам пробела.

То, что мне пришлось сделать, это это в конструкторе класса:

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

Это отлично работает, но я уверен, что должен быть более простой способ сделать это. Когда массив установлен в UserCode = new byte[6] в конструкторе, массив байтов автоматически инициализируется нулевыми значениями по умолчанию. Не существует способа, чтобы я мог превратить это все пробелы в объявление, так что, когда я вызываю конструктор моего класса, что он инициализируется прямо так? Или некоторая memset -подобная функция?

Ответ 1

Для небольших массивов используйте синтаксис инициализации массива:

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

Ответ 2

Используйте это, чтобы создать массив в первую очередь:

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

Замените <number of elements> на требуемый размер массива.

Ответ 3

Вы можете использовать Enumerable.Repeat()

Массив из 100 элементов, инициализированных до 0x20:

byte[] arr1 = Enumerable.Repeat(0x20,100).ToArray();

Ответ 4

var array = Encoding.ASCII.GetBytes(new string(' ', 100));

Ответ 5

Если вам нужно инициализировать небольшой объект, вы можете использовать массив:

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

Если у вас есть больший массив, вы можете использовать:

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

Что просто и легко для следующего парня/девушки читать. И будет достаточно быстро 99,9% времени. (Обычно это BestOption ™)

Однако, если вам действительно нужна сверхскоростная скорость, вызывая оптимизированный метод memset, используя P/invoke, для вас: (Здесь завернутый в класс, удобный для использования)

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

Ответ 6

Ребята передо мной ответили вам. Я просто хочу указать на ваше неправильное использование цикла foreach. Смотрите, так как вы должны увеличивать индексный стандарт "for loop" будет не только более компактным, но и более эффективным ( "foreach" делает много вещей под капотом):

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

Ответ 8

Чтобы увеличить мой ответ, более простой способ сделать это несколько раз:

PopulateByteArray(UserCode, 0x20);

который вызывает:

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

У этого есть преимущество хорошего эффективного цикла (упоминание gwiazdorrr ответа), а также хороший аккуратный поисковый вызов, если он используется много. И много mroe с первого взгляда читабельны, чем перечисление, которое я лично считаю.:)

Ответ 9

Вы можете ускорить инициализацию и упростить код, используя класс 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;
}

Ответ 10

Вы можете использовать инициализатор коллекции:

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

Это будет работать лучше, чем Repeat, если значения не идентичны.

Ответ 11

Эта функция работает быстрее, чем цикл for для заполнения массива.

Команда Array.Copy - очень быстрая функция копирования памяти. Эта функция использует это, повторяя вызов команды Array.Copy и удваивая размер того, что мы копируем, пока массив не будет заполнен.

Я обсуждаю это в своем блоге на 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);
    }
}

Ответ 12

Это более быстрая версия кода из сообщения, помеченного как ответ.

Все тесты, которые я выполнил, показывают, что простой цикл, содержащий только что заполненный массив, обычно в два раза быстрее, если он уменьшает или увеличивает его.

Кроме того, свойство массива Length уже передано как параметр, поэтому его не нужно извлекать из свойств массива. Он также должен быть предварительно рассчитан и назначен локальной переменной. Вычисления границ цикла, которые включают в себя свойство accessor, будут повторно вычислять значение границ перед каждой итерацией цикла.

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

Ответ 13

Самый быстрый способ сделать это - использовать api:

bR = 0xFF;

RtlFillMemory (pBuffer, nFileLen, bR);

используя указатель на буфер, длину записи и закодированный байт. Я думаю, что самый быстрый способ сделать это в управляемом коде (гораздо медленнее) - создать небольшой блок инициализированных байтов, а затем использовать Buffer.Blockcopy для записи их в массив байтов в цикле. Я выбросил это вместе, но не протестировал его, но вы поняли:

long size = GetFileSize(FileName);
// zero byte
const int blocksize = 1024;
// 1 array
byte[] ntemp = new byte[blocksize];
byte[] nbyte = new byte[size];
// init 1 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);