验证字符串是否为十六进制

41

我有一个类似于"09a"的字符串,我需要一个方法来确认这个文本是否是十六进制。我发布的代码执行了类似的操作,它验证了一个字符串是否是十进制数。我想做相同的事情,但是对于十六进制。

    private static boolean isNumeric(String cadena) {
    try {
        Long.parseLong(cadena);
        return true;
    } catch (NumberFormatException nfe) {
        JOptionPane.showMessageDialog(null,"Uno de los números, excede su capacidad.");
        return false;
    }
}
8个回答

38

异常处理的恶劣滥用。绝不要这样做!(这不是我说的,而是Josh Bloch的《Effective Java》)。无论如何,我建议

private static final Pattern HEXADECIMAL_PATTERN = Pattern.compile("\\p{XDigit}+");

private boolean isHexadecimal(String input) {
    final Matcher matcher = HEXADECIMAL_PATTERN.matcher(input);
    return matcher.matches();
}

2
你误引了《Effective Java》的内容,应该避免使用异常来控制逻辑流程。如果提供错误的十六进制字符串确实是一个例外情况,那么使用parseLong并捕获异常是没有问题的。Josh的书中举了一个例子,其中异常将始终被抛出以控制循环,但这并不是真正的情况。此外,Josh的担忧也与效率和可读性有关,而你提出了REGEX,这既更慢,也不够易读。 - samthebest
6
根据我的基准测试,使用你的正则表达式比使用parseLong要慢一些,假设绝大部分字符串都是正确的。我同意使用异常可能不完美,并且存在更好的解决方案,但我主要反对你的回答,因为你建议的方法比使用异常还要糟糕。 - samthebest
这将在每次检查时编译正则表达式,通常最好使用Pattern。 - Czyzby
1
@Czyzby 提出的编译问题已经在后续编辑中得到解决。祝使用愉快! - Mark Stewart
1
@JurabekAzizkhujaev 我同意这个观点,但是完全禁止Java和C++可能会更好;)。我相信Go语言可能有非常好的库来处理其他方式的异常。在函数式编程中,我们使用验证Monad,这真是太棒了。 - samthebest
显示剩余2条评论

36

在Java中,有一个重载的Long.parseLong方法,它接受第二个参数来指定进制数:

Long.parseLong(cadena,16);

作为替代方案,您可以对字符串中的字符进行迭代,并在其上调用Character.digit(c,16)(如果其中任何一个返回-1则不是有效的十六进制数字)。如果字符串太大而无法适合long中(正如评论中指出的那样,使用第一种方法将导致异常),这种方法尤其有用。例如:

private static boolean isNumeric(String cadena) {
    if ( cadena.length() == 0 || 
         (cadena.charAt(0) != '-' && Character.digit(cadena.charAt(0), 16) == -1))
        return false;
    if ( cadena.length() == 1 && cadena.charAt(0) == '-' )
        return false;

    for ( int i = 1 ; i < cadena.length() ; i++ )
        if ( Character.digit(cadena.charAt(i), 16) == -1 )
            return false;
    return true;
}

顺便说一句,我建议将“测试一个有效数字”和“向用户显示消息”的问题分开处理,这就是为什么在上面的示例中我只是简单地返回了false,而没有先通知用户。

最后,您可以简单地使用正则表达式:

cadena.matches("-?[0-9a-fA-F]+");

作为替代方案,您可以迭代字符串中的字符并对它们调用Character.forDigit(c,16)(如果其中任何一个返回null - '\0' - 它不是有效的十六进制数字)。或者使用正则表达式(虽然这可能更复杂,性能可能更差)。 - mgibsonbr
1
你指定的 Character.forDigit(..) 方法是无效的。Character.forDigit(..) 接受一个整数并将其转换为字符,而不是相反的过程。 - Kevin Wheeler
1
您IP地址为143.198.54.68,由于运营成本限制,当前对于免费用户的使用频率限制为每个IP每72小时10次对话,如需解除限制,请点击左下角设置图标按钮(手机用户先点击左上角菜单按钮)。 - Czyzby
1
顺便提一下,你的isNumeric方法对于数字前带有“-”的字符串返回false,这可能是有效的负数。在迭代之前,我会检查第一个字符是否为“-”,如果是,则迭代应该从第二个索引开始。如果字符串中只有“-”,它也应该返回false。 - Czyzby
1
注意:Long.parseLong("f7e511c46d1b8a03", 16) 不起作用,会触发 NumberFormatException(无效的 long)。因此,迭代或正则表达式可能是更好的选择,适用于更大的十六进制数。 - Junior Mayhé
显示剩余2条评论

19

我在自己的代码中使用这个来检查字符串是否为MAC地址

boolean isHex = mac_addr.matches("^[0-9a-fA-F]+$");

我对该线程中提供的其他答案有异议,因为如果字符串的长度很大,它也会抛出异常。因此,如果您要测试MAC地址是否由有效的十六进制组成,它并不是非常有用。

不要害怕使用正则表达式!


3
每次检查时都会编译正则表达式,通常最好使用Pattern。 - Czyzby

13

这里是不同选项的代码和执行时间结果(使用JDK 11):

execution time isHex1: 3670 ms
execution time isHex2: 3294 ms
execution time isHex3: 3946 ms
execution time regex: 31288 ms

