判断字符串转换为double/float/int/short/byte是否超出范围。

8

我有以下内容:

string outOfRange = "2147483648"; // +1 over int.MaxValue

显然,如果输入的不是数字,这将失败:
var defaultValue = 0;
int.TryParse(outOfRange, out defaultValue);

我的问题是,既然这是一个数字,并且在使用int.TryParse()时将失败,那么你如何判断它失败是因为字符串超出了存储容器的范围?


1
首先将其解析为更大的容量值(例如long或者uint,如果你知道只有正数),然后再检查是否符合Int32.Max/MinValue是可行的吗? - Chris Sinclair
为什么不总是使用最大容量,如果可能你的值会溢出较小的容量?你能补充一下上下文吗? - Pierre-Luc Pineault
1
你可以使用 Try-Catch 并检查异常消息来获取一个想法,而不是使用 int.TryParse - Yoav
1
有什么用途呢?如果只是为了验证输入,那么可能并不必要,因为你可以在消息中涵盖它,而不需要复杂的多个不同消息。如果是针对无法控制的情况,那么最好使用Parse方法来允许异常发生,也许?这只是我的好奇心和观察。 - Grant Thomas
1
我同意@GrantThomas的观点,从用户的角度来看,为什么他们需要知道int在+- 20亿时会溢出呢?使用情况应该决定您期望的实际输入范围,并根据该范围编写验证,您还应选择适当的数据类型。 - Matthew
显示剩余4条评论
8个回答

4
我会为这种情况选择“Try/Catch”解决方案。
        string outOfRange = "2147483648";
        try
        {
            int.Parse(outOfRange);
        }
        catch (OverflowException oex)
        {

        }
        catch (Exception ex)
        { }

我知道这里的大多数人都会建议避免使用它,但有时我们只是不得不使用它(或者我们没有必要使用它,但这会节省我们很多时间)。
这里有一篇关于Try/Catch效率的小文章。

3

可以解析为十进制数,然后检查范围,避免使用 try/catch

string s = "2147483648";
decimal.Parse(s) > int.MaxValue;

2
string outOfRange = "2147483648"; // +1 over int.MaxValue
int value;
if(! int.TryParse(outOfRange, out value)) {
    try {
        int.Parse(defaultValue);
    } catch(OverflowException e) {
        // was overflow
    } catch(Exception e) {
        // was other reason
    }
}

假设有少数情况下数字过大,那么抛出和捕获异常的开销是可以容忍的,因为正常情况下使用更快的 TryParse 方法来处理而不涉及异常。

对于其他数字数据类型(如浮点数),也可以采用类似的方法。


不要使用异常来控制流程。 - It'sNotALie.
你可以使用 Int64.TryParse 代替;如果它通过了,那么你就知道它超出了范围。如果失败了,那么你就知道输入有误。 - Chris Sinclair
@ChrisSinclair 但是原始问题想知道长整型、双精度等的解决方案。 - FrankPl
@It'sNotALie。在标准情况下,对于有效数字,通常不会出现异常情况。我认为溢出的情况至少在某种程度上是异常的。如果您想要将其用于原始帖子所请求的双精度浮点数,则首先检查所有有效字符串的解决方案并不简单。因此,我认为在这里使用异常是不使用异常进行控制流的例外。 - FrankPl

2

我会尝试解析,如果失败了,那么尝试解析更高容量的值。如果更高容量的值可以成功解析,那么你就知道它超出范围了。如果它同样无法解析,那么输入就是错误的。

string outOfRange = "2147483648"; // +1 over int.MaxValue
int result;
if (!Int32.TryParse(outOfRange, out result))
{
    long rangeChecker;
    if (Int64.TryParse(outOfRange, out rangeChecker))
        //out of range
    else
        //bad format
}

很遗憾,我认为没有一种通用的方法可以适用于任何类型;您必须为所有类型编写实现。例如,对于 Int64,该怎么办?也许可以使用 BigInteger 替代:

string outOfRange = "9223372036854775808"; // +1 over Int64.MaxValue
long result;
if (!Int64.TryParse(outOfRange, out result))
{
    BigInteger rangeChecker;
    if (BigInteger.TryParse(outOfRange, out rangeChecker))
        //out of range
    else
        //bad format
}

