【问题标题】:Parse a number from a string with non-digits in between从中间有非数字的字符串中解析一个数字
【发布时间】:2010-07-08 14:32:43
【问题描述】:

我正在处理 .NET 项目,我正在尝试仅解析字符串中的数值。例如,

string s = "12ACD";
int t = someparefun(s); 
print(t) //t should be 12

几个假设是

  1. 字符串模式始终是数字后跟字符。
  2. 数字部分始终为一位或两位数字。

是否有任何 C# 预定义函数来解析字符串中的数值?

【问题讨论】:

标签: c#


【解决方案1】:

没有这样的功能,至少我不知道。但是一种方法是使用正则表达式来删除所有不是数字的内容:

using System;
using System.Text.RegularExpressions;

int result =
    // The Convert (System) class comes in pretty handy every time
    // you want to convert something.
    Convert.ToInt32(
        Regex.Replace(
            "12ACD",  // Our input
            "[^0-9]", // Select everything that is not in the range of 0-9
            ""        // Replace that with an empty string.
    ));

此函数将为12ABC 生成12,因此如果您需要能够处理负数,则需要不同的解决方案。它也不安全,如果你只传递非数字,它会产生一个FormatException。以下是一些示例数据:

"12ACD"  =>  12
"12A5"   =>  125
"CA12A"  =>  12
"-12AD"  =>  12
""       =>  FormatException
"AAAA"   =>  FormatException

更详细但更安全的方法是使用int.TryParse()

using System;
using System.Text.RegularExpression;

public static int ConvertToInt(String input)
{
    // Replace everything that is no a digit.
    String inputCleaned = Regex.Replace(input, "[^0-9]", "");

    int value = 0;

    // Tries to parse the int, returns false on failure.
    if (int.TryParse(inputCleaned, out value))
    {
        // The result from parsing can be safely returned.
        return value;
    }

    return 0; // Or any other default value.
}

再来一些示例数据:

"12ACD"  =>  12
"12A5"   =>  125
"CA12A"  =>  12
"-12AD"  =>  12
""       =>  0
"AAAA"   =>  0

或者如果你只想要字符串中的第一个数字,基本上停在遇到不是数字的东西,我们突然也可以轻松处理负数:

using System;
using System.Text.RegularExpression;

public static int ConvertToInt(String input)
{
    // Matches the first numebr with or without leading minus.
    Match match = Regex.Match(input, "-?[0-9]+");

    if (match.Success)
    {
        // No need to TryParse here, the match has to be at least
        // a 1-digit number.
        return int.Parse(match.Value);
    }

    return 0; // Or any other default value.
}

我们再次对其进行测试:

"12ACD"  =>  12
"12A5"   =>  12
"CA12A"  =>  12
"-12AD"  =>  -12
""       =>  0
"AAAA"   =>  0

总的来说,如果我们谈论的是用户输入,我会考虑根本不接受无效输入,只使用 int.TryParse() 而不使用一些额外的魔法,并且在失败时通知用户输入不是最佳的(并且可能会再次提示输入有效号码)。

【讨论】:

  • 危险将罗宾逊!危险!正则表达式在前!破坏!毁灭!
  • @Randoplho:我知道,我知道......我知道他现在有两个问题,但没有一个是我的。 ;P
【解决方案2】:

正则表达式是一种方法,如demonstrated by Bobby

根据您的假设,另一种方法是以这种方式使用TakeWhile(使用TryParse 以提高安全性):

string input = "12ACD";
string digits = new string(input.TakeWhile(c => Char.IsDigit(c)).ToArray());
int result;
if (Int32.TryParse(digits, out result))
{
    Console.WriteLine(result);
}

当然,代码的用途不会立即向读者展示,因为他们大部分时间都将花费在破译 TakeWhile 部分被转换为 string 上。

【讨论】:

  • 如果你将 TakeWhile 调用封装到一个额外的 GetStartingDigits 函数中,它应该很容易阅读代码。
  • 为什么将Int32.TryParse 括在if 中?如果解析失败,原始result 将保持不变。此外,您不应该使用 try catch 吗?因此内置函数TryParse?如果您使用.Parse,那将是有意义的。不过,非常有用的代码。谢谢
  • @ppumkin TryParse 用于避免显式尝试/捕获。我将它包含在if 中,因为它返回一个布尔值,所以通过使用它,您可以立即知道解析是否成功,并且可以继续使用result。如果您不想依赖默认的 result 解析尝试失败,这将为您提供更多控制。这取决于场景。如果我选择使用 int.Parse,我会使用 try/catch。同样的想法,但TryParse 是为了让我们更具可读性并避免额外的工作。感谢您的评论!
【解决方案3】:

Bobby 描述的正则表达式方法可能是处理这个问题的最佳方法,但如果您对正则表达式特别警惕,您可以结合使用 LINQ 和 Convert.ToInt32 方法:

    string test = "12ACD";
    int number = Convert.ToInt32(new String(test.Where(x => char.IsNumber(x)).ToArray()));

