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



этот вопрос уже есть ответ здесь:




  • Что такое эквивалент memset в C#?

    13 ответов



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



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



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



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



Это очень важно, как утилита, что этот файл в конечном итоге идет к ожидает файл в этом формате. Мне нужно было изменить эту структуру на класс В 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подобная функция?

1258   13  

13 ответов:

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

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

используйте это для создания массива в первую очередь:

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

заменить <number of elements> с нужным размером массива.

можно использовать перечисли.Повторить ()

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

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

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

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

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

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

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

однако, если вам действительно нужна супер скорость, вызов оптимизированного метода 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);

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

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

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

bR = 0xFF;

RtlFillMemory(pBuffer, nFileLen, bR);

используя указатель на буфер, длину для записи и закодированный байт. Я думаю, что самый быстрый способ сделать это в управляемом коде (гораздо медленнее) - создать небольшой блок инициализированных байтов, а затем использовать буфер.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);

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

PopulateByteArray(UserCode, 0x20);

которых вызовы:

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

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

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

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

это будет работать лучше, чем Repeat если значения не совпадают.

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

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

Я обсуждаю это в своем блоге на http://coding.grax.com/2013/06/fast-array-fill-function-revisited.html

обратите внимание, что это будет легко сделать в метод расширения, просто добавив слово " это " в объявления метода, т. е. 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);
    }
}

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

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

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

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

вы можете ускорить инициализацию и упростить код с помощью параллельного класса (.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;
}

Comments

    Ничего не найдено.