[英]Zero padding a 2D array in C#
I currently have an issue with zero padding my 2d Array.我目前有一个零填充我的二维数组的问题。 I want to transfer my current data in my array to a new array, which is the exact same array but with a border of 0's around it.
我想将数组中的当前数据传输到一个新数组,该数组是完全相同的数组,但其周围的边界为 0。 Example:
例子:
|1 2 3| |1 2 3|
|4 5 6| |4 5 6|
|7 8 9| |7 8 9|
Should become应该成为
|0 0 0 0 0| |0 0 0 0 0|
|0 1 2 3 0| |0 1 2 3 0|
|0 4 5 6 0| |0 4 5 6 0|
|0 7 8 9 0| |0 7 8 9 0|
|0 0 0 0 0| |0 0 0 0 0|
int[,] Array = new int[,] { { 1, 2, 3 }, { 3, 4, 5 }, { 6, 7, 8 } };
int[,] ArrayZeroPad = new int[Array.GetLength(0) + 2, Array.GetLength(1) + 2];
for (int y = 0; y < Array.GetLength(1); y++)
{
for (int x = 0; x < ArrayZeroPad.GetLength(0); x++)
{
if (y == 0)
{ ArrayZeroPad[y, x] = 0; }
else if (y == ArrayZeroPad.GetLength(1))
{ ArrayZeroPad[y, x] = 0; }
else if (x == 0)
{
ArrayZeroPad[y, x] = 0;
}
else if (x == ArrayZeroPad.GetLength(0))
{ ArrayZeroPad[y, x] = 0; }
else ArrayZeroPad[y, x] = Array[y, x];
}
}
for (int y = 0; y < ArrayZeroPad.GetLength(1); y++)
{
Console.WriteLine();
for (int x = 0; x < ArrayZeroPad.GetLength(0); x++)
{ Console.Write(ArrayZeroPad[y, x]); }
Console.ReadLine();
}
}
This is what I have come to thus far, but I keep getting stuck on out of bounds errors, is there anyone who could work this out for me with some explanation?到目前为止,这就是我所做的,但我一直陷入越界错误,有没有人可以为我解决一些解释?
Kind regards, D.亲切的问候,D.
This is not quite what you are asking (I thought a completely different alternative would be interesting).这不是你要问的(我认为一个完全不同的选择会很有趣)。
Here is a No-Copy version that works for any type of array, of any size.这是一个No-Copy版本,适用于任何大小的任何类型的数组。 It's appropriate if the original array is quite large (since it doesn't require a copy).
如果原始数组非常大(因为它不需要副本),这是合适的。
It uses a 2-dimensional indexer that either returns the default value of T (zero or null) for items on the edge, and uses the original array (with the indexes offset) for non-edge values:它使用二维索引器,为边缘上的项目返回 T 的默认值(零或空),并为非边缘值使用原始数组(带有索引偏移):
public class ZeroPadArray <T>
{
private readonly T[,] _initArray;
public ZeroPadArray(T[,] arrayToPad)
{
_initArray = arrayToPad;
}
public T this[int i, int j]
{
get
{
if (i < 0 || i > _initArray.GetLength(0) + 1)
{
throw new ArgumentOutOfRangeException(nameof(i),
$@"Index {nameof(i)} must be between 0 and the width of the padded array");
}
if (j < 0 || j > _initArray.GetLength(1) + 1)
{
throw new ArgumentOutOfRangeException(nameof(j),
$@"Index {nameof(j)} must be between 0 and the width of the padded array");
}
if (i == 0 || j == 0)
{
return default(T);
}
if (i == _initArray.GetLength(0) + 1)
{
return default(T);
}
if (j == _initArray.GetLength(1) + 1)
{
return default(T);
}
//otherwise, just offset into the original array
return _initArray[i - 1, j - 1];
}
}
}
I just tested it with some Debug.Assert
calls.我只是用一些
Debug.Assert
调用测试了它。 The test coverage is weak, but it was good enough to say "this probably works":测试覆盖率很弱,但足以说“这可能有效”:
int[,] array = new int[,] { { 1, 2, 3 }, { 11, 12, 13 }, { 21, 22, 23 } };
var paddedArray = new ZeroPadArray<int>(array);
Debug.Assert(paddedArray[0, 0] == 0);
Debug.Assert(paddedArray[4,4] == 0);
Debug.Assert(paddedArray[2,3] == 13);
And, finally, for fun, I added a nice little hack to make creating these things require less typing.最后,为了好玩,我添加了一个不错的小技巧,使创建这些东西需要更少的输入。 When you call a method, the compiler is often able to deduce the generic type of the object from the method parameters.
当您调用一个方法时,编译器通常能够从方法参数中推断出对象的泛型类型。 This doesn't work for constructors.
这对构造函数不起作用。 That's why you need to specify
new ZeroPadArray<int>(array)
even though array
is obviously an array of int
.这就是为什么您需要指定
new ZeroPadArray<int>(array)
即使array
显然是一个int
数组。
The way to get around this is to create a second, non-generic class that you use as a static factory for creating things.解决这个问题的方法是创建第二个非泛型类,用作创建事物的静态工厂。 Something like:
就像是:
public static class ZeroPadArray
{
public static ZeroPadArray<T> Create<T>(T[,] arrayToPad)
{
return new ZeroPadArray<T>(arrayToPad);
}
}
Now, instead of typing:现在,而不是键入:
var paddedArray = new ZeroPadArray<int>(array);
you can type:你可以输入:
var paddedArray = ZeroPadArray.Create(array);
Saving you two characters of typing (but, you need to admit that typing the <int>
is frustrating).为您节省两个输入字符(但是,您需要承认输入
<int>
令人沮丧)。
It seems that you are confusing dimensions - Array.GetLength(0)
is for the first one in access Array[i, j]
and Array.GetLength(1)
is for the second.似乎您混淆了维度 -
Array.GetLength(0)
用于访问Array[i, j]
的第一个,而Array.GetLength(1)
用于第二个。 Also you can simplify copy by just scanning through Array
elements and adjusting destination indexes by one, you don't need to explicitly set others to 0
cause it would be done for you (unless you are using stackalloc
and skipping local init but I highly doubt that this is the case):您也可以通过扫描
Array
元素并将目标索引调整为 1 来简化复制,您不需要将其他人显式设置为0
因为它会为您完成(除非您使用stackalloc
并跳过本地初始化,但我非常怀疑情况就是这样):
var length0 = Array.GetLength(0);
var length1 = Array.GetLength(1);
for (int i = 0; i < length0; i++)
{
for (int j = 0; j < length1; j++)
{
ArrayZeroPad[i + 1, j + 1] = Array[i, j];
}
}
And in the "print" method too - y
should be the first dimension and x
- second:并且在“打印”方法中 -
y
应该是第一个维度, x
- 第二个:
var length = ArrayZeroPad.GetLength(0);
for (int y = 0; y < length; y++)
{
Console.WriteLine();
var i = ArrayZeroPad.GetLength(1);
for (int x = 0; x < i; x++)
{
Console.Write(ArrayZeroPad[y, x]);
}
Console.ReadLine();
}
int[,] Array = new int[,] { { 1, 2, 3 }, { 3, 4, 5 }, { 6, 7, 8 } };
int[,] ArrayZeroPad = new int[Array.GetLength(0) + 2, Array.GetLength(1) + 2];
for (int x = 0; x < ArrayZeroPad.GetLength(0); x++)
{
for (int y = 0; y < ArrayZeroPad.GetLength(0); y++)
{
//First row and last row
if (x == 0 || x == ArrayZeroPad.GetLength(0) - 1)
ArrayZeroPad[x, y] = 0;
else
{
//Fist column and last column
if (y == 0 || y == ArrayZeroPad.GetLength(0) - 1)
ArrayZeroPad[x, y] = 0;
else
{
//Content
ArrayZeroPad[x, y] = Array[x-1, y-1];
}
}
}
}
You can also solve this using Array.Copy()
.您也可以使用
Array.Copy()
解决此问题。 If you require highest performance and the arrays are big enough, then this might be faster than explicitly copying each element:如果您需要最高性能并且数组足够大,那么这可能比显式复制每个元素更快:
public static int[,] Pad(int[,] input)
{
int h = input.GetLength(0);
int w = input.GetLength(1);
var output = new int[h+2, w+2];
for (int r = 0; r < h; ++r)
{
Array.Copy(input, r*w, output, (r+1)*(w+2)+1, w);
}
return output;
}
The (r+1)*(w+2)+1
requires some explanation. (r+1)*(w+2)+1
需要一些解释。 Array.Copy()
treats a 2D array as a linear 1D array, and you must specify the destination offset for the copy as an offset from the start of the 1D array (in row-major order). Array.Copy()
将二维数组视为线性一维数组,您必须将副本的目标偏移量指定为距一维数组开头的偏移量(按行优先顺序)。
Since w
is the width of the input array and r
is the current row of the input array, the destination for the copy of the current input row will be the output row number, (r+1)
times the output row width (w+2)
, plus 1
for to account for the left-hand column of 0
in the output array.由于
w
是输入数组的宽度,而r
是输入数组的当前行,当前输入行的副本的目的地将是输出行号, (r+1)
乘以输出行宽度(w+2)
,加上1
for 以解释输出数组中的左侧列0
。
It's possible that using Buffer.BlockCopy()
(which operates on bytes) could be even faster:使用
Buffer.BlockCopy()
(对字节进行操作)可能会更快:
public static int[,] Pad(int[,] input)
{
int h = input.GetLength(0);
int w = input.GetLength(1);
var output = new int[h+2, w+2];
for (int r = 0; r < h; ++r)
{
Buffer.BlockCopy(input, r*w*sizeof(int), output, ((r+1)*(w+2)+1)*sizeof(int), w*sizeof(int));
}
return output;
}
As always, this is only worth worrying about if performance is critical, and even then only after you've benchmarked the code to verify that it actually is faster.一如往常,这是唯一值得担心如果性能是至关重要的,你基准代码来验证它实际上是更快后才即使这样。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.