【问题标题】:Parse an integer from a string with trailing garbage从带有尾随垃圾的字符串中解析整数
【发布时间】:2010-12-06 09:15:00
【问题描述】:

我需要解析一个出现在字符串开头的十进制整数。

十进制数后面可能有尾随垃圾。这需要忽略(即使它包含其他数字。)

例如

"1" => 1
" 42 " => 42
" 3 -.X.-" => 3
" 2 3 4 5" => 2

.NET 框架中是否有内置方法可以做到这一点?

int.TryParse() 不适合。它允许尾随空格,但不允许其他尾随字符。

这很容易实现,但如果存在标准方法,我更愿意使用它。

【问题讨论】:

  • 我假设你讨厌正则表达式,但我认为这正是它们要解决的问题......
  • 使用正则表达式就可以了。但是如果有一个内置函数会更好。
  • 一个有效的“整数”字符是总是跟在还是只跟在空格字符前面?
  • @ChrisBD,根本不需要任何空格。但第一个非空格字符始终是数字。

标签: c# .net parsing


【解决方案1】:
string s = " 3 -.X.-".Trim();
string collectedNumber = string.empty;
int i;

for (x = 0; x < s.length; x++) 
{

  if (int.TryParse(s[x], out i))
     collectedNumber += s[x];
  else
     break;     // not a number - that's it - get out.

} 

if (int.TryParse(collectedNumber, out i))
    Console.WriteLine(i); 
else
    Console.WriteLine("no number found");

【讨论】:

  • 那只会解析一位数字。该号码可能有多个数字。
  • @finnw- 然后在第一个 if 语句中抛出另一个 if 语句以迭代到以下位置进行检查
  • @finnw 好的,这是另一个处理多个数字的迭代
【解决方案2】:
foreach (var m in Regex.Matches(" 3 - .x. 4", @"\d+"))
{
    Console.WriteLine(m);
}

按 cmets 更新

不知道你为什么不喜欢正则表达式,所以我只发布我认为最短的解决方案。

要获得第一个 int:

Match match = Regex.Match(" 3 - .x. - 4", @"\d+");
if (match.Success)
    Console.WriteLine(int.Parse(match.Value));

【讨论】:

  • 我只需要第一个数字,所以你可以在里面加上一个'break'。
  • @finnw:对您对另一个答案的评论感到困惑。要获得第一个值,请使用 Regex.Match 函数,它可以在我的一个回滚中看到。
  • @Yuriy,我指的是多位数字(例如“42”),而不是字符串中的多个数字。
【解决方案3】:

没有标准的 .NET 方法可以做到这一点——尽管我不会惊讶地发现 VB 在 Microsoft.VisualBasic 程序集中有一些东西(随 .NET 一起提供,所以即使从C#)。

结果是否总是非负数(这会使事情变得更容易)?

说实话,正则表达式是这里最简单的选择,但是...

public static string RemoveCruftFromNumber(string text)
{
    int end = 0;

    // First move past leading spaces
    while (end < text.Length && text[end] == ' ')
    {
        end++;
    }

    // Now move past digits
    while (end < text.Length && char.IsDigit(text[end]))
    {
        end++;
    }

    return text.Substring(0, end);
}

那么你只需要在RemoveCruftFromNumber 的结果上调用int.TryParse(不要忘记整数可能太大而无法存储在int 中)。

【讨论】:

  • 垃圾在字符串的末尾,而不是开头(我不认为前导空格是垃圾,因为像 int.Parse 这样的内置函数可以处理。)跨度>
  • 好的,已编辑。 (这是否决票的原因吗?如果不是,我很想知道它的用途......)
  • “这是否决票的原因吗?如果不是,我很想知道它是为了什么......”这就像费德勒在抱怨裁判告诉他保持安静。跨度>
  • @Yuriy:恐怕我不明白你的评论。我总是喜欢听我为什么被否决,这样我就可以改进我的答案。 @finnw:是的,这个答案可以很容易地简化为正则表达式-我没有这样做是因为您对问题中正则表达式的不喜欢表达:) 如果您想让我把它放在答案中,请告诉我。
  • 除非您编辑了您的问题,否则他很可能无法删除反对票。系统有时会很不幸。
【解决方案4】:

我不确定在这种情况下您为什么要避免使用 Regex。

这里有一些小技巧,您可以根据自己的需要进行调整。

" 3 -.X.-".ToCharArray().FindInteger().ToList().ForEach(Console.WriteLine);

public static class CharArrayExtensions
{
    public static IEnumerable<char> FindInteger(this IEnumerable<char> array)
    {
        foreach (var c in array)
        {
            if(char.IsNumber(c))
                yield return c;
        }
    }
}

编辑: 关于不正确的结果(以及维护开发人员:)),这是正确的。

这是一个修订:

    public static int FindFirstInteger(this IEnumerable<char> array)
    {
        bool foundInteger = false;
        var ints = new List<char>();

        foreach (var c in array)
        {
            if(char.IsNumber(c))
            {
                foundInteger = true;
                ints.Add(c);
            }
            else
            {
                if(foundInteger)
                {
                    break;
                }
            }
        }

        string s = string.Empty;
        ints.ForEach(i => s += i.ToString());
        return int.Parse(s);
    }