【讨论】:

  • ToCharArray 调用是不必要的,因为string 实现了IEnumerable<char>。另外,我可能会选择TakeWhile 而不是Where,这样,例如,“12ACD3”就不会变成“123”。但这显然需要指定。
  • @Dan Tao - 我听说字符串实现了 IEnumerable 但由于智能感知在我的 VS2008 副本中没有给我这个选项,我不确定它是否会起作用。刚刚测试它编译正常。所以我编辑了我的答案。为信息喝彩,只是表明你不能总是相信你的工具。
  • 许多人使用ToCharArray 来强制它显示在 IntelliSense 中,但没有它也能正常工作。您可能还对这篇文章感兴趣:为什么 VS 2008 不显示 Intellisense for String 类的扩展方法 (stackoverflow.com/questions/345883/…)。
【解决方案4】:

使用Sprache

int t = Parse.Number.Select(int.Parse).Parse("12ACD");
print(t) //t should be 12 and type of int32.

【讨论】:

    【解决方案5】:

    由于您知道您关心的唯一字符是前 2 个或只是第一个,因此您可以在前 2 个字符上使用 int.TryParse 和 SubStringing。

    如果返回 false(即第二个字符不是数字),那么只需对第一个字符执行 int.Parse 和 Substring ing。

    可能有一种更清洁的方法,但根据您的假设,应该可以完成这项工作。

    【讨论】:

      【解决方案6】:

      即使 CLI 中有这样的内在函数;您要么发现它仅适用于特定表单,要么必须告诉它与所述表单一起使用的表单和/或行为。换句话说,您希望您的解决方案如何处理“AB123CD456EF”?仅解析第一次出现,将所有数字字符连接在一起并解析,还是将每次出现解析为可枚举结果的元素?

      正则表达式可以很好地处理任何这些情况。我建议您将您的解决方案广泛地包装成可读的、有据可查的函数,无论您选择已经给出的好建议中的哪一个。

      【讨论】:

        【解决方案7】:

        Ahmads 解决方案让我想到了这一点 - 假设字符串始终是一两个数字,后跟至少一个非数字字符:

        int number = Int32.Parse(
            Char.IsDigit(foo, 1)  ?  foo.Substring(0, 2)  :  foo.Substring(0, 1), 
            CultureInfo.InvariantCulture);
        

        逻辑如下:如果索引 1(位置 2)处的字符是数字,则获取前两个字符,然后解析它们。如果索引 1 处的字符不是数字,则获取第一个字符,然后对其进行解析。

        【讨论】:

          【解决方案8】:

          怎么样:

              public int ReadStartingNumber(string text)
              {
                  if (string.IsNullOrEmpty(text) || !char.IsDigit(text[0]))
                      throw new FormatException("Text does not start with any digits");
          
                  int result = 0;
                  foreach (var digit in text.TakeWhile(c => char.IsDigit(c)))
                  {
                      result = 10*result + (digit - '0');
                  }
          
                  return result;
              }
          

          【讨论】:

          • 以非常小的可读性代价,您也可以简单地在经典的 'i' 上使用一个普通的 for 循环,并在第一个非数字处中断。
          【解决方案9】:

          您可以使用 RegEx.Match(正则表达式) 阅读有关它们的 msdn 文章。很简单。

          【讨论】:

            【解决方案10】:

            Int32.Parse()

            也有其他数字类型的等价物。

            编辑:重读后,我看到你的字符串不只是那个数字。在这种情况下,您需要先使用正则表达式提取数字,然后再使用解析。

            【讨论】:

              【解决方案11】:

              根据您的假设,最直接的代码如下...

              string s = "13AD";
              string s2 = s.Substring(0, s.Length - 2);
              int i = int.Parse(s2);
              

              如果您的假设得到保证,这是最易读的方法。不需要正则表达式或花哨的 LINQ 东西。 LINQ 很棒,但它似乎经常被过度使用。

              【讨论】:

              • 你不是说s.Substring(0, 2);吗?尽管如此,如果只有一位数字,它还是行不通的。 (使用上面的代码,“12ACD”会失败,因为 s.Length-23 并且 12A 不能解析为 Int32。)
              • 由于格式问题,我误读了问题。他有一个列表,但它没有这样格式化。我认为他有 1 或 2 个字符后跟正好 2 个字符,因为 2 个列表项看起来不像列表项。事实上,他有任意数量的字符,所以正则表达式选项(目前投票最多)可能是最好的,正如答案的海报敏锐地指出的那样。
              • 哦,Substring(0,2); 是不够的,因为他可以有一个或两个数字。这部分无法保证。
              猜你喜欢
              • 1970-01-01
              • 1970-01-01
              • 2013-01-10
              • 2016-08-13
              • 2023-01-31
              • 2017-09-30
              • 1970-01-01
              • 1970-01-01
              相关资源
              最近更新 更多