检查一个string是否可以parsing为Long而没有try-catch?
Long.parseLong("string")
如果string不能parsing为long,则会抛出一个错误。 有没有比使用try-catch
更快地validationstring的方法? 谢谢
你可以创build相当复杂的正则expression式,但这不值得。 在这里使用例外是绝对正常的。
这是天生的例外情况:你假定string中有一个整数,但确实有其他的东西。 应该抛出exception并妥善处理。
如果您查看parseLong
代码,您会看到有许多不同的validation和操作。 如果你想在parsing之前做所有的事情,会降低性能(如果我们正在谈论parsing数以百万计的数字,否则没关系)。 因此, 如果真的需要通过避免exception来提高性能,唯一可以做的事情是:将parseLong
实现复制到您自己的函数中,并返回NaN,而不是在所有对应的情况下抛出exception。
来自commons-lang的StringUtils:
public static boolean isNumeric(String str) { if (str == null) { return false; } int sz = str.length(); for (int i = 0; i < sz; i++) { if (Character.isDigit(str.charAt(i)) == false) { return false; } } return true; }
你可以做类似的事情
if(s.matches("\\d*")){ }
使用正则expression式 – 检查string是否充满数字。 但你有什么收获? 另一个如果条件?
这是一个有效的问题,因为有时候你需要推断在一个string中表示了什么types的数据。 例如,您可能需要将大型CSV导入到数据库中,并准确表示数据types。 在这种情况下,调用Long.parseLong并捕获exception可能会太慢。
以下代码仅处理ASCII十进制:
public class LongParser { // Since tryParseLong represents the value as negative during processing, we // counter-intuitively want to keep the sign if the result is negative and // negate it if it is positive. private static final int MULTIPLIER_FOR_NEGATIVE_RESULT = 1; private static final int MULTIPLIER_FOR_POSITIVE_RESULT = -1; private static final int FIRST_CHARACTER_POSITION = 0; private static final int SECOND_CHARACTER_POSITION = 1; private static final char NEGATIVE_SIGN_CHARACTER = '-'; private static final char POSITIVE_SIGN_CHARACTER = '+'; private static final int DIGIT_MAX_VALUE = 9; private static final int DIGIT_MIN_VALUE = 0; private static final char ZERO_CHARACTER = '0'; private static final int RADIX = 10; /** * Parses a string representation of a long significantly faster than * <code>Long.ParseLong</code>, and avoids the noteworthy overhead of * throwing an exception on failure. Based on the parseInt code from * http://nadeausoftware.com/articles/2009/08/java_tip_how_parse_integers_quickly * * @param stringToParse * The string to try to parse as a <code>long</code>. * * @return the boxed <code>long</code> value if the string was a valid * representation of a long; otherwise <code>null</code>. */ public static Long tryParseLong(final String stringToParse) { if (stringToParse == null || stringToParse.isEmpty()) { return null; } final int inputStringLength = stringToParse.length(); long value = 0; /* * The absolute value of Long.MIN_VALUE is greater than the absolute * value of Long.MAX_VALUE, so during processing we'll use a negative * value, then we'll multiply it by signMultiplier before returning it. * This allows us to avoid a conditional add/subtract inside the loop. */ int signMultiplier = MULTIPLIER_FOR_POSITIVE_RESULT; // Get the first character. char firstCharacter = stringToParse.charAt(FIRST_CHARACTER_POSITION); if (firstCharacter == NEGATIVE_SIGN_CHARACTER) { // The first character is a negative sign. if (inputStringLength == 1) { // There are no digits. // The string is not a valid representation of a long value. return null; } signMultiplier = MULTIPLIER_FOR_NEGATIVE_RESULT; } else if (firstCharacter == POSITIVE_SIGN_CHARACTER) { // The first character is a positive sign. if (inputStringLength == 1) { // There are no digits. // The string is not a valid representation of a long value. return null; } } else { // Store the (negative) digit (although we aren't sure yet if it's // actually a digit). value = -(firstCharacter - ZERO_CHARACTER); if (value > DIGIT_MIN_VALUE || value < -DIGIT_MAX_VALUE) { // The first character is not a digit (or a negative sign). // The string is not a valid representation of a long value. return null; } } // Establish the "maximum" value (actually minimum since we're working // with negatives). final long rangeLimit = (signMultiplier == MULTIPLIER_FOR_POSITIVE_RESULT) ? -Long.MAX_VALUE : Long.MIN_VALUE; // Capture the maximum value that we can multiply by the radix without // overflowing. final long maxLongNegatedPriorToMultiplyingByRadix = rangeLimit / RADIX; for (int currentCharacterPosition = SECOND_CHARACTER_POSITION; currentCharacterPosition < inputStringLength; currentCharacterPosition++) { // Get the current digit (although we aren't sure yet if it's // actually a digit). long digit = stringToParse.charAt(currentCharacterPosition) - ZERO_CHARACTER; if (digit < DIGIT_MIN_VALUE || digit > DIGIT_MAX_VALUE) { // The current character is not a digit. // The string is not a valid representation of a long value. return null; } if (value < maxLongNegatedPriorToMultiplyingByRadix) { // The value will be out of range if we multiply by the radix. // The string is not a valid representation of a long value. return null; } // Multiply by the radix to slide all the previously parsed digits. value *= RADIX; if (value < (rangeLimit + digit)) { // The value would be out of range if we "added" the current // digit. return null; } // "Add" the digit to the value. value -= digit; } // Return the value (adjusting the sign if needed). return value * signMultiplier; } }
你可以使用java.util.Scanner
Scanner sc = new Scanner(s); if (sc.hasNextLong()) { long num = sc.nextLong(); }
这也是范围检查等等。 当然,它会说"99 bottles of beer"
hasNextLong()
,所以如果你想确保它只有很long
你必须做额外的检查。
这种情况对于有input字段的表单和程序很常见,并且不确定string是否是有效的数字。 因此,如果您了解try / catch如何与您自己编写函数相比,那么使用try / catch和java函数是最好的办法。 为了在.NET虚拟机中设置try catch块,没有开销的指令,在Java中可能是一样的。 如果在try关键字中使用了指令,那么这些指令将是最小的,并且大部分指令将在catch部分使用,并且仅在less数情况下在数字无效的情况下使用。
所以虽然看起来好像你可以自己编写一个更快的函数,但为了击败你已经使用的try / catch机制,你必须比Java编译器更好地优化它,而更优化函数的好处是由于数字parsing是相当通用的,因此非常小。
如果你使用你已经描述的编译器和java catch机制来运行时序testing,那么你可能不会注意到任何高于边缘的减速,而我认为它应该几乎没有任何变化。
获取Java语言规范以更多地理解exception,您将会看到在这种情况下使用这种技术是完全可以接受的,因为它包装了一个相当大且复杂的函数。 在CPU中添加这些less量的额外指令对于尝试部分来说不是什么大问题。
我认为这是检查一个string是一个有效的长整型值的唯一方法。 但你可以实现自己的方法来做到这一点,铭记最大的长期价值。
有比parsing Long.parseLong长得多的方法 。 如果你想看到一个没有优化的方法的例子,那么你应该看看parseLong 🙂
你真的需要考虑非ASCII码的“数字”吗?
你真的需要做几个方法调用传递一个基数,即使很难,你可能parsing基地10?
🙂
使用正则expression式不是一种好的方法:确定数字是否太长久难以确定:如何使用正则expression式来确定9223372036854775807可以被parsing为很长,但是9223372036854775907不能?
也就是说,一个真正的快速parsing方法的答案是一个状态机,无论你想testing它是可parsing的还是parsing它。 简单地说,它不是一个接受复杂正则expression式的通用状态机,而是一个硬编码的状态机。
我可以写一个方法来分析一个很长的,另一个决定一个long是否可以被parsing,完全胜过Long.parseLong() 。
现在你想要什么? 状态testing方法? 在这种情况下,如果你想避免两倍的计算,状态testing方法可能是不可取的。
只需在try / catch中包装你的电话。
如果你真的想要的东西比默认的Long.parseLong更快,那么写一个适合你的问题的方法:如果你的基数是10,那么写10,不检查ASCII以外的数字(因为你可能对日文的itchi-ni不感兴趣 – 永远去等)。
希望这有助于积极的价值观。 我曾经使用过这个方法来validation数据库主键。
private static final int MAX_LONG_STR_LEN = Long.toString(Long.MAX_VALUE).length(); public static boolean validId(final CharSequence id) { //avoid null if (id == null) { return false; } int len = id.length(); //avoid empty or oversize if (len < 1 || len > MAX_LONG_STR_LEN) { return false; } long result = 0; // ASCII '0' at position 48 int digit = id.charAt(0) - 48; //first char cannot be '0' in my "id" case if (digit < 1 || digit > 9) { return false; } else { result += digit; } //start from 1, we already did the 0. for (int i = 1; i < len; i++) { // ASCII '0' at position 48 digit = id.charAt(i) - 48; //only numbers if (digit < 0 || digit > 9) { return false; } result *= 10; result += digit; //if we hit 0x7fffffffffffffff // we are at 0x8000000000000000 + digit - 1 // so negative if (result < 0) { //overflow return false; } } return true; }
尝试使用这个正则expression式:
^(-9223372036854775808|0)$|^((-?)((?!0)\d{1,18}|[1-8]\d{18}|9[0-1]\d{17}|92[0-1]\d{16}|922[0-2]\d{15}|9223[0-2]\d{14}|92233[0-6]\d{13}|922337[0-1]\d{12}|92233720[0-2]\d{10}|922337203[0-5]\d{9}|9223372036[0-7]\d{8}|92233720368[0-4]\d{7}|922337203685[0-3]\d{6}|9223372036854[0-6]\d{5}|92233720368547[0-6]\d{4}|922337203685477[0-4]\d{3}|9223372036854775[0-7]\d{2}|922337203685477580[0-7]))$
它会检查所有可能的数字。 但是正如你在Java中所知,Long可以包含像+
, L
, _
等附加符号,而这个正则expression式不会validation这些值。 但是,如果这个正则expression式对你来说不够,你可以为它添加额外的限制。
org.apache.commons.lang3.math.NumberUtils.isParsable(yourString)将确定该string是否可以通过以下之一进行分析:Integer.parseInt(String),Long.parseLong(String),Float.parseFloat(String)或Double .parseDouble(string)
既然你对Longs感兴趣,你可能会有一个检查isParsable并且不包含小数的条件
if (NumberUtils.isParsable(yourString) && !StringUtils.contains(yourString,".")){ ...
在尝试parsing它之前,你可以尝试使用正则expression式来检查string的forms吗?