当前位置: 主页 > JAVA语言

java获取字符位置-java获取pdf的中间坐标位置

发布时间:2023-02-10 09:25   浏览次数:次   作者:佚名

字符串详细信息

char数据类型在Java中用来表示字符,但是char类型不能表示所有的字符。

Unicode 字符集

首先要知道的是,我们在 Java 中使用的是 Unicode 字符集。 在它出现之前,已经有很多字符集,比如ANSI、GB2312等等。 由于有这么多不同标准的字符集,这导致了两个问题:

对于任何给定的编码值,不同的字符可能对应不同字符集中的不同字符; 字符集较大的语言可能会有不同的编码长度,比如对常用字符进行单字节编码,对其他字符进行多字节编码。

Unicode字符集的出现就是为了统一编码,消除上述问题。 所谓字符集就是许多不同字符的集合。 Unicode字符集为每个字符分配了一个唯一的代码点(code point)来标识字符本身。 所谓码位就是一个十六进制整数加上U+前缀,比如字母A的码位就是U+0041。

有了Unicode字符集,我们需要考虑的是如何传输和存储这些字符。 这就是Unicode编码的实现,我们称之为Unicode Transformation Format(简称UTF)。 大家熟悉的UTF-8、UTF-16等都是Unicode的不同编码实现。

Unicode字符集诞生之初,采用UCS-2(2-byte Universal Character Set)的定长编码方式对Unicode字符集进行编码。 该方法使用16位长度进行字符编码,所以最大可以编码2^16 = 65536个字符(编码范围从U+0000 ~ U+FFFF)。 在当时的情况下,设计者使用了不到一半的数字来编码所有的字符,并认为剩余的空间足以用于未来额外字符的编码。

不幸的是,随着汉、日、韩等表意字符的不断加入,Unicode字符集中的字符数很快就超过了16位所能编码的最大字符数,于是设计者对Unicode 字符集。 设计。

新设计将字符集中的所有字符划分为17个代码平面。 其中,U+0000 ~ U+FFFF的码位范围定义为基本多语言平面(Basic Multilingual Plane,简称BMP),其余字符分为16个辅助平面(Supplementary Plane),以及码位范围为U+10000 ~ U+10FFFF,辅助平面中的这些字符称为增补字符。

Unicode字符集中的字符重新归类到不同平面后,需要注意以下两个方面:

BMP范围内的字符与UCS-2下的字符编码基本一致,但BMP中的U+D800~U+DFFF部分留空,不分配给任何字符,用于UCS-2下的字符编码辅助平面。 编码。 并非每个平面内的每个位置都分配给给定字符,因为:

UTF-16

UTF-16也是用16位编码来表示Unicode字符,也就是说UTF-16的编码单元是16位。 编码单元是指字符编码的最基本单位,即任何一个字符都必须由n(n≥1)个编码单元组成。

UTF-16下,由于16位长度只能表示65536个字符,默认映射BMP范围内的所有字符,所以U+D800~U+DFFF这部分留空,那么辅助平面的字符就可以了也可以借助这个空白部分来表达。 这就是UTF-16的巧妙设计,在不浪费空间的情况下java获取字符位置,解决了所有字符的编码问题。

那么如何表达辅助位面的特征呢? 实际上,辅助平面字符的码位被编码成一对16位长的码单元,称为代理对,代理对必须落在BMP的U+D800~U+DFFF部分。 这解决了用 16 位代码单元对整个 Unicode 字符集进行编码的问题。 需要注意的是,U+D800~U+DFFF部分可以称为代理区,U+D800~U+DBFF部分称为高代理区(leading proxy area),U+DC00~部分称为代理区U+DFFF 称为低代理区(后代理区)。

下面以UTF-16编码U+64321这个辅助平面字符为例,说明辅助平面字符的编码方式。

