初始化一个字节数组到一个特定的值,除了默认的空?

我正忙着把一个用 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]时,字节数组将自动初始化为默认的空值。有没有办法让它在声明时变成所有的空格,这样当我调用类的构造函数时,它就像这样被直接初始化了?或者类似 memset的功能?

627775 次浏览

你可以使用 < strong > 可枚举,重复()

Enumerable.Repeat生成一个包含一个重复值的序列。

初始化为 0x20的100个项目的数组:

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

首先使用它来创建数组:

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

用所需的数组大小替换 <number of elements>

你可以使用 集合初始值设定项:

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

如果值不相同,这将比 Repeat工作得更好。

在我之前的人给了你答案。我只是想指出你对 foreach 循环的误用。看,因为你必须递增索引标准“ for loop”不仅更紧凑,而且更有效(“ foreach”在底层做了很多事情) :

for (int index = 0; index < UserCode.Length; ++index)
{
UserCode[index] = 0x20;
}
var array = Encoding.ASCII.GetBytes(new string(' ', 100));

对于小数组,使用数组初始化语法:

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

为了扩展我的答案,一个更简洁的方法可能是:

PopulateByteArray(UserCode, 0x20);

叫做:

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

这有一个很好的高效 for 循环(提到 gwiazdorrr 的回答)的优点,如果经常使用它,还有一个漂亮整洁的调用。而且比我个人认为的枚举一目了然的可读性要好得多。:)

您可以使用并行类(.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;
}

这个函数比用于填充数组的 for 循环快得多。

数组。复制命令是一个非常快速的内存复制函数。这个函数通过重复调用 Array 来利用这一点。复制命令并将复制内容的大小加倍,直到数组满为止。

我在我的博客 https://grax32.com/2013/06/fast-array-fill-function-revisited.html上讨论了这个问题(链接更新于2019年12月16日)。另请参阅提供此扩展方法的 Nuget 包。http://sites.grax32.com/ArrayExtensions/

请注意,只需在方法声明(即 public static void ArrayFill<T>(this T[] arrayToFill ...)中添加单词“ this”,就可以很容易地将其转换为扩展方法

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

这是一个更快的版本的代码从标记为答案的帖子。

我所执行的所有基准测试都表明,一个只包含类似数组填充的简单 循环如果是递减的,通常是两倍的速度,而它是否在递增。

此外,数组 Llength 属性已经作为参数传递,因此不需要从数组属性中检索它。它也应该预先计算和分配给一个局部变量。 包含 属性访问器属性访问器的循环边界计算将在循环的每次迭代之前重新计算边界的值。

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

最快的方法是使用 api:

BR = 0xFF;

RtlFillMemory (pBuffer,nFileLen,bR) ;

使用指向缓冲区的指针、要写入的长度和编码的字节。我认为在托管代码中实现这一点的最快方法(要慢得多)是创建一个初始化字节的小块,然后使用 Buffer。块复制,将它们写入循环中的字节数组。我把它们组合在一起,但还没有测试过,不过你应该知道:

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

如果你需要初始化一个小数组,你可以使用:

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

如果您有一个更大的数组,那么您可以使用:

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

这很简单,也很容易让下一个男生/女生读懂,而且在99.9% 的时间里都足够快。 (通常是 BestOptionTM)

然而,如果您真的需要超高速,使用 P/call 调用优化的 memset 方法是为您准备的: (这里包装在一个漂亮的使用类)

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);
gch.Free();
return arrayBytes;
}
}

用法:

byte[] oneofManyBigArrays =  Superfast.InitByteArray(0x20,700000);