C语言 c++ php mysql nginx linux lnmp lamp lanmp memcache redis 面试 笔记 ppt 设计模式 问题 远程连接

字符编码那些事


java char的述职范围 0-65535 超过的用两个长度的char数组(utf-16)表示
Character.toChars(0x4E2Df)


你不能用一个单一的char(持有一个UTF-16代码单元)做到这一点
// This represents U+10FFFF
String x = "\udbff\udfff";

两个UTF-16代码单元结合在一起,形成超出基本多语言平面的单一Unicode代码点
String y = new StringBuilder().appendCodePoint(0x10ffff).toString();

int x = 0x10FFFF;
String y = new String(Character.toChars(ch));


notepad UCS-2 直接用两个字节存入字符的 Unicode 码, UCS-2用两个字节编码,UCS-4用4个字节编码

Unicode的最大码位是0x10FFFF

UTF-16与UCS-2的关系
UTF-16可看成是UCS-2的父集。在没有辅助平面字符(surrogate code points)前,UTF-16与UCS-2所指的是同一的意思。但当引入辅助平面字符后,就称为UTF-16了。现在若有软件声称自己支援UCS-2编码,那其实是暗指它不能支援在UTF-16中超过2bytes的字集。对于小于0x10000的UCS码,UTF-16编码就等于UCS码。

utf-16 用两个字节或四个字节表示,中间用到里代理,以更方便和unicode转换
unicode编码范围:0x0000~0x10FFFF,共17个面,但实际中只用了5个面

0号面 bmp 用的最多

 

unicode转换工具:http://tool.chinaz.com/tools/unicode.aspx 

 

https://www.javacodex.com/Strings/Get-Unicode-Values-Of-A-String 

对于超过65535的unicode会有问题

public class StringUnicode {
   public static void main(String[] args) {
      String str = "abc ABC";
      for (char c : str.toCharArray()) {
         System.out.printf("\\u%04x \n", (int) c); 
      }
   }
}
 

 

java中获取字符的unicode值(适用于所有字符)

        String y = new String(Character.toChars(0x2B85B)); // code point转字符串
        System.out.println(y.codePointAt(0)); // 字符串转 code point

 

        String y1 = new StringBuilder().appendCodePoint(0x2B85B).toString(); // StringBuilder 也支持 code point
        System.out.println(y1);

 

        String x = "\uD86E\uDC5B";
        System.out.println(x);

 

        /**
         * char 不同的赋值类型
         */
        //char c = '\u0061';
        //char c = 0x61;
        //char c = 97;
        char c = 'a';

        System.out.println(c);

 

php中unicode操作 

  Unicode codepoint 转译语法

  这接受一个以16进制形式的 Unicode codepoint,并打印出一个双引号或heredoc包围的 UTF-8 编码格式的字符串。 可以接受任何有效的 codepoint,并且开头的 0 是可以省略的。

  

    public function test4(){

        echo "\u{aa}";
        echo "\u{0000aa}";
        echo "\u{9999}";

    }

 

需要扩展(国际化与字符编码)支持  https://secure.php.net/manual/zh/class.intlchar.php

    public function test4(){

        $values = ["A", 63, 123, 9731];
        foreach ($values as $value) {
            var_dump(IntlChar::chr($value));
        }
    }

 

 新增加的 IntlChar 类旨在暴露出更多的 ICU 功能。这个类自身定义了许多静态方法用于操作多字符集的 unicode 字符

<?php

printf('%x', IntlChar::CODEPOINT_MAX);
echo IntlChar::charName('@');
var_dump(IntlChar::ispunct('!'));

 

超过U+FFFF的码位也支持

        $a = "𫡛";
        $str = json_encode($a);
        var_dump($str);

        var_dump(json_decode($str));

        // output
        //  string(14) ""\ud86e\udc5b"" (utf-16)
        //string(4) "𫡛"

 

\[0-7]{1,3}       符合该正则表达式序列的是一个以八进制方式来表达的字符
\x[0-9A-Fa-f]{1,2}   符合该正则表达式序列的是一个以十六进制方式来表达的字符

 

// 您可以直接在PHP中对字符进行编码 \x 二进制转义。该 \x 转义语法也是 PHP 5支持
echo
("\xE3\x82\xA2");

 

 

 

