C# LINQ:如何将字符串“[1, 2, 3]”解析为数组?

4
我正在尝试将一个字符串解析成数组,并寻找一种非常简洁的方法。
string line = "[1, 2, 3]";
string[] input = line.Substring(1, line.Length - 2).Split();
int[] num = input.Skip(2)
                 .Select(y => int.Parse(y))
                 .ToArray();

我试过去掉Skip(2),但由于非整数字符串,无法获取数组。我的问题是这些LINQ函数的执行顺序是什么。在这里调用了多少次Skip?

提前致谢。


1
你说“去掉了Skip()之后它不再工作”,这是什么意思?在使用Skip()时它做了什么,现在没有Skip()为什么不行了? - krillgar
1
不要使用可能会失败的子字符串,而应该使用.Trim('['],)。如果删除空格,则Split也不再起作用,例如:1,2,3 - Tim Schmelter
1
你需要使用逗号 , 进行分割,而不仅仅是 Split。 - Sriram Sakthivel
调用Skip(2),然后执行返回对象的Select()语句,接着将从Select返回的对象转换为数组。 - Caio César S. Leonardi
你为什么不用JsonSerializer解析这个? - Yuval Itzchakov
显示剩余5条评论
4个回答

6
订单是您指定的顺序。因此,input.Skip(2) 跳过数组中的前两个字符串,因此只剩下最后一个字符串 3。它可以解析为 int。如果删除 Skip(2),则尝试解析所有字符串。这不起作用,因为逗号仍然存在。您已经按空格拆分了字符串,但未删除逗号。
您可以使用 line.Trim('[', ']').Split(',');int.TryParse
string line = "[1, 2, 3]";
string[] input = line.Trim('[', ']').Split(',');
int i = 0;
int[] num = input.Where(s => int.TryParse(s, out i)) // you could use s.Trim but the spaces don't hurt
                 .Select(s => i)
                 .ToArray(); 

为了澄清,我只是使用int.TryParse来确保如果输入包含无效数据时不会出现异常。它并不能修复任何问题。同样可以使用int.Parse

更新:正如Eric Lippert在评论区证明的那样,在LINQ查询中使用int.TryParse可能会有害。因此最好使用一个帮助方法来封装int.TryParse并返回Nullable<int>。因此,可以编写以下扩展:

public static int? TryGetInt32(this string item)
{
    int i;
    bool success = int.TryParse(item, out i);
    return success ? (int?)i : (int?)null;
}

现在你可以用以下方式在LINQ查询中使用它:
string line = "[1, 2, 3]";
string[] input = line.Trim('[', ']').Split(',');
int[] num = input.Select(s => s.TryGetInt32())
                 .Where(n => n.HasValue)
                 .Select(n=> n.Value)
                 .ToArray();

1
非常简洁而详细的回答,回答得非常好。 - MikeT
2
我强烈建议不要在LINQ查询中使用输出参数,就像这样。虽然这种特定用法是安全的,但您很容易进行微小更改,使您陷入变量被多次改变的情况,并且查询的后续部分查看的是当前变量的值,而不是您想要的值。 - Eric Lippert
1
当然。假设我们有一个序列 var seq = new List<string> { "1", "blah", "3" };,我们希望提取一系列数字,如果其中一个不是数字,则使用零代替。很容易实现 var nums = from item in seq let success = int.TryParse(item, out tmp) select success ? tmp : 0; 看起来不错,对吧?现在,如果我们进行了小的编辑,并说“我希望按项目排序”,请预测执行查询的结果 from item in seq let success = int.TryParse(item, out tmp) orderby item select success ? tmp : 0; --你的预测是否与现实相符? - Eric Lippert
1
你的看似正确但实际上是错误的信念正是为什么这个模式很危险。这个查询实际上意味着“生成一系列的对和副作用。第一对是{"1", true},副作用是tmp = 1。第二对是{"blah", false},副作用是tmp = 0。第三对是{"3", true},副作用是tmp = 3。现在按照第一项对这些对进行排序。现在对于排序后序列中的每一个对,检查对中的第二项,如果它是false,则产生零,如果它是true,则产生tmp的当前值”。tmp的当前值为3。 - Eric Lippert
3
开发人员实现该方法的时间为五分钟。但是该方法需要一个规范--当然,这是一个简短的规范,但是测试和文档需要该规范进行工作。接着你需要编写测试、文档,然后将文档翻译成日语,......所有这些都会累加成大量工作量。所有这些工作都不是用于更昂贵功能的开发,而这些功能拥有更高的投资回报率。就像我说的,没有便宜的功能,只有相对较少成本的功能。 - Eric Lippert
显示剩余11条评论

3
除非您跳过前两行,否则它将无法工作的原因是这些行在“int”之后有逗号。您的输入看起来像这样:
"1," "2," "3"

只有最后一个条目可以解析为 int; 最初的两个将产生异常。

通过将逗号和空格作为分隔符传递给 Split,可以解决问题:

string[] input = line
    .Substring(1, line.Length - 2)
    .Split(new[] {',', ' '}, StringSplitOptions.RemoveEmptyEntries);

注意使用StringSplitOptions.RemoveEmptyEntries来删除由逗号和空格引起的空字符串。

0

我认为这样做会更好:

JsonConvert.DeserializeObject(line, typeof(List<int>));

0

你可以尝试

    string line = "[1,2,3]";
    IEnumerable<int> intValues = from i in line.Split(',')
                                 select Convert.ToInt32(i.Trim('[', ' ', ']'));

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接