URI 源码分析

使用 URI 来校验 url,如下代码:

import org.apache.commons.validator.routines.UrlValidator;

import java.net.URI;
import java.net.URISyntaxException;

public class UrlUtils {
    
    

    public static void main(String[] args) {
    
    
        String url = "http://www.jiaobuchong.com?name=tom&request={\"hobby\":\"film\"}";
        System.out.println(new UrlValidator().isValid(url));
        isValidUrl(url);
    }

    public static boolean isValidUrl(String url) {
    
    
        try {
    
    
            URI uri = new URI(url);
            return true;
        } catch (URISyntaxException e) {
    
    
            e.printStackTrace();
        }
        return false;
    }
}

如果 url 含有 {}""则会抛出 URISyntaxException,校验不通过。 看了一下 URI 里面的源码,有个校验还挺有意思的。

一、分析 checkChar 方法

1、通过 highMask 获取合法字符范围的二进制

private static final long L_ALPHA = L_LOWALPHA | L_UPALPHA;
private static final long L_LOWALPHA = 0L;
private static final long L_UPALPHA = 0L;

private static final long H_ALPHA = H_LOWALPHA | H_UPALPHA;
private static final long H_LOWALPHA = highMask('a', 'z');
private static final long H_UPALPHA = highMask('A', 'Z');
checkChar(0, L_ALPHA, H_ALPHA, "scheme name");

L_ALPHA 与的结果是 0,下面就要开始比较有意思的部分了,来看 highMask 到底做了什么:

    // Compute a high-order mask for the characters
    // between first and last, inclusive
    private static long highMask(char first, char last) {
    
    
        long m = 0;
        // Math.min 表示从 ASCII 的范围里取值
        // Math.max(Math.min(first, 127), 64) 表示在 ASCII 码表[64, 127]之间的字符
        // 减去 64 表示不包括 64 这个字符,相对 64 之间还有多少个个字符
        int f = Math.max(Math.min(first, 127), 64) - 64;
        int l = Math.max(Math.min(last, 127), 64) - 64;
        for (int i = f; i <= l; i++)
            m |= 1L << i;
        return m;
    }

highMask(‘a’, ‘z’ ) m 的二进制:
在这里插入图片描述
highMask(‘A’, ‘Z’ ) m 的二进制:
在这里插入图片描述
H_ALPHA = H_LOWALPHA | H_UPALPHA 相与的结果是:
11111111111111111111111111000000111111111111111111111111110,相与之后的结果 H_UPALPHA 表示 a-zA-Z 这个范围的字符,一个萝卜一个坑,不在这个范围的数据对应的坑位就是 0。

2、match 方法

   // Tell whether the given character is permitted by the given mask pair
    private static boolean match(char c, long lowMask, long highMask) {
    
    
        if (c == 0) // 0 doesn't have a slot in the mask. So, it never matches.
            return false;
        if (c < 64)
            return ((1L << c) & lowMask) != 0;
        if (c < 128)
            // 左移 (c - 64) 位,和 highMask 进行 & 运算,如果不等于 0 就表示这个字符是合法的
            return ((1L << (c - 64)) & highMask) != 0;
        return false;
    }

二、分析 checkChars 方法

通过上面的分析,URI 判断是否合法字符的原理就是:将和合法范围的字符转成一个二进制数,然后将传进来的字符和这个二进制进行与运算,如果不等于 0 就表示这个字符是合法的。

再来分析一下这个方法:

checkChars(1, p, L_SCHEME, H_SCHEME, "scheme name");

通过代码中的计算,L_SCHEME 的结果是:

0 | lowMask('0', '9') | lowMask("+-.")

H_SCHEME:

H_SCHEME = highMask('a', 'z') | highMask('A', 'Z') | 0 | highMask("+-.")

来看 lowMask的代码,生成的结果 m 表示 [0-9] 二进制的范围:

    // Compute a low-order mask for the characters
    // between first and last, inclusive
    private static long lowMask(char first, char last) {
    
    
        long m = 0;
        int f = Math.max(Math.min(first, 63), 0);
        int l = Math.max(Math.min(last, 63), 0);
        for (int i = f; i <= l; i++)
            m |= 1L << i;
        return m;
    }

然后 在 match 方法里:

    // Tell whether the given character is permitted by the given mask pair
    private static boolean match(char c, long lowMask, long highMask) {
    
    
        if (c == 0) // 0 doesn't have a slot in the mask. So, it never matches.
            return false;
        if (c < 64)
            // 对于小于 64 的字符,和 lowMask 进行与运算,不等于0表示合法的字符
            return ((1L << c) & lowMask) != 0;
        if (c < 128)
            return ((1L << (c - 64)) & highMask) != 0;
        return false;
    }

猜你喜欢

转载自blog.csdn.net/jiaobuchong/article/details/102757459
URI