【问题标题】:Match increasing/decreasing sequences using regex使用正则表达式匹配增加/减少序列
【发布时间】:2014-11-14 15:03:40
【问题描述】:

我想匹配任何只包含递增或递减数字的字符串。这意味着这些字符串将被匹配:

123, 234567, 0123456789, 87654, 321

这些不会:

7891011, 1234566789, 987865, 134

我一直在寻找答案,all I found 是使用正则表达式无法做到这一点。但是,我想用正则表达式来做,否则会在代码中弄得一团糟。

感谢您的回答。

【问题讨论】:

  • Regex 不适合在这里使用
  • 正则表达式是在字符串中查找模式的好工具。但是,它没有任何数字或计数的概念。您通常可以验证一个数字是否在一个小范围内,例如 0..100,但无法确定这些数字是连续的。
  • 所以它不知道一个字符的ASCII码并与前一个比较?
  • “正则表达式,否则会弄得一团糟” - 你听说过"now you have two problems"这个短语吗?
  • 如果你让问题更通用,它可能会更好:“匹配来自唯一字符字符串(即连续数字)的任何子字符串”。

标签: c# regex


【解决方案1】:

从技术上讲,使用前瞻是可能的,但它看起来不太好。

升序逻辑:

  1. 从字符串的开头开始
  2. 仅在当前数字后跟下一个数字或字符串结尾的情况下匹配当前数字。移动到下一个位置。
  3. 重复 2 直到到达字符串结尾。

123 的示例:

  1. 从字符串的开头开始
  2. 当前符号为 0?不。好的,跳过。
  3. 当前符号是 1?是的。它后面是 2 还是字符串结尾?是的。好的,转到下一个符号。
  4. 当前符号是 2?是的。后面是 3 还是字符串结尾?是的。好的,转到下一个符号。
  5. 当前符号是 3?是的。后面是 4 还是字符串结尾?是的。好的,转到下一个符号。
  6. 当前符号是 4?不,当前符号是 5?不。...它是字符串的结尾吗?是的。

134 示例:

  1. 从字符串的开头开始
  2. 当前符号为 0?不。好的,跳过。
  3. 当前符号是 1?是的。它后面是 2 还是字符串结尾?不,错误,不匹配。

正则表达式匹配升序模式:

^(?:0(?=1|$))?(?:1(?=2|$))?(?:2(?=3|$))?(?:3(?=4|$))?(?:4(?=5|$))?(?:5(?=6|$))?(?:6(?=7|$))?(?:7(?=8|$))?(?:8(?=9|$))?9?$

Debuggex Demo

正则表达式匹配降序模式:

^(?:9(?=8|$))?(?:8(?=7|$))?(?:7(?=6|$))?(?:6(?=5|$))?(?:5(?=4|$))?(?:4(?=3|$))?(?:3(?=2|$))?(?:2(?=1|$))?(?:1(?=0|$))?0?$

Debuggex Demo

结合在一起:

^((?:0(?=1|$))?(?:1(?=2|$))?(?:2(?=3|$))?(?:3(?=4|$))?(?:4(?=5|$))?(?:5(?=6|$))?(?:6(?=7|$))?(?:7(?=8|$))?(?:8(?=9|$))?9?|(?:9(?=8|$))?(?:8(?=7|$))?(?:7(?=6|$))?(?:6(?=5|$))?(?:5(?=4|$))?(?:4(?=3|$))?(?:3(?=2|$))?(?:2(?=1|$))?(?:1(?=0|$))?0?)$

Debuggex Demo

Regex101 Demo

【讨论】:

  • +1。所用构造的信息 - MSDN: zero-width positive lookahead assertion
  • 哇,感谢您的精彩回答!虽然,它看起来很吓人/很大/不可读,所以我可能会重新考虑是否真的会使用它,但肯定回答了我的问题。
  • 这绝对是问题的答案。但这不是 Regex 的设计目的。这是一种非常迂回的方式来节省几行代码,而且表达式大得令人痛苦。
【解决方案2】:

执行此操作的代码不会比正则表达式更混乱。正则表达式不擅长数学。

这样的事情可以做到:

bool IsSequentiallyIncreasing(string input)
{
    char? lastDigit = null;

    foreach (char c in input)
    {
        if (!c.IsDigit || (lastDigit != null && c != lastDigit + 1))
        {
            return false;
        }

        lastDigit = c;
    }

    return true;
}

因此,您遍历字符串中的所有字符,一旦遇到非数字或不是最后一个数字 + 1 的数字,您就会返回 false。

对递减也做同样的事情,你可以简单地调用IsSequentiallyIncreasing(input) || IsSequentiallyDecreasing(input)。我将由您来添加错误处理并将这两种方法合并为一个。

【讨论】:

  • 是的,我已经有了检查序列的方法,但我更愿意使用正则表达式
  • 我的意思是我更喜欢 使用正则表达式,但如果没有办法做到这一点,那么我想我会被困在一个方法上。无论如何,谢谢。
  • 有可能,但同样丑陋。你的正则表达式看起来像(([01|12|23|...|89]){1,}|([98|87|76|...|10]){1,})
  • 是的,我认为这是不可接受的,因为我必须首先对 2 位数字执行此操作,而不是对 3 位数字执行此操作,依此类推,直到 10 位数字。我只是好奇是否没有一些聪明的 10-20 个字符长的正则表达式可以为我做到这一点。
【解决方案3】:

没有可以做到这一点的正则表达式模式,但您可以使用正则表达式来帮助收集这些项目

List<Match> oMatches = new List<Match>();
String sData = "123, 234567, 0123456789, 87654, 321 , 7891011, 1234566789, 987865";
Regex.Replace(sData, @"\d+", delegate(Match oMatch)//delegate is run for each match as closure
{
    char? oLast = null;
    foreach (char oChar in oMatch.Value)//for each char
    {
        if ((oLast != null && Math.Abs(oChar - oLast) > 1))//not in sequense
            return oMatch.Value;//early return
        oLast = oChar;
    }
    oMatches.Add(oMatch)//add to outside collection
    return oMatch.Value; //return match itself because we don't actually want to replace anything
});

【讨论】:

    【解决方案4】:

    我会创建一个函数IsSequential

    static bool IsSequential(string input)
    {
        var nums = input.Select(Convert.ToInt32);
    
        int last = nums.First();
        foreach (var num in nums.Skip(1))
        {
            if (num < last) return false;
            last = num;
        }
    
        return true;
    }
    

    然后只需检查每个数字是否为IsSequential,或者在反转时是否为IsSequential

    var inputs = @"123, 234567, 0123456789, 87654, 321, 7891011, 1234566789, 987865";
    var valids = inputs
                    .Split(',')
                    .Select(n => n.Trim())
                    .Where(n => IsIncreasing(n) || IsIncreasing(new string(n.Reverse().ToArray())))
                    .ToList();
    

    如果您需要它们精确地增加 1,请使用 if(num != last+1)

    【讨论】:

    • 是的,听起来我必须采用这样的解决方案!虽然,我试图避免它。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-11-29
    • 1970-01-01
    • 2018-01-08
    • 2013-12-30
    相关资源
    最近更新 更多