【问题标题】:Problem with String Splitting in Java (MAC ADRESSES)Java 中的字符串拆分问题(MAC 地址)
【发布时间】:2019-11-22 19:30:08
【问题描述】:

我有这台服务器,它记录了对 MAC 地址的观察。当客户端想要查看给定 MAC 地址的观察结果时,他可以使用完整 MAC 或分段 MAC 发出该请求。有效请求可以是由 8 位倍数组成的 MAC 地址的开头或结尾,示例如下:

  • FF: - MAC 地址的前 8 位
  • FF:AA: - MAC 地址的前 16 位
  • :FF - MAC 地址的最后 8 位
  • :AA:FF - MAC 地址的最后 16 位
  • :AA:BB:CC:DD:FF - 没有前 8 位的 MAC 地址的结尾。
  • AA:BB:CC:DD:EE:FF - 完整的 MAC 地址

这些示例不允许

  • FF - 必须以“:”结束以让服务器知道它是一个初始片段
  • FF:AA - 必须以“:”结束以让服务器知道它是一个初始片段
  • :FF: - 不允许中间片段
  • OO:JJ - 不允许使用非十六进制字符。

这是我的代码:

    private static boolean isMAC(String mac) {
        String[] fragMACs = mac.split(":");

        if (fragMACs.length > 0 && fragMACs.length <= 6) {
            for (String s : fragMACs) {
                if (s.length() != 2) {
                    return false;
                } else {
                    try {
                        Long.parseLong(s, 16);
                    } catch (NumberFormatException nfe) {
                        return false;
                    }
                }
            }
            return true;
        }
        return false;
    }

目前我的功能不适用于以下情况:

  • 任何以“:”开头的片段,分割功能都会搞砸。
  • 它不包含用于确保它不是中间 mac 片段的验证。 (AA, AA:AA, :AA:)

最后的验证很简单,一个简单的if语句如下:

if ((mac.charAt(0) == ':' && mac.charAt(fragMACs.length - 1) == ':') 
    || (mac.charAt(0) != ':' && mac.charAt(fragMACs.length - 1) != ':')) { 
    return false; 
}

应该可以解决问题。

我的问题在于那个拆分子句。有没有关于如何有效解决这个问题的建议?

【问题讨论】:

  • 你在模式匹配方面遇到了问题 - 对我来说,这叫 REGEX。
  • 是的,但只有当输入字符串以':'开头时才会失败,我该如何解决?

标签: java regex string split


【解决方案1】:

这可以通过一个正则表达式来完成,该表达式在很大程度上依赖于正负前瞻来进行大部分验证。正则表达式是:

^(?=(:|.*:$|.{17}$))(?=.{3,17}$)(?!.{4}$):?[0-9A-F]{2}(:[0-9A-F]{2})*:?$

主要部分是:

:?[0-9A-F]{2}(:[0-9A-F]{2})*:?
  1. :? 可选冒号
  2. [0-9A-F]{2} 后跟集合中的 2 个字符 [0-9A-F]
  3. (:[0-9A-F]{2})* 后跟 0 次或多次出现的冒号,后跟 2 个字符集 [0-9A-F]
  4. :? 可选地后跟冒号

真正的执行发生在匹配开始时,在使用前瞻扫描任何字符之前:

  1. (:|.*:$|.{17}$)) 字符串必须以冒号开头、以冒号结尾或正好为 17 个字符
  2. (?=.{3,17}$) 整个字符串的长度必须为 3 到 17 个字符(这会消除,例如,FF
  3. (?!.{4}$) 但是字符串不能是 4 个字符长(这消除了:FF:

代码:

import java.util.regex.Pattern;
import java.util.regex.Matcher;

public class Test
{
    public static void main(String[] args) {
        String[] tests = {
            "FF:",
            "FF:AA:",
            ":FF",
            ":AA:FF",
            ":AA:BB:CC:DD:FF",
            "AA:BB:CC:DD:EE:FF",
            "FF",
            "FF:AA",
            ":FF:",
            ":OO:JJ",
            ":AA:BB:CC:DD:EE:FF",
        };
        Pattern pattern = Pattern.compile("^(?=(:|.*:$|.{17}$))(?=.{3,17}$)(?!.{4}$):?[0-9A-F]{2}(:[0-9A-F]{2})*:?$");
        for (String test : tests) {
            Matcher matcher = pattern.matcher(test);
            System.out.println(test + " " + (matcher.matches() ? "match" : "no match"));
        }
    }
}

打印:

FF: match
FF:AA: match
:FF match
:AA:FF match
:AA:BB:CC:DD:FF match
AA:BB:CC:DD:EE:FF match
FF no match
FF:AA no match
:FF: no match
:OO:JJ no match
:AA:BB:CC:DD:EE:FF no match

【讨论】:

  • 尽管我个人避免使用这么长的正则表达式,但它非常紧凑并且解释得很好。您可能应该将 [A-F] 更改为 [0-9A-F],因为 OP 提到了 MAC 地址。
  • @Eritrean 你是绝对正确的,我已经更新了我的答案。谢谢。
  • 这非常有用,非常感谢。我想我会更多地研究一下正则表达式,这可以节省我大量的编码行......
【解决方案2】:

使用单词边界\b 进行分割可以使这项任务更容易,因为它不会消耗任何输入文本,让您保留分隔符:。例如以下语句输出:

System.out.println(Arrays.toString("AA:BB:CC:DD:EE:FF".split("\\b")));
// [AA, :, BB, :, CC, :, DD, :, EE, :, FF]

我不会在 try-catch 中解析十六进制字符串,而是使用简单的正则表达式

[0-9A-F]{2}

获取来自0 to 9 的数字和字母A to F 的任意组合,精确长度为2

将以上内容加在一起,您的代码可能如下所示:

private static boolean isMac(String mac) {        
    String[] parts = mac.split("\\b");
    //Full MAC Address
    if (parts.length == 11){
        return Pattern.compile(":").splitAsStream(mac)
                      .allMatch(s -> s.matches("[0-9A-F]{2}"));
    }
    //input shouldn't start and end with ':'
    else if(mac.startsWith(":") && mac.endsWith(":")){
        return false;
    }
    //input should start or end with ':'
    else if( !(parts[0].equals(":") || parts[parts.length-1].equals(":")) ){
        return false;
    }
    //rest
    return Pattern.compile(":").splitAsStream(mac)
                      .filter(s -> !s.isEmpty())
                      .allMatch(s -> s.matches("[0-9A-F]{2}"));
}

【讨论】:

  • 感谢您的回答:D
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2023-04-03
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多