c# - example - Inicializar una matriz de bytes a un cierto valor, que no sea el nulo predeterminado?
using byte[] (13)
Esta pregunta ya tiene una respuesta aquí:
- ¿Cuál es el equivalente de memset en C #? 13 respuestas
Estoy ocupado reescribiendo un viejo proyecto que se hizo en C ++, a C #.
Mi tarea es reescribir el programa para que funcione tan cerca del original como sea posible.
Durante un montón de manejo de archivos, el desarrollador anterior que escribió este programa crea una estructura que contiene una tonelada de campos que corresponden al formato establecido en el que se debe escribir un archivo, por lo que todo ese trabajo ya está hecho para mí.
Estos campos son todos los arrays de bytes. Lo que hace el código C ++ es usar memset
para establecer toda esta estructura en todos los caracteres de espacios ( 0x20
). Una linea de codigo Fácil.
Esto es muy importante ya que la utilidad a la que finalmente va este archivo espera el archivo en este formato. Lo que he tenido que hacer es cambiar esta estructura a una clase en C #, pero no puedo encontrar una manera de inicializar fácilmente cada una de estas matrices de bytes a todos los caracteres espaciales.
Lo que terminé teniendo que hacer es esto en el constructor de la clase:
//Initialize all of the variables to spaces.
int index = 0;
foreach (byte b in UserCode)
{
UserCode[index] = 0x20;
index++;
}
Esto funciona bien, pero estoy seguro de que debe haber una forma más sencilla de hacerlo. Cuando la matriz se configura como UserCode = new byte[6]
en el constructor, la matriz de bytes se inicializa automáticamente a los valores nulos predeterminados. ¿No hay forma de que pueda hacer que se convierta en todos los espacios en la declaración, de modo que cuando llamo al constructor de mi clase, se inicialice inmediatamente así? ¿O alguna función similar a memset
?
Chicos antes que yo te dieron tu respuesta. Solo quiero señalar su mal uso del ciclo foreach. Ver, dado que tiene que incrementar el índice estándar "for loop", no solo sería más compacto, sino también más eficiente ("foreach" hace muchas cosas bajo el capó):
for (int index = 0; index < UserCode.Length; ++index)
{
UserCode[index] = 0x20;
}
Esta es una versión más rápida del código de la publicación marcada como la respuesta.
Todos los puntos de referencia que he realizado muestran que un bucle simple que solo contiene algo así como un relleno de matriz suele ser el doble de rápido si se reduce o aumenta.
Además, la propiedad Longitud de la matriz ya pasó como parámetro, por lo que no es necesario recuperarla de las propiedades de la matriz. También debe precalcularse y asignarse a una variable local. Los cálculos de límites de bucle que implican un acceso de propiedad calcularán de nuevo el valor de los límites antes de cada iteración del bucle.
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;
}
Esta función es mucho más rápida que un ciclo for para llenar una matriz.
El comando Array.Copy es una función de copia de memoria muy rápida. Esta función aprovecha eso llamando repetidamente al comando Array.Copy y doblando el tamaño de lo que copiamos hasta que la matriz esté llena.
Discuto esto en mi blog en http://coding.grax.com/2013/06/fast-array-fill-function-revisited.html
Tenga en cuenta que esto sería fácil de hacer en un método de extensión simplemente agregando la palabra "this" a las declaraciones del método, es decir, 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);
}
}
La forma más rápida de hacerlo es usar la API:
bR = 0xFF;
RtlFillMemory (pBuffer, nFileLen, bR);
usando un puntero a un buffer, la longitud para escribir, y el byte codificado. Creo que la manera más rápida de hacerlo en código administrado (mucho más lento) es crear un pequeño bloque de bytes inicializados, luego usar Buffer.Blockcopy para escribirlos en la matriz de bytes en un bucle. Tiré esto pero no lo he probado, pero entiendes la idea:
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);
Para matrices pequeñas, use la sintaxis de inicialización de matriz:
var sevenItems = new byte[] { 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20 };
Para matrices más grandes, use un bucle estándar for
. Esta es la forma más legible y eficiente de hacerlo:
var sevenThousandItems = new byte[7000];
for (int i = 0; i < sevenThousandItems.Length; i++)
{
sevenThousandItems[i] = 0x20;
}
Por supuesto, si necesita hacer esto mucho, entonces puede crear un método auxiliar para ayudar a mantener su código conciso:
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;
}
Puede acelerar la inicialización y simplificar el código utilizando la clase Parallel (.NET 4 y posteriores):
public static void PopulateByteArray(byte[] byteArray, byte value)
{
Parallel.For(0, byteArray.Length, i => byteArray[i] = value);
}
Por supuesto, puedes crear la matriz al mismo tiempo:
public static byte[] CreateSpecialByteArray(int length, byte value)
{
var byteArray = new byte[length];
Parallel.For(0, length, i => byteArray[i] = value);
return byteArray;
}
Puede usar Enumerable.Repeat()
Matriz de 100 elementos inicializados a 0x20:
byte[] arr1 = Enumerable.Repeat(0x20,100).ToArray();
Puede usar un inicializador de colección :
UserCode = new byte[]{0x20,0x20,0x20,0x20,0x20,0x20};
Esto funcionará mejor que Enumerable.Repeat() si los valores no son idénticos.
Si necesita inicializar una matriz pequeña, puede usar:
byte[] smallArray = new byte[] { 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20 };
Si tienes una matriz más grande, entonces podrías usar:
byte[] bitBiggerArray Enumerable.Repeat(0x20, 7000).ToArray();
Lo cual es simple y fácil de leer para el próximo chico / chica. Y será lo suficientemente rápido el 99.9% del tiempo. (Normalmente será BestOption ™)
Sin embargo, si realmente necesitas una gran velocidad, llamar al método optimizado de memset, usando P / invoke, es para ti: (Aquí envuelto en una clase agradable de usar)
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;
}
}
Uso:
byte[] oneofManyBigArrays = Superfast.InitByteArray(0x20,700000);
Solo para ampliar mi respuesta, una forma más clara de hacer esto varias veces sería probablemente:
PopulateByteArray(UserCode, 0x20);
que llama:
public static void PopulateByteArray(byte[] byteArray, byte value)
{
for (int i = 0; i < byteArray.Length; i++)
{
byteArray[i] = value;
}
}
Esto tiene la ventaja de un buen bucle eficiente (mención a la respuesta de gwiazdorrr) así como una buena llamada de aspecto ordenado si se usa mucho. Y mucho más a simple vista que la enumeración que personalmente pienso. :)
Tal vez esto podría ser útil?
¿Cuál es el equivalente de memset en C #?
http://techmikael.blogspot.com/2009/12/filling-array-with-default-value.html
Use esto para crear la matriz en primer lugar:
byte[] array = Enumerable.Repeat((byte)0x20, <number of elements>).ToArray();
Reemplace <number of elements>
con el tamaño de matriz deseado.
var array = Encoding.ASCII.GetBytes(new string('' '', 100));