编辑:由于我所知道的没有“BigDecimal”,因此使用double浮点值可能更有趣,您可能还需要考虑极端情况下接近0的值(不确定)。可能您可以对BigInteger检查进行变化,但您还必须考虑小数点(在这里可能最好使用简单的正则表达式只有数字、可选负号和最多一个小数点)。如果有任何小数点,您必须将它们截断并仅检查字符串的整数部分。

编辑x2:这里是一个相当丑陋的实现,用于检查double值:

// +bajillion over Double.MaxValue
string outOfRange = "90000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000.1";
double result;
if (!Double.TryParse(outOfRange, out result))
{
    string bigIntegerInput = outOfRange;

    if (!Regex.IsMatch(bigIntegerInput, @"^-?[0-9]\d*(\.\d+)?$"))
        //bad format

    int decimalIndex = bigIntegerInput.IndexOf('.');
    if (decimalIndex > -1)
        bigIntegerInput = bigIntegerInput.Substring(0, decimalIndex);

    BigInteger rangeChecker;
    if (BigInteger.TryParse(bigIntegerInput, out rangeChecker))
        //out of range
    else
        //bad format
}

说实话,在这个阶段,我认为我们已经走向了深渊。除非你有一些真正的性能瓶颈,或者你的应用程序输入了超出范围的值,而这种情况发生得频繁,否则你最好只是像这个回答中所述的那样随机捕捉它们,或者更简单地将正则表达式应用于输入。在我的最后一个例子中,我可能在做完正则表达式后就放弃了(但我不知道TryParse实现是否更宽容,允许指数/科学表示法。如果是这样,正则表达式也必须覆盖这些)

2
您可以尝试使用BigInteger进行解析。
BigInteger bigInt;
bool isAnOutOfRangeInt = BigInteger.TryParse(input, out bigInt)
                         && (bigInt > int.MaxValue || bigInt < int.MinValue);
// if you care to have the value as an int:
if (!isAnOutOfRangeInt)
{
    int intValue = (int)bigInt;
}

那么,如何按照原始帖子的要求将其用于“double”而不是“int”? - FrankPl

1

我建议使用 System.Convert.ToInt32(String) 进行数据类型转换;因为它已经为你实现了 OverflowException 异常处理,非常方便。

这种方法十分简单,比如:

 try 
 {
      result = Convert.ToInt32(value);
      Console.WriteLine("Converted the {0} value '{1}' to the {2} value {3}.",
                    value.GetType().Name, value, result.GetType().Name, result);
 }
 catch (OverflowException) 
 {
      Console.WriteLine("{0} is outside the range of the Int32 type.", value);
 }   
 catch (FormatException) 
 {
      Console.WriteLine("The {0} value '{1}' is not in a recognizable format.",
                    value.GetType().Name, value);
 }   

并且这个逻辑已经是标准系统库的一部分。

那并没有涉及到double/float。 - hatchet - done with SOverflow
Convert.ToDouble(String)和Convert.ToFloat(String)肯定会实现。它们已经被MSDN实现了。 - Az Za
我曾将你的“作为转换事物的机制”解释为指上面的代码也可以用于转换双精度和浮点数。如果你的代码只是部分解决问题的示例,而其他类似的代码需要用于双精度/浮点数等情况,那么我的评论就不适用了。 - hatchet - done with SOverflow

1

为什么在控制流程时要使用异常,而不是直接使用“TryParse”的返回值呢? - Mike Precup
1
TryParse 返回一个 bool,这与从 Parse 中获取许多不同的异常是不可比较的。 - meilke

0

直接的方法是使用 Int32.Parse(string s) 并捕获 OverflowException

OverflowException
s 表示一个小于 MinValue 或大于 MaxValue 的数字。


1
@It'sNotALie。一般情况下,我会避免在这种操作中使用它们,但在这种情况下,编写替代方案并不是很直接。如果涉及到 IFormatProvider,情况就更加复杂了。 - Joachim Isaksson

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接