测试代码:

public class HexPerformanceTest {
    @Test
    public void testPerformance() {
        int count = 100000000;
        char[] chars = {
            '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f',
            '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'
        };
        String regexString = new String(chars);

        Predicate<String> isHex1Test = testString -> {
            boolean isHex = true;
            for (char c: testString.toCharArray()) {
                if (!isHex1(c)) {
                    isHex = false;
                    break;
                }
            }
            return isHex;
        };

        Predicate<String> isHex2Test = testString -> {
            boolean isHex = true;
            for (char c: testString.toCharArray()) {
                if (!isHex2(c)) {
                    isHex = false;
                    break;
                }
            }
            return isHex;
        };

        Predicate<String> isHex3Test = testString -> {
            boolean isHex = true;
            for (char c: testString.toCharArray()) {
                if (!isHex3(c)) {
                    isHex = false;
                    break;
                }
            }
            return isHex;
        };

        Pattern pattern = Pattern.compile("^[0-9a-fA-F]+$");
        Predicate<String> regexTest = testString -> {
            Matcher matcher = pattern.matcher(regexString);
            return matcher.matches();
        };

        System.out.println("execution time isHex1: " + milliseconds(regexString, isHex1Test, count) + " ms");
        System.out.println("execution time isHex2: " + milliseconds(regexString, isHex2Test, count) + " ms");
        System.out.println("execution time isHex3: " + milliseconds(regexString, isHex3Test, count) + " ms");
        System.out.println("execution time regex: " + milliseconds(regexString, regexTest, count) + " ms");
    }

    private long milliseconds(String testString, Predicate<String> hexTest, int count) {
        long start = System.currentTimeMillis();
        for (int i = 0; i < count; i++) {
            hexTest.test(testString);
        }
        return System.currentTimeMillis() - start;
    }

    private boolean isHex1(char c) {
        return (c >= '0' && c <= '9') || (c >= 'a' && c <= 'f') || (c >= 'A' && c <= 'F');
    }

    private boolean isHex2(char c) {
        switch (c) {
            case '0':
            case '1':
            case '2':
            case '3':
            case '4':
            case '5':
            case '6':
            case '7':
            case '8':
            case '9':
            case 'a':
            case 'b':
            case 'c':
            case 'd':
            case 'e':
            case 'f':
            case 'A':
            case 'B':
            case 'C':
            case 'D':
            case 'E':
            case 'F':
                return true;
            default:
                return false;
        }
    }

    private boolean isHex3(char c) {
        return (Character.digit(c, 16) != -1);
    }
}

10

Long.parseLong有另外一种形式,它的第二个参数是代表进制的数值。

private static boolean isHexNumber (String cadena) {
  try {
    Long.parseLong(cadena, 16);
    return true;
  }
  catch (NumberFormatException ex) {
    // Error handling code...
    return false;
  }
}

如果我调用isHexNumber(“1234567890abcdef1234567890abcdef”)会发生什么?即使它是十六进制数,但过于长而无法转换为“long”,因此可能会出现其他异常。 - Gavriel

2
无库方法
public static boolean isHexadecimal(String value)
{
    if (value.startsWith("-"))
    {
        value = value.substring(1);
    }

    value = value.toLowerCase();

    if (value.length() <= 2 || !value.startsWith("0x"))
    {
        return false;
    }

    for (int i = 2; i < value.length(); i++)
    {
        char c = value.charAt(i);

        if (!(c >= '0' && c <= '9' || c >= 'a' && c <= 'f'))
        {
            return false;
        }
    }

    return true;
}

1

试一下这个。

static boolean isHexadecimal(String s) {
    return s.chars()
        .skip(s.startsWith("-") ? 1 : 0)
        .allMatch(c -> "0123456789ABCDEFabcdef".indexOf(c) >= 0);
}

public static void main(String[] args) {
    System.out.println(isHexadecimal("-0e34a29Fb"));
    System.out.println(isHexadecimal("-ff-"));
    System.out.println(isHexadecimal("ef-"));
    System.out.println(isHexadecimal("efg"));
}

输出:

true
false
false
false

如果不允许符号,您可以省略.skip(s.startsWith("-") ? 1 : 0)

0

你可以使用以下方法实际检查任何长度的文本。

public static boolean isHexadecimal(String text) {
    Objects.requireNonNull(text);
    if(text.length() < 1)
        throw new IllegalArgumentException("Text cannot be empty.");

    char[] hexDigits = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
            'a', 'b', 'c', 'd', 'e', 'f', 'A', 'B', 'C', 'D', 'E', 'F' };

    for (char symbol : text.toCharArray()) {
        boolean found = false;
        for (char hexDigit : hexDigits) {
            if (symbol == hexDigit) {
                found = true;
                break;
            }
        }
        if(!found)
            return false;
    }
    return true;
}

1
如果您想检查输入是否为十六进制格式,不应该在开头修剪它。此外,如果您只在第一个非十六进制字符出现时返回false,则循环可能会更快地失败(可能有一些检查包含的方法,因此您可以执行:if(!hexDigits.Contains(symbol))return false;)。 - Oliver
这不是检查字符串是否为十六进制的非常有效的方法,而且返回语句中的三元运算符是多余的。 - Czyzby

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