【问题标题】:Best option how to find last 2 digits in string with RegEx or LINQ in C#?最佳选择如何在 C# 中使用 RegEx 或 LINQ 查找字符串中的最后 2 位数字?
【发布时间】:2017-09-12 11:53:00
【问题描述】:

我正在尝试使用 RegExLINQ 获取字符串中的 最后 2 位数字。 例如我得到了这些字符串:

N43OET28W -> result should be 28
N1OET86W  -> result should be 86
S02CT55A  -> result should be 55
M4AKT99A  -> result should be 99
1W24ET39W -> result should be 39
S03KT45A  -> result should be 45
M1AKT23A  -> result should be 23
N1OET35W  -> result should be 35
N12FET42W -> result should be 42
MAKTFDAAD -> result should be null or 0
N3XUK407Q -> result should be 07
MAKT23A   -> result should be 23

现在我尝试了这段代码:

  getIntPattern("N1WET99W");
  getIntPattern("S03KT45A");
  getIntPattern("M1AKT23A");
  getIntPattern("N1OET35W");
  getIntPattern("N1OET42W");
  getIntPattern("MAKTFDAAD");
  getIntPattern("N12FET42W");

  private int getIntPattern(string text)
  {
    int result = 0;
    string m = Regex.Matches(text, @".*?\d+.*?(\d+)")
               .Cast<Match>()
               .Select(match => match.Groups[1].Value).First();
    int.TryParse(m, out result);
    return result;
  }

有没有更好的方法来实现这一点?输入字符串的长度不同,开头可以包含更多数字。我只需要最后两位数。

【问题讨论】:

  • 请问"ABC12DEF456XY" 的预期输出是多少(124556)?
  • 只有最后2位,这里是56
  • 如果最后两位数字被非数字分隔怎么办? W875W6 会产生什么?
  • 如果您正在寻找更好的答案,那么不应该将其发布在 codereview.stackexchange.com 上吗?
  • @juharr:XYZ5A2 无效。我必须是 2 位数字或 null/false

标签: c# regex linq


【解决方案1】:

您可以尝试Linq:尝试从字符串末尾开始的每个 2 字母子字符串:

  string source = "N43OET28W";

  string result = Enumerable
    .Range(2, source.Length - 1)
    .Select(index => source.Substring(source.Length - index, 2))
    .Where(item => item.All(c => char.IsDigit(c)))
    .FirstOrDefault();

如果你正在寻找速度,比如说,你有很多项要分析我建议for循环:

  int result = -1;
  int last = -1;

  for (int i = source.Length - 1; i >= 0; --i) {
    int current = source[i] - '0';

    if (current >= 0 && current <= 9)
      if (last >= 0 && last <= 9) {
        result = current * 10 + last;

        break;
      }
      else
        last = current;
    else
      last = -1;
  }

【讨论】:

  • 嗨,Dmitry,您的解决方案可以解决问题。一切都比正则表达式更快...循环中最快的一个,但对于我的项目来说,最好的选择是您的 linq 解决方案。谢谢!!
  • @biohell:最好的通常是一个模糊的请求(最快最易读最短)。不客气!
【解决方案2】:

我会用这个方法:

public static int? GetLastDigits(string text, int maxDigits = int.MaxValue)
{
    var digits = new Stack<char>();  // Last-in-First-out because we iterate backwards
    for (int i = text.Length - 1; i >= 0; i--)
    {
        if (char.IsDigit(text[i]))
            digits.Push(text[i]);
        else if (digits.Count > 0)
            break;
        if (digits.Count == maxDigits)
            break;
    }

    if (digits.Count == 0)
        return null;
    return int.Parse(string.Concat(digits));
}

【讨论】:

  • 对于 ABCD123 将返回 123 而不是 23。但根据示例不确定这是否重要。
  • 感谢蒂姆的回答。我刚刚将 Dmitry 的解决方案与 Linq 一起使用。您的解决方案就像 dimitrys 非常快!
【解决方案3】:

通过对正则表达式进行一些精心设计,可以将 LINQ 和正则表达式的组合简化为正则表达式:

private static int? GetIntPattern(string text) {
    var m = Regex.Match(text, @"(\d{2})\D*$");
    int res;
    return m != null && int.TryParse(m.Groups[1].Value, out res) ? (int?)res : null;
}

这个想法是用\D*$将两个数字“锚定”到字符串的末尾,使其成为最后两个可用数字。

Demo.

【讨论】:

    【解决方案4】:

    这是我的两分钱。如果性能是关键利益相关者,这可以通过正则表达式和一些 Parallel Linq 轻松实现。

    假设data 包含需要挖掘的文本列表。

    var rx = new Regex(
        "(\\d{2})(?:(?=\\D$)|$)",
        RegexOptions.Compiled | RegexOptions.Singleline);
    

    1)

    foreach (var item in data)
    {
        var match = rx.Match(item);
        var value = match?.Groups[1].Value;
    
        Console.WriteLine(string.IsNullOrEmpty(value)?"0":value);
    }
    

    2)

    var result = data.AsParallel().Select(
        x =>
        {
            var match = rx.Match(x);
            var value = match?.Groups[1].Value;
            return string.IsNullOrEmpty(value) ? "0" : value;
        }).ToArray();
    
    foreach (var s in result)
    {
        Console.WriteLine(s);
    }
    

    它会在屏幕上发射:

    28
    86
    55
    99
    39
    45
    23
    35
    42
    0
    07
    23
    

    ideone查看完整演示

    【讨论】:

      【解决方案5】:

      这不是很有效,但是给定了string 扩展函数Reverse

      public static string Reverse(this string s) => new String(s.ToCharArray().Reverse().ToArray());
      

      你可以用正则表达式将后两位作为前两位拉出来:

      private int getIntPattern(string text) => Convert.ToInt32("0"+Regex.Match(text.Reverse(), @"\d{2}").Value.Reverse());
      

      我认为最有效的方法是向后扫描并找到您要查找的内容:

      private int getIntPattern(string text) {
          for (var pos = text.Length - 1; pos > 0; --pos)
              if (Char.IsDigit(text[pos]) && Char.IsDigit(text[pos - 1]))
                  return Convert.ToInt32(text.Substring(pos-1, 2));
          return 0;
      }
      

      【讨论】:

        【解决方案6】:

        您可以添加一个结束锚点 $,以从字符串末尾开始搜索...

        string pattern = @"[0-9]{2}(?=[A-Za-z]*$)";
        string input = "N43OET28W";
        var matches = Regex.Matches(input, pattern);
        

        说明:从字符串末尾开始,忽略字符,取2位。

        q(?=u):正向前瞻,如果后面跟着u则匹配q

        $: 字符串锚的结尾

        [0-9]{2}:正好 2 位数

        【讨论】:

          猜你喜欢
          • 2023-03-22
          • 1970-01-01
          • 1970-01-01
          • 2013-12-21
          • 1970-01-01
          • 1970-01-01
          • 2012-03-10
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多