1、先将这个字符的码位减去0x10000java获取字符位置,得到一个长度为20位的值。 该值的范围必须在 0x0000 ~ 0xFFFF 之间。

V = 0x64321

php获取特定字符_java获取pdf的中间坐标位置_java获取字符位置

Vx= V - 0x10000 = 0x54321 = 0101 0100 0011 0010 0001

2、将Vx的高10位值作为高位代理的运算基Vh,将低10位的值作为低位代理的运算基Vl。 这两个10位值的取值范围必须在0x0000~0x3FF之间。

 Vh = 0101 0100 00
 Vl = 11 0010 0001

3、将Vh和Vl分别与高位代理区和低位代理区起始位置的码点进行按位或运算,结果为字符U+64321的UTF-16编码在辅助平面上。

 W1 = 0xD800
    = 1101 1000 0000 0000
 W2 = 0xDC00
    = 1101 1100 0000 0000
 W1 = W1 | Vh
    = 1101 1000 0000 0000
     |       01 0101 0000

java获取字符位置_java获取pdf的中间坐标位置_php获取特定字符

= 1101 1001 0101 0000 = 0xD950 W2 = W2 | Vl = 1101 1100 0000 0000 | 11 0010 0001 = 1101 1111 0010 0001 = 0xDF21

4. 最后,字符U+64321被编码为由高位代理和低位代理组成的代理对。 我们需要同时用0xD950和0xDF21来表示这个字符。

从上面的例子我们可以看出,在UTF-16下,辅助平面中的任意一个字符都会被编码为由两个长度为16位的代理码组成的代理对。 在程序中表示这个字符的时候,需要占用的不再是16位的空间,而是32位的空间。

不建议在 Java 程序中使用 char 数据类型

在上面解释了Unicode字符集和UTF-16之后,我们现在讨论为什么不推荐在Java程序中使用char数据类型。

由于Java使用16位的Unicode字符集,即UTF-16,所以Java中的char数据类型是定长的,其长度始终只有16位。 char数据类型只能表示码位在U+0000~U+FFFF之间的字符,即BMP中的字符。 如果code point超出这个范围,即使使用增补字符,也不会支持char数据类型,因为增补字符需要32位长度来存储,我们只能用String来存储这个字符。

    char c1 = '?';
    char c2 = '\u64321';

如果上面写的代码使用char数据类型来保存辅助平面的字符,编译器会报错Invalid character constant。

java获取pdf的中间坐标位置_java获取字符位置_php获取特定字符

随着互联网用户的不断增加和互联网语言的不断丰富,用户越来越频繁地使用互联网上的一些特殊字符来表达丰富的语义,而这些字符很可能属于辅助平面中的增补字符,所以如果我们使用char类型进行处理,很可能会降低我们程序的健壮性。

获取字符串长度

String是我们在编程中使用较多的一种数据类型,它用来表示一个字符串。 查看String的源码,我们可以看到它的底层其实是使用了一个char类型的数组来存储我们的字符。

    /** The value is used for character storage. */
    private final char value[];

我们也知道调用它的length()方法可以得到字符串的长度,即字符串的字符个数。 它的实现是直接返回底层值数组的长度,代码如下:

    /**
     * Returns the length of this string.
     * The length is equal to the number of Unicode code units in the string.
     *
     * @return  the length of the sequence of characters represented by this object.
     */
    public int length() {
        return value.length;
    }

结合我们上面关于字符编码的知识,我们知道Java中char的长度总是16位。 如果我们在字符串中使用增补字符,就意味着需要存储2个char类型的长度。 对于String底层存储字符的数组值,需要2个数组元素位置。 所以下面的程序我们会得到意想不到的结果:

java获取pdf的中间坐标位置_php获取特定字符_java获取字符位置

    String tt = "我喜欢?这个字符";
    System.out.println(tt.length()); // 9