【讨论】:

  • 这很聪明。维护开发人员当然会讨厌你。
  • 对于超过 1 位的数字,这会给出不正确的结果。
【解决方案5】:
    private string GetInt(string s)
    {
        int i = 0;

        s = s.Trim();
        while (i<s.Length && char.IsDigit(s[i])) i++;

        return s.Substring(0, i);
    }

【讨论】:

  • 我不是反对者,但我猜这是因为您对“nums”列表进行线性搜索,而不是更简单的“char.IsNumber(s[i])”。
  • 我也猜到了,但我不知道它的存在......无论如何我很高兴我学到了一些东西并在图中取了 -1 ;)
【解决方案6】:

还不如加上我的。

        string temp = " 3 .x£";
        string numbersOnly = String.Empty;
        int tempInt;
        for (int i = 0; i < temp.Length; i++)
        {
            if (Int32.TryParse(Convert.ToString(temp[i]), out tempInt))
            {
                numbersOnly += temp[i];
            }
        }

        Int32.TryParse(numbersOnly, out tempInt);
        MessageBox.Show(tempInt.ToString());

消息框仅用于测试目的,验证方法有效后将其删除。

【讨论】:

    【解决方案7】:

    您可以使用 Linq 来执行此操作,无需正则表达式:

    public static int GetLeadingInt(string input)
    {
       return Int32.Parse(new string(input.Trim().TakeWhile(c => char.IsDigit(c) || c == '.').ToArray()));
    }
    

    这适用于您提供的所有示例:

    string[] tests = new string[] {
       "1",
       " 42 ",
       " 3 -.X.-",
       " 2 3 4 5"
    };
    
    foreach (string test in tests)
    {
       Console.WriteLine("Result: " + GetLeadingInt(test));
    }
    

    【讨论】:

    • 你为什么要调用 ToCharArray?字符串已经实现了IEnumerable&lt;char&gt;
    • 我喜欢它!感谢您的出色解决方案。
    • 不错的解决方案。一个问题... || c == '.' 真的需要吗?这些示例只显示整数结果。如果删除它会加快一点,如果有很多提取,这可能很重要。
    • 这非常低效,为一个可以用零完成的操作创建至少四个中间对象。
    【解决方案8】:

    这就是我在 Java 中的做法:

    int parseLeadingInt(String input)
    {
        NumberFormat fmt = NumberFormat.getIntegerInstance();
        fmt.setGroupingUsed(false);
        return fmt.parse(input, new ParsePosition(0)).intValue();
    }
    

    我希望在 .NET 中也能实现类似的功能。

    这是我目前使用的基于正则表达式的解决方案:

    int? parseLeadingInt(string input)
    {
        int result = 0;
        Match match = Regex.Match(input, "^[ \t]*\\d+");
        if (match.Success && int.TryParse(match.Value, out result))
        {
            return result;
        }
        return null;
    }
    

    【讨论】:

      【解决方案9】:

      我喜欢@Donut 的做法。

      我想补充一点,char.IsDigitchar.IsNumber 还允许一些 Unicode 字符,这些字符是其他语言和脚本中的数字 (see here)。
      如果您只想检查数字 0 到 9,您可以使用 "0123456789".Contains(c)

      三个示例实现:

      要删除尾随的非数字字符:

      var digits = new string(input.Trim().TakeWhile(c =>
          ("0123456789").Contains(c)
      ).ToArray());
      

      删除前导非数字字符:

      var digits = new string(input.Trim().SkipWhile(c =>
          !("0123456789").Contains(c)
      ).ToArray());
      

      删除所有非数字字符:

      var digits = new string(input.Trim().Where(c =>
          ("0123456789").Contains(c)
      ).ToArray());
      

      当然还有:int.Parse(digits)int.TryParse(digits, out output)

      【讨论】:

      • 恕我直言,将("0123456789").Contains(c) 替换为c &gt;= '0' &amp;&amp; c &lt;= '9' 效率更高
      【解决方案10】:

      这并不能真正回答您的问题(关于内置 C# 方法),但您可以尝试将输入字符串末尾的字符一个接一个地切掉,直到 int.TryParse() 接受它作为有效数字:

      for (int p = input.Length;  p > 0;  p--)
      {
          int  num;
          if (int.TryParse(input.Substring(0, p), out num))
              return num;
      }
      throw new Exception("Malformed integer: " + input);
      

      当然,如果input 很长,这会很慢。

      附录 (2016 年 3 月)

      这可以通过在尝试每次解析之前切断右侧的所有非数字/非空格字符来加快速度:

      for (int p = input.Length;  p > 0;  p--)
      {
          char  ch;
          do
          {
              ch = input[--p];
          } while ((ch < '0'  ||  ch > '9')  &&  ch != ' '  &&  p > 0);
          p++;
      
          int  num;
          if (int.TryParse(input.Substring(0, p), out num))
              return num;
      }
      throw new Exception("Malformed integer: " + input);
      

      【讨论】:

        猜你喜欢
        • 2010-09-21
        • 2023-04-08
        • 2013-01-10
        • 1970-01-01
        • 2022-01-15
        • 2021-12-04
        • 2020-04-08
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多