Java string.split 与 C# Regex.split - 限制为一定数量的字段

Posted

技术标签:

【中文标题】Java string.split 与 C# Regex.split - 限制为一定数量的字段【英文标题】:Java string.split vs. C# Regex.split - limit to certain number of fields 【发布时间】:2020-05-22 22:55:47 【问题描述】:

我是一名 Java 开发人员,但正在开发一个 C# 项目。我需要做的是用分隔符拆分字符串,但将其限制为一定数量的字段。在 Java 中,我可以这样做:

String message = "xx/xx - xxxxxxxxxxxxxxxxxxx - xxxxxxx";
String[] splitMessage = message.split("\\s-", 3);

在这种情况下,它将被- 拆分,但我还想让它检查破折号之前的任何空格,并将其限制为字符串的 3 个字段。通过的字符串被分解为___ - ____________ - _________,第一个空格是日期(如12/31),第二个空格是关于字符串的消息,第三个空格是与消息相关的位置。我将其限制为 3 个字段的原因是数组 only 有 3 个元素。我这样做的原因是因为有时消息中可能会有破折号,看起来像这样:12/31 - Test message - test - Test City, 11111。所以我上面的 Java 代码会将其拆分为:

0: 12/31
1: Test message - test
2: Test City, 11111

我正在尝试在 C# 中实现类似的功能,但不确定如何将其限制为一定数量的字段。这是我的 C# 代码:

var splitMessage = Regex.Split(Message, " -");

问题在于,没有限制,它会将其拆分为 4 个或 5 个字段,而不仅仅是 3 个。例如,如果这是消息:12/31 - My test - don't use - just a test - Test City, 11111,它将返回一个带有 5 个索引的字符串 []:

0: 12/31
1: My test
2: don't use
3: just a test
4: Test City, 11111

当我希望它返回这个时:

0: 12/31
1: My test - don't use - just a test
2: Test City, 11111

在你问之前,我不能改变传入的字符串。我必须像在 Java 中那样解析它。那么是否有相当于将其限制为 3 个字段的方法?除了使用Regex.Split(),还有更好的方法吗?

【问题讨论】:

如果你想要["12/31","My test - don't use - just a test", "Test City, 11111"] Regex.Split count 参数将不起作用,因为它会在每场比赛中从左到右拆分。 尝试var m = Regex.Match(text, @"^([^-]*)(?: - (.*?))?(?: - ([^-]*))?$") 并检查m.Groups[1].Valuem.Groups[2].Valuem.Groups[3].Value,参见demo。 即使使用 Java 的 message.split("\\s-", 3) 也不会给您描述的结果。我建议使用前面的正则表达式并简单地使用message.indexOf(" -")message.lastIndexOf(" -"),然后使用三个 message.substring 调用。 @VGR 是的,它会在我的代码中工作,谢谢。 【参考方案1】:

如果你想基于- 的第一个和最后一个实例进行拆分,这样你就得到了三个字段(只要字符串中至少有两个破折号),C# 实际上确实有一个巧妙的技巧这。 C# Regex 允许非固定宽度的lookbehinds。所以下面的正则表达式:

(?<=^[^-]*)-|-(?=[^-]*$)

(<=      //start lookbehind
   ^     //look for start of string
   [^-]* //followed by any amount of non-dash characters
)        //end lookbehind
-        //match the dash
|        //OR
-        //match a dash
(?=      //lookahead for
   [^-]* //any amount of non-dash characters
   $     //then the end of the string
)        //end lookahead

将匹配第一个和最后一个破折号,并允许您以您想要的方式拆分字符串。

var splitMessage = Regex.Split(Message, "(?<=^[^-]*)-|-(?=[^-]*$)");

请注意,如果破折号较少,分成少于三个组也没有问题,但不会分成三个以上。

【讨论】:

这个好,我发了一个comment 类似的匹配方法,但不知道是否需要多字符分隔符支持。一个否定的字符类不会这样做。 确实如此,但如果我们需要多个分隔符,将其更改为(?:(?!delim1|delim2).)* 之类的内容并不难。但无论如何,嘿!感谢您对我上一个答案的建议:) 谢谢,这正是我一直在寻找的——我只是在一般的正则表达式上很烂,所以我不知道如何让它做我想做的事情。我很感激! @WitchKing17 没问题!我喜欢正则表达式,这些都是有趣的小挑战!很高兴我能帮忙:) @ZaelinGoodman,嗯,看起来这仍然只是一点点。你能不能让它以前只用空白捕捉破折号?因为有时我会收到如下所示的消息:test -test 而不是 test - test,它会正确拆分它,只是有时会在错误的位置拆分它。【参考方案2】:

您不能像分隔符那样在所需分组之一内进行拆分,除非那是最后一个组。

但是,您可以使用在第二组中尽可能多地消耗的自定义正则表达式来解析所述输入:

var splitMessage = Regex.Match("12/31 - Test message - test - Test City, 11111", "^(.+?) - (.+) - (.+)$")
    .Groups
    .Cast<Group>()
    // skip first group which is the entire match
    .Skip(1)
    .Select(x => x.Value)
    .ToArray();

鉴于第一组是“xx/xx”,您也可以选择使用此正则表达式:

"^(../..) - (.+) - (.+)$"
// or, assuming they are date
"^(\d2/\d2) - (.+) - (.+)$"

编辑:或者,您可以只用“ - ”分割,然后在超过 3 个匹配项时将中间的所有内容连接在一起:

var groups = "12/31 - Test message - test - Test City, 11111".Split(new[]  " - " , StringSplitOptions.None);
if (groups.Length > 3)

    groups = new[]
    
        groups[0],
        string.Join(" - ", groups.Skip(1).Take(groups.Length - 2)),
        groups[groups.Length - 1]
    ;

【讨论】:

您的正则表达式至少需要两个 - 分隔符,因此如果字符串出现 0 或 1 个分隔符,它将找不到匹配项。【参考方案3】:

当我必须在某些分隔符(包括可选空格)处拆分字符串时,我通常这样做:

String message = "xx/xx - xxxxxxxxxxxxxxxxxxx - xxxxxxx";
String[] splitMessage = message.split(" *- *", 3);    
System.out.println(Arrays.asList(splitMessage));

输出:[xx/xx,xxxxxxxxxxxxxxxxxx,xxxxxxx]

String message = "12/31 - My test - don't use - just a test - Test City; 11111";
String[] splitMessage = message.split(" *- *", 3);    
System.out.println(Arrays.asList(splitMessage));

输出:[12/31,我的测试,不要使用 - 只是一个测试 - 测试城市; 11111]

但你似乎想要一些不同的东西:

splitMessage[0] shall contain the first part
splitMessage[1] shall contain the second and third part
splitMessage[2] shall contain the rest

您想如何告诉您的计算机第二个输出元素应包含两个部分?我认为这是不可能的,除非将字符串分成所有 5 个部分,然后根据需要将这些部分重新连接在一起。

也许不清楚你想要什么结果。能否更明确地说明需求:如果输入字符串包含超过 3 个元素,会发生什么情况?

【讨论】:

询问澄清问题应该在问题的 cmets 中完成,而不是作为答案发布。

以上是关于Java string.split 与 C# Regex.split - 限制为一定数量的字段的主要内容,如果未能解决你的问题,请参考以下文章

System.String.Split(null) 不删除空格 (C#)

Java - 解析字符串 - String.split() 与 Pattern & Matcher

C#的String.Split 分割字符串用法详解的代码

C#中的Split用法以及详解

String.Split 仅在 C# 中的第一个分隔符上?

Java, Stringtokenizer和String split有啥区别?