按照我们的想法,字符串tt应该只有8个字符,但是实际输出的是9个。上面我们已经提到Java使用16位的Unicode字符集,所以Java中一个编码单元的长度也是16位。 增补字符需要两个代码单元来表示,所以字符 ? in tt字符串需要在value数组中占据两个位置,这就是为什么输出9而不是8的原因。

这反映了Java中char类型不能表示增补字符的问题。 其实仔细看length()方法的注释我们也可以知道,这个方法返回的是这个字符串中Unicode编码单元的个数。

那么有什么办法可以得到我们想要的8呢? 我们可以调用 codePointCount(int beginIndex, int endIndex) 方法来实现这一点。 顾名思义,此方法返回字符串指定部分的代码点数。 不管你是BMP范围内的字符还是辅助平面字符,你的code point只能是一个,所以这个可以准确获取。 字符串中的字符个数,我们看一下这个方法的实现:

    public int codePointCount(int beginIndex, int endIndex) {
        if (beginIndex < 0 || endIndex > value.length || beginIndex > endIndex) {
            throw new IndexOutOfBoundsException();
        }
        return Character.codePointCountImpl(value, beginIndex, endIndex - beginIndex);
    }

该方法首先判断传入的范围是否合法,然后调用java.lang.Character的static int codePointCountImpl(char[] a, int offset, int count)方法计算码位。 我们看一下具体实现:

    static int codePointCountImpl(char[] a, int offset, int count) {
        int endIndex = offset + count;
        int n = count;
        for (int i = offset; i < endIndex; ) {

java获取字符位置_php获取特定字符_java获取pdf的中间坐标位置

if (isHighSurrogate(a[i++]) && i < endIndex && isLowSurrogate(a[i])) { n--; i++; } } return n; }

默认情况下,该方法返回传入的指定字符串的长度,也就是说默认字符串中的每个字符都是BMP中的一个字符。 接下来的for循环是核心逻辑,判断字符串中第n个字符和第n+1个字符是否分别落在高代理区和低代理区。 如果满足判断条件,则默认返回的字符总数减一。

因为如果第n个字符和第n+1个字符分别落在高位代理区和低位代理区,就说明这是一个增补字符,增补字符占用两个编码单元,所以总共有默认返回的字符数需要减一,所以得到的是真实的总字符数。

尽量不要使用String来存储密码等敏感信息

我们通常使用String类型的变量来保存用户提交的密码等敏感信息,但实际上这是一种不安全的做法。

从String类的签名可以看出,String对象是不可变的,也就是说,String对象一旦被创建,就不能被任何方法(除了使用反射)修改,直到被垃圾回收器回收( this 有一段时间这个 String 对象通常存在于常量池中)。 这意味着在String对象被创建到垃圾回收器回收期间,一旦内存被转储,密码等敏感信息将以明文形式暴露。

另外,我们在编程时可能会无意中将密码打印到日志中,这也可能导致敏感信息因为日志文件被盗而泄露。 最后,官方 Java 文档中关于密码加密的部分也建议不要使用 String 对象来存储密码。

那么我们应该用什么方法来保存密码等敏感信息呢? 正确的选择是使用 char 数组。 因为使用了数组,我们可以在敏感信息的业务逻辑处理完成后,及时将其设置为任意其他值,从而清除我们的密码信息。 此外,如果我们不小心记录了密码,char 数组会输出内存地址而不是我们的敏感数据。

需要注意的是,即使使用char数组来存储敏感信息,也不能保证绝对的安全,因为内存中仍有可能存在这些数据的零散碎片。 更安全的做法是对存储的敏感信息进行哈希处理,最好在哈希中加盐,这样可以进一步提高信息的安全性。 但不得不说,没有绝对的安全,只有更可靠的安全保护方式。

————————————————————————————————————————

学习过程中有任何问题,欢迎来dreawer.com提问,或扫描下方二维码,关注dreawer官方微信,在平台下方留言~

php获取特定字符_java获取pdf的中间坐标位置_java获取字符位置