UTF-8 使用一至四个字节为每个字符编码,其中大部分汉字采用三个字节编码,少量不常用汉字采用四个字节编码。 

UTF-16 使用二或四个字节为每个字符编码,其中大部分汉字采用两个字节编码,少量不常用汉字采用四个字节编码。 

 

Unicode 字符表示形式

char 数据类型(和 Character 对象封装的值)基于原始的 Unicode 规范,将字符定义为固定宽度的 16 位实体。Unicode 标准曾做过修改,以允许那些其表示形式需要超过 16 位的字符。合法代码点 的范围现在是从 U+0000 到 U+10FFFF,即通常所说的 Unicode 标量值。(请参阅 Unicode 标准中 U+n 表示法的定义。)

从 U+0000 到 U+FFFF 的字符集有时也称为 Basic Multilingual Plane (BMP)。代码点大于 U+FFFF 的字符称为增补字符Java 2 平台在 char 数组以及 StringStringBuffer 类中使用 UTF-16 表示形式。在这种表现形式中,增补字符表示为一对 char 值,第一个值取自高代理项 范围,即 (\uD800-\uDBFF),第二个值取自低代理项 范围,即 (\uDC00-\uDFFF)。

所以,char 值表示 Basic Multilingual Plane (BMP) 代码点,其中包括代理项代码点,或 UTF-16 编码的代码单元。int 值表示所有 Unicode 代码点,包括增补代码点。int 的 21 个低位(最低有效位)用于表示 Unicode 代码点,并且 11 个高位(最高有效位)必须为零。除非另有指定,否则与增补字符和代理项 char 值有关的行为如下:

  • 只接受一个 char 值的方法无法支持增补字符。它们将代理项字符范围内的 char 值视为未定义字符。例如,Character.isLetter('\uD840') 返回 false,即使是特定值,如果在字符串的后面跟着任何低代理项值,那么它将表示一个字母。
  • 接受一个 int 值的方法支持所有 Unicode 字符,其中包括增补字符。例如,Character.isLetter(0x2F81A) 返回 true,因为代码点值表示一个字母(一个 CJK 象形文字)。

在 Java SE API 文档中,Unicode 代码点 用于范围在 U+0000 与 U+10FFFF 之间的字符值,而 Unicode 代码点 用于作为 UTF-16 编码的代码单元的 16 位 char 值。有关 Unicode 技术的详细信息,请参阅 Unicode Glossary

参见:https://docs.oracle.com/javase/1.5.0/docs/api/java/lang/Character.html#unicode

 

- A Java char takes always 16 bits.
- A Unicode character, when encoded as UTF-16, takes “almost always” (not always) 16 bits: that’s because there are more than 64K unicode characters. Hence, a Java char is NOT a Unicode character (though “almost always” is).
- “Almost always”, above, means the 64K first code points of Unicode, range 0x0000 to 0xFFF (BMP), which take 16 bits in the UTF-16 encoding.
- A non-BMP (“rare”) Unicode character is represented as two Java chars (surrogate representation). This applies also to the literal representation as a string: For example, the character U+20000 is written as “\uD840\uDC00”.
- Corolary: string.length() returns the number of java chars, not of Unicode chars. A string that has just one “rare” unicode character (eg U+20000) would return length() = 2 . Same consideration applies to any method that deals with char-sequences.
- Java has little intelligence for dealing with non-BMP unicode characters as a whole. There are some utility methods that treat characters as code-points, represented as ints eg: Character.isLetter(int ch). Those are the real fully-Unicode methods.

 

java中字符串以UTF-16编码格式对字符进行编码

大部分情况下一个char类型等同于该字符的unicode码值,但当unicode码值超过0xFFFF时,会采用两个char(utf-16编码,大端)来存储

因为一个char最大的值为 0xFFFF

 

 

JavaScript 内部,字符以 UTF-16 的格式储存,每个字符固定为2个字节。对于那些需要4个字节储存的字符(Unicode 码点大于0xFFFF的字符),JavaScript 会认为它们是两个字符。

 

var s = "𠮷";

s.length // 2
s.charAt(0) // ''
s.charAt(1) // ''
s.charCodeAt(0) // 55362
s.charCodeAt(1) // 57271

 

上面代码中,汉字“𠮷”(注意,这个字不是“吉祥”的“吉”)的码点是0x20BB7,UTF-16 编码为0xD842 0xDFB7(十进制为55362 57271),需要4个字节储存。对于这种4个字节的字符,JavaScript 不能正确处理,字符串长度会误判为2,而且charAt()方法无法读取整个字符,charCodeAt()方法只能分别返回前两个字节和后两个字节的值。

 

ES6 提供了codePointAt()方法,能够正确处理 4 个字节储存的字符,返回一个字符的码点。

 

let s = '𠮷a';

s.codePointAt(0) // 134071
s.codePointAt(1) // 57271

s.codePointAt(2) // 97

 

codePointAt()方法的参数,是字符在字符串中的位置(从 0 开始)。上面代码中,JavaScript 将“𠮷a”视为三个字符,codePointAt 方法在第一个字符上,正确地识别了“𠮷”,返回了它的十进制码点 134071(即十六进制的20BB7)。在第二个字符(即“𠮷”的后两个字节)和第三个字符“a”上,codePointAt()方法的结果与charCodeAt()方法相同。

 

总之,codePointAt()方法会正确返回 32 位的 UTF-16 字符的码点。对于那些两个字节储存的常规字符,它的返回结果与charCodeAt()方法相同。

 

codePointAt()方法返回的是码点的十进制值,如果想要十六进制的值,可以使用toString()方法转换一下。

 

let s = '𠮷a';

s.codePointAt(0).toString(16) // "20bb7"
s.codePointAt(2).toString(16) // "61"

 

你可能注意到了,codePointAt()方法的参数,仍然是不正确的。比如,上面代码中,字符a在字符串s的正确位置序号应该是 1,但是必须向codePointAt()方法传入 2。解决这个问题的一个办法是使用for...of循环,因为它会正确识别 32 位的 UTF-16 字符。

 

let s = '𠮷a';
for (let ch of s) {
  console.log(ch.codePointAt(0).toString(16));
}
// 20bb7
// 61

 

另一种方法也可以,使用扩展运算符(...)进行展开运算。

 

let arr = [...'𠮷a']; // arr.length === 2
arr.forEach(
  ch => console.log(ch.codePointAt(0).toString(16))
);
// 20bb7
// 61

 

codePointAt()方法是测试一个字符由两个字节还是由四个字节组成的最简单方法。

 

function is32Bit(c) {
  return c.codePointAt(0) > 0xFFFF;
}

is32Bit("𠮷") // true
is32Bit("a") // false

 

 

 

 

Unicode编码下载地址

 

java中的编码库

sun.nio.cs
  sun.nio.cs.ext.GB18030
  sun.nio.cs.ext.GBK
  ...  sun.nio.cs.ext
  sun.nio.cs.UTF_8
  sun.nio.cs.UTF_16
  sun.nio.cs.UTF_32
  ...

GBK、GB2312--Unicode--UTF8

UTF8--Unicode--GBK、GB2312

gbk和utf-8转换需要经过Unicode转换

https://blog.csdn.net/cszhang570221322/article/details/84786918

 

 编码对应示例

 

一
Unicode:  u'\u4e00'
UTF-8:  '\xe4\xb8\x80'
GBK:  '\xd2\xbb'
********************
龥
Unicode:  u'\u9fa5'
UTF-8:  '\xe9\xbe\xa5'
GBK:  '\xfd\x9b'
********************
烫
Unicode:  u'\u70eb'
UTF-8:  '\xe7\x83\xab'
GBK:  '\xcc\xcc'
********************
锟
Unicode:  u'\u951f'
UTF-8:  '\xe9\x94\x9f'
GBK:  '\xef\xbf'
********************
斤
Unicode:  u'\u65a4'
UTF-8:  '\xe6\x96\xa4'
GBK:  '\xbd\xef'
********************
拷
Unicode:  u'\u62f7'
UTF-8:  '\xe6\x8b\xb7'
GBK:  '\xbf\xbd'
********************
锟斤拷
Unicode:  u'\u951f\u65a4\u62f7'
UTF-8:  '\xe9\x94\x9f\xe6\x96\xa4\xe6\x8b\xb7'
GBK:  '\xef\xbf\xbd\xef\xbf\xbd'
********************
鐜
Unicode:  u'\u941c'
UTF-8:  '\xe9\x90\x9c'
GBK:  '\xe7\x8e'
********************
嬪
Unicode:  u'\u5b2a'
UTF-8:  '\xe5\xac\xaa'
GBK:  '\x8b\xe5'
********************
悍
Unicode:  u'\u608d'
UTF-8:  '\xe6\x82\x8d'
GBK:  '\xba\xb7'
********************
鐜嬪悍
Unicode:  u'\u941c\u5b2a\u608d'
UTF-8:  '\xe9\x90\x9c\xe5\xac\xaa\xe6\x82\x8d'
GBK:  '\xe7\x8e\x8b\xe5\xba\xb7'
********************
王
Unicode:  u'\u738b'
UTF-8:  '\xe7\x8e\x8b'
GBK:  '\xcd\xf5'
********************
康
Unicode:  u'\u5eb7'
UTF-8:  '\xe5\xba\xb7'
GBK:  '\xbf\xb5'
********************
王康
Unicode:  u'\u738b\u5eb7'
UTF-8:  '\xe7\x8e\x8b\xe5\xba\xb7'
GBK:  '\xcd\xf5\xbf\xb5'
********************
王康
UTF-8 misdecode as GBK:
鐜嬪悍
u'\u941c\u5b2a\u608d'
GBK misdecode as UTF-8:
>_<
********************

 

json编码转义的处理

  对于非基础平面的字符,以utf16编码(四个字节)表示

   To escape an extended character that is not in the Basic Multilingual
   Plane, the character is represented as a twelve-character sequence,
   encoding the UTF-16 surrogate pair.  So, for example, a string
   containing only the G clef character (U+1D11E) may be represented as
   "\uD834\uDD1E".

  JSON RFC, section 2.5

  the G clef character (U+1D11E) => "\uD834\uDD1E"

 

实体编码 entity code

    三种格式:
        &name; //&+实体名称
        &#dddd; //&#+Unicode code point(十进制)
        &#xhhhh; //&#+Unicode code point(十六进制)

  并不是所有的浏览器都支持最新的Entity名字(更容易记忆)。而实体(Entity)编号,各种浏览器都能处理

 

  php中转为实体

        var_dump(mb_convert_encoding ("a你好",  "HTML-ENTITIES", "utf-8"));

  不知道编码时,看哪个显示正常

        $text = "A strange string to pass, maybe with some ø, æ, å characters.";

        foreach(mb_list_encodings() as $chr){
            echo mb_convert_encoding($text, 'UTF-8', $chr)." : ".$chr."<br>";
        }
<?php
/* 转换内部编码为 SJIS */
$str = mb_convert_encoding($str, "SJIS");

/* 将 EUC-JP 转换成 UTF-7 */
$str = mb_convert_encoding($str, "UTF-7", "EUC-JP");

/* 从 JIS, eucjp-win, sjis-win 中自动检测编码,并转换 str 到 UCS-2LE */
$str = mb_convert_encoding($str, "UCS-2LE", "JIS, eucjp-win, sjis-win");

/* "auto" 扩展成 "ASCII,JIS,UTF-8,EUC-JP,SJIS" */
$str = mb_convert_encoding($str, "EUC-JP", "auto");
$string = "你好";
//注意编码顺序 UTF-8要放到前面
print_r(mb_detect_encoding($string, 'UTF-8,GB18030', true));

 

tomcat中request.getParameter编码

get、post都是作为 Parameters 保存,都是通过 request.getParameter 获取参数值
对它们的解码是在 request.getParameter 方法第一次被调用时进行的
request.getParameter 方法被调用时将会调用 org.apache.catalina.connector.RequestparseParameters 方法
这个方法将会对 GET 和 POST 方式传递的参数进行解码,但是它们的解码字符集有可能不一样(因为是两个属性获取)

 

get:  

QueryString 的解码字符集要么是 Header 中 ContentType 中定义的 Charset 要么就是默认的 utf-8


注意:要使用 ContentType 中定义的编码就要设置 connector 的 <Connector URIEncoding=”UTF-8” useBodyEncodingForURI=”true”/> 中的 useBodyEncodingForURI 设置为 true。

        @Bean //spring boot中打开
        TomcatConnectorCustomizer containerCustomizer() {
            return new TomcatConnectorCustomizer() {
                @Override
                public void customize(Connector connector) {
                    connector.setUseBodyEncodingForURI(true);
                }
            };

        }

 

  post: 

spring boot 中 有CharacterEncodingFilter 过滤器

spring.http.encoding.force=false 打开时,设置 Request对象的 charset 值(从content-type的charset字段中取)

没有设置时spring.http.encoding.force,使用默认的编码 utf-8

 

 

           

  没有过滤器:

    默认的:ISO-8859-1

 

 综上分析,spring boot默认情况下 

  GET/POST参数只支持 UTF-8 且不会解析 content-type中的编码

  spring.http.encoding.force=false ,post方式支持从content-type中获取编码

  还可以手动转码

  String name = new String(request.getParameter("name").getBytes("ISO8859-1【获取时使用的错误编码】"), "UTF-8")

 

ZERO WIDTH SPACE

  

Unicode码:U+200B
html实体(十进制):&#8203;
html实体(十六进制):&#x200b;
"\u200B"

 

spring中解析参数时使用的编码

以下只适用于 application/x-www-form-urlencoded 和 multipart/form-data 的普通字段部分

设置输入编码
request.setCharacterEncoding();
必须在第一次调用
request.getParameter()
request.getParameterMap()
之前设置,否则不生效

如果不设置 则使用 ISO-8859-1 编码 (org.apache.coyote.Constants.DEFAULT_BODY_CHARSET)
一般只影响post请求的编码

 

默认编码为:org.apache.coyote.Constants中的 Latin1

 

 

 

application/json:固定都是utf-8编码
 
 

java中字符串遍历

/**
     * java中字符串遍历
     *
     * https://stackoverflow.com/questions/11229986/get-string-character-by-index-java
     *
     * @param args
     */
    public static void main(String[] args) {

        String str = "The quick brown 𓃥 jumps over the lazy 𓊃𓍿𓅓𓃡";

        System.out.println("遍历包含超过 \uFFFF 的字符串, codePoints");
        StringBuilder sb = new StringBuilder();
        str.codePoints().forEach(value -> {
            char[] chars = Character.toChars(value);
            System.out.printf("%s ", new String(chars));

            sb.appendCodePoint(value);
        });
        System.out.println(sb.toString());

        System.out.println("遍历包含超过 \uFFFF 的字符串, codePointAt 适合正序循环,向前寻找匹配");
        for (int pos = 0; pos < str.length(); ) {
            int cp = str.codePointAt(pos);
            char[] chars = Character.toChars(cp);
            System.out.printf("%s ", new String(chars));
            pos += Character.charCount(cp);
        }

        System.out.println("遍历包含超过 \uFFFF 的字符串, codePointAt=>适合逆序循环,向后寻找匹配");
        for (int pos = str.length(); pos > 0; ) {
            int cp = str.codePointBefore(pos);
            char[] chars = Character.toChars(cp);
            System.out.printf("%s ", new String(chars));
            pos -= Character.charCount(cp);
        }
    }

 

 

 

参考文章:

  https://blog.csdn.net/u014631304/article/details/77509380

  http://www.fmddlmyy.cn/text16.html

  https://baike.baidu.com/item/Unicode/750500?fr=aladdin

   http://www.ruanyifeng.com/blog/2007/10/ascii_unicode_and_utf-8.html

   http://landcareweb.com/questions/2269/phpzi-fu-chuan-zhong-de-unicodezi-fu

https://www.php.net/manual/zh/function.mb-convert-encoding.php

  

https://www.ruanyifeng.com/blog/2014/12/unicode.html

https://github.com/ruanyf/es6tutorial/blob/0e7b53f00f6235929753c0fa526bf9e8e41a039f/docs/string-methods.md

 

posted on 2019-01-10 17:16  思齐_  阅读(990)  评论(0编辑  收藏  举报