在 C# 中将二维数组转换为字符串,寻找最优雅的方式
Convert 2D array to string in C#, looking for most elegant way
我不敢相信没有聪明的方法可以从二维数组中得到这样的东西,
在这种情况下 int[,] a
:
"{1,2,3},{4,5,6},{7,8,9}"
我读过很多类似的问题,了解到 string.Join()
只能用于锯齿状数组(二维)。但我不想使用它们,因为初始化更复杂,而且当我的行(长度相同)分布在内存中的多个位置时感觉很糟糕。
这是我的 "normal" 代码:
var s = "";
for (int i = 0; i < a.GetLength(0); i++) {
if (i > 0) s += ',';
s += '{';
for (int j = 0; j < a.GetLength(1); j++) {
if (j > 0) s += ',';
s += a[i, j];
}
s += '}';
}
这里是 "golfed" 一个:
var s = "{";
var i = 0;
foreach (var item in a) s += (i++ > 0 ? i % a.GetLength(1) == 1 ? "},{" : "," : "") + item;
s += '}';
:) - 也不是很优雅,可读性也很差。
有什么建议吗?我对 Linq 持开放态度,因为它不一定要快。我对改进代码的优雅很感兴趣,但不仅仅是将其移至扩展方法。
考虑这种方法:
var numbers = new int[,] { { 1, 2, 3 }, { 4, 5, 6 }, { 7, 8, 9 } };
var results = string.Join(",",
Enumerable.Range(0, numbers.GetUpperBound(0) + 1)
.Select(x => Enumerable.Range(0, numbers.GetUpperBound(1) + 1)
.Select(y => numbers[x, y]))
.Select(z => "{" + string.Join(",", z) + "}"));
Console.WriteLine(results);
Console.ReadLine();
它与您的非常相似,但使用的是 LINQ。 它将二维数组投影为LINQ可枚举,然后用大括号括起来,并在需要的地方添加逗号。
Linq 解决方案,不是性能方面的。
var str = string.Join(",", a.OfType<int>()
.Select((value, index) => new {value, index})
.GroupBy(x => x.index / a.GetLength(1))
.Select(x => $"{{{string.Join(",", x.Select(y => y.value))}}}"));
请注意,您不能在二维数组上使用 Select
,但您可以使用 OfType
,这将为二维数组 return 枚举,枚举器将水平遍历二维数组。
x.index / a.GetLength(1)
简单地将每个索引除以总行数。所以如果你有 3 行,你的索引将等效地分布在 3 行中。
最后对每个组进行字符串连接
稍微简化一点的版本。 (分组结果选择器中的格式)
var str = string.Join(",", a.OfType<int>()
.Select((value, index) => new {value, index})
.GroupBy(x => x.index / a.GetLength(1), x => x.value,
(i, ints) => $"{{{string.Join(",", ints)}}}"));
AFAIK,当我们想要一个对象的字符串时,我们正在调用序列化,所以我更喜欢使用像 Newtonsoft.Json:[=17= 这样的序列化程序]
var result = $@"{{{JsonConvert.SerializeObject(a)
.Trim('[', ']').Replace("[", "{").Replace("]", "}")}}}";
一种使用像您的解决方案一样简单的 for
s 并删除 if
s 的方法可以是 - this code will be faster for small arrays -:
var result = string.Empty;
var maxI = a.GetLength(0);
var maxJ = a.GetLength(1);
for (var i = 0; i < maxI; i++)
{
result += ",{";
for (var j = 0; j < maxJ; j++)
{
result += $"{a[i, j]},";
}
result += "}";
}
result = .Replace(",}", "}").Substring(1);
建议使用 StringBuilder
来提高大数组的性能:
var sb = new StringBuilder(string.Empty);
var maxI = a.GetLength(0);
var maxJ = a.GetLength(1);
for (var i = 0; i < maxI; i++)
{
sb.Append(",{");
for (var j = 0; j < maxJ; j++)
{
sb.Append($"{a[i, j]},");
}
sb.Append("}");
}
sb.Replace(",}", "}").Remove(0, 1);
var result = sb.ToString();
发布此答案仅供参考,适用于当您不介意以连续方式呈现数据以及边界是静态且始终已知的情况时。
您可以使用 LINQ Cast
将字符串输出为纯 csv,然后 string.Join
。
var array = new int[2,3] { { 1, 2, 3 }, { 1, 2, 3 } };
var output = string.Join(',', array.Cast<int>());
要解析它:
var input = new int[upperLen,lowerLen];
for (var upper = 0; upper < upperLen; upper++)
for(var lower = 0; lower < lowerLen; lower++)
input[upper, lower] = int.Parse(parsed[(upper * lowerLen) + lower]);
参见示例 here。
我不敢相信没有聪明的方法可以从二维数组中得到这样的东西,
在这种情况下 int[,] a
:
"{1,2,3},{4,5,6},{7,8,9}"
我读过很多类似的问题,了解到 string.Join()
只能用于锯齿状数组(二维)。但我不想使用它们,因为初始化更复杂,而且当我的行(长度相同)分布在内存中的多个位置时感觉很糟糕。
这是我的 "normal" 代码:
var s = "";
for (int i = 0; i < a.GetLength(0); i++) {
if (i > 0) s += ',';
s += '{';
for (int j = 0; j < a.GetLength(1); j++) {
if (j > 0) s += ',';
s += a[i, j];
}
s += '}';
}
这里是 "golfed" 一个:
var s = "{";
var i = 0;
foreach (var item in a) s += (i++ > 0 ? i % a.GetLength(1) == 1 ? "},{" : "," : "") + item;
s += '}';
:) - 也不是很优雅,可读性也很差。
有什么建议吗?我对 Linq 持开放态度,因为它不一定要快。我对改进代码的优雅很感兴趣,但不仅仅是将其移至扩展方法。
考虑这种方法:
var numbers = new int[,] { { 1, 2, 3 }, { 4, 5, 6 }, { 7, 8, 9 } };
var results = string.Join(",",
Enumerable.Range(0, numbers.GetUpperBound(0) + 1)
.Select(x => Enumerable.Range(0, numbers.GetUpperBound(1) + 1)
.Select(y => numbers[x, y]))
.Select(z => "{" + string.Join(",", z) + "}"));
Console.WriteLine(results);
Console.ReadLine();
它与您的非常相似,但使用的是 LINQ。 它将二维数组投影为LINQ可枚举,然后用大括号括起来,并在需要的地方添加逗号。
Linq 解决方案,不是性能方面的。
var str = string.Join(",", a.OfType<int>()
.Select((value, index) => new {value, index})
.GroupBy(x => x.index / a.GetLength(1))
.Select(x => $"{{{string.Join(",", x.Select(y => y.value))}}}"));
请注意,您不能在二维数组上使用 Select
,但您可以使用 OfType
,这将为二维数组 return 枚举,枚举器将水平遍历二维数组。
x.index / a.GetLength(1)
简单地将每个索引除以总行数。所以如果你有 3 行,你的索引将等效地分布在 3 行中。
最后对每个组进行字符串连接
稍微简化一点的版本。 (分组结果选择器中的格式)
var str = string.Join(",", a.OfType<int>()
.Select((value, index) => new {value, index})
.GroupBy(x => x.index / a.GetLength(1), x => x.value,
(i, ints) => $"{{{string.Join(",", ints)}}}"));
AFAIK,当我们想要一个对象的字符串时,我们正在调用序列化,所以我更喜欢使用像 Newtonsoft.Json:[=17= 这样的序列化程序]
var result = $@"{{{JsonConvert.SerializeObject(a)
.Trim('[', ']').Replace("[", "{").Replace("]", "}")}}}";
一种使用像您的解决方案一样简单的 for
s 并删除 if
s 的方法可以是 - this code will be faster for small arrays -:
var result = string.Empty;
var maxI = a.GetLength(0);
var maxJ = a.GetLength(1);
for (var i = 0; i < maxI; i++)
{
result += ",{";
for (var j = 0; j < maxJ; j++)
{
result += $"{a[i, j]},";
}
result += "}";
}
result = .Replace(",}", "}").Substring(1);
建议使用 StringBuilder
来提高大数组的性能:
var sb = new StringBuilder(string.Empty);
var maxI = a.GetLength(0);
var maxJ = a.GetLength(1);
for (var i = 0; i < maxI; i++)
{
sb.Append(",{");
for (var j = 0; j < maxJ; j++)
{
sb.Append($"{a[i, j]},");
}
sb.Append("}");
}
sb.Replace(",}", "}").Remove(0, 1);
var result = sb.ToString();
发布此答案仅供参考,适用于当您不介意以连续方式呈现数据以及边界是静态且始终已知的情况时。
您可以使用 LINQ Cast
将字符串输出为纯 csv,然后 string.Join
。
var array = new int[2,3] { { 1, 2, 3 }, { 1, 2, 3 } };
var output = string.Join(',', array.Cast<int>());
要解析它:
var input = new int[upperLen,lowerLen];
for (var upper = 0; upper < upperLen; upper++)
for(var lower = 0; lower < lowerLen; lower++)
input[upper, lower] = int.Parse(parsed[(upper * lowerLen) + lower]);
参见示例 here。