如果字符串无法解析为long,则Long.parseLong("string")
会抛出错误。有没有办法比使用try-catch
更快地验证字符串?谢谢
您可以创建相当复杂的正则表达式,但这不值得。在这里使用例外是绝对正常的。
这是很自然的特殊情况:你假设字符串中有一个整数,但确实还有其他东西。应抛出异常并妥善处理。
如果你查看parseLong
代码,你会发现有许多不同的验证和操作。如果你想在解析它之前做所有这些事情会降低性能(如果我们谈论解析数百万个数字,因为否则无关紧要)。因此,如果您真的需要通过避免异常来提高性能,那么您唯一可以做的就是:将parseLong
实现复制到您自己的函数并返回NaN而不是在所有通信情况下抛出异常。
希望这有助于积极的价值观。我使用此方法一次验证数据库主键。
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;
}
尝试使用此正则表达式:
^(-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]))$
它检查Long的所有可能数字。但正如你所知,Java Long可以包含额外的符号,如+
,L
,_
等。这个正则表达式不会验证这些值。但是,如果此正则表达式对您不够,则可以为其添加其他限制。
您可以尝试使用正则表达式来检查字符串的形式,然后再尝试解析它?
如果解析失败,Guava Longs.tryParse("string")将返回null而不是抛出异常。但这种方法现在是marked as Beta。
验证适合long的整数的简单实现是:
public static boolean isValidLong(String str) {
if( str==null ) return false;
int len = str.length();
if (str.charAt(0) == '+') {
return str.matches("\\+\\d+") && (len < 20 || len == 20 && str.compareTo("+9223372036854775807") <= 0);
} else if (str.charAt(0) == '-') {
return str.matches("-\\d+") && (len < 20 || len == 20 && str.compareTo("-9223372036854775808") <= 0);
} else {
return str.matches("\\d+") && (len < 19 || len == 19 && str.compareTo("9223372036854775807") <= 0);
}
}
它不处理八进制,0x前缀左右,但这很少是一个要求。
对于速度,“.match”表达式很容易在循环中编码。
来自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*")){
}
使用正则表达式 - 检查String s是否已满数字。但是你有什么收获的呢?另一个条件?
这是一个有效的问题,因为有时您需要推断字符串中表示的数据类型。例如,您可能需要将大型CSV导入数据库并准确表示数据类型。在这种情况下,调用Long.parseLong并捕获异常可能会太慢。
以下代码仅处理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
你必须做额外的检查。
org.apache.commons.lang3.math.NumberUtils.isParsable(yourString)将确定字符串是否可以解析为:Integer.parseInt(String),Long.parseLong(String),Float.parseFloat(String)或Double .parseDouble(字符串)
由于您对Longs感兴趣,因此您可以使用检查isParsable并且不包含小数的条件
if (NumberUtils.isParsable(yourString) && !StringUtils.contains(yourString,".")){ ...
这种情况对于您有输入字段且不确定字符串是否为有效数字的表单和程序是常见的。因此,如果您了解try / catch的工作方式与尝试自己编写函数相比,那么将try / catch与java函数一起使用是最好的办法。为了在.NET虚拟机中设置try catch块,没有任何开销指令,它在Java中可能是相同的。如果在try关键字上使用了指令,则这些指令将是最小的,并且大部分指令将在catch部分使用,并且仅在极少数情况下才会发生。
因此,虽然它“看起来”像你可以自己编写一个更快的函数,但你必须比Java编译器更好地优化它,以便击败你已经使用的try / catch机制,并且更优化的函数的好处是因为数字解析非常通用,所以非常小。
如果你用你的编译器和你已经描述的java catch机制运行时序测试,你可能不会注意到任何上面的边际减速,而边际我的意思是它应该几乎没有。
获取java语言规范以更多地理解异常,你会发现在这种情况下使用这种技术是完全可以接受的,因为它包含了一个相当大而复杂的函数。在CPU中为try部分添加那些额外的指令并不是什么大问题。
我认为这是检查String是否是有效long值的唯一方法。但是你可以自己实现一种方法来实现这一点,同时考虑到最大的长期价值。
解析long的速度比Long.parseLong快得多。如果你想看一个未优化的方法的例子,你应该看看parseLong :)
你真的需要考虑非ASCII的“数字”吗?
你真的需要做几个方法调用绕过基数,即使你可能解析基数10?
:)
使用正则表达式不是要走的路:很难确定你的数字是否太长了:你如何使用正则表达式来确定9223372036854775807可以被解析为长但是9223372036854775907不能?
也就是说,一个非常快速的长解析方法的答案是一个状态机,无论你是想测试它是否可解析或解析它。简单来说,它不是一个接受复杂正则表达式而是硬编码的通用状态机。
我可以写一个解析long的方法,另一个方法确定是否可以解析一个完全优于Long.parseLong()的long。
那你想要什么?状态测试方法?在这种情况下,如果您想避免计算两倍的长度,则可能不需要状态测试方法。
只需将您的电话包裹在try / catch中即可。
如果你真的想要比默认的Long.parseLong更快的东西,写一个针对你的问题定制的东西:如果你是10的基数10,不检查ASCII之外的数字(因为你可能对日语的itchi-ni不感兴趣) -yon-go等)。