java输出 US-ASCII、ISO_8859_1、UTF-16、UTF-8、GB2312、GBK、GB18030所有字符,并对比

相关文章

1.ASICII、GB2312、GBK、GB18030 以及 UTF8 的关系

在这里插入图片描述

2.编写代码

  • 引入依赖:hutool工具类
        <dependency>
            <groupId>cn.hutool</groupId>
            <artifactId>hutool-all</artifactId>
            <version>5.8.32</version>
        </dependency>
  • 编写java代码
package top.lishuoboy.regex.java;

import cn.hutool.core.date.DateUtil;
import cn.hutool.core.date.TimeInterval;
import cn.hutool.core.io.FileUtil;
import cn.hutool.core.lang.Console;
import cn.hutool.core.thread.ThreadUtil;
import cn.hutool.core.util.ArrayUtil;
import cn.hutool.core.util.ByteUtil;
import cn.hutool.core.util.StrUtil;
import lombok.extern.slf4j.Slf4j;

import java.io.File;
import java.nio.ByteOrder;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.util.SortedMap;
import java.util.concurrent.ThreadPoolExecutor;

@Slf4j
public class MyCharsetPrint {
    static TimeInterval timer = DateUtil.timer();
    static ThreadPoolExecutor pool = ThreadUtil.newExecutor(100, 100, Integer.MAX_VALUE);
    static String BASE_DIR = "asset/0.charset";
    static Charset CHARSET_ASCII = StandardCharsets.US_ASCII;
    static Charset CHARSET_ISO_8859_1 = StandardCharsets.ISO_8859_1;
    static Charset CHARSET_UTF_16 = StandardCharsets.UTF_16;
    static Charset CHARSET_UTF_8 = StandardCharsets.UTF_8;
    static Charset CHARSET_GB2312 = Charset.forName("GB2312");
    static Charset CHARSET_GBK = Charset.forName("GBK");
    /**
     * jdk17.0.8、jdk11.0.20 和 jdk8u381 对GB18030编码提供了增强支持。 默认版本从GB18030-2000升级到了18030-2022。
     * 如需使用GB18030-2000,需指定虚拟机参数-Djdk.charset.GB18030=2000
     */
    static Charset CHARSET_GB18030 = Charset.forName("GB18030");

    public static void main(String[] args) {
        // 输出所有支持的字符集 及 对应的别名
        printAvailableCharsets();
        // 输出 '中' 的UTF-16(unicode)编码,正则找中文就是一以UTF-16为准
        System.err.println(Integer.toHexString('中'));   // 4e2d

        timer.restart();
        // 输出各个字符集的所有字符
        printChar("0.1-1.1", CHARSET_ASCII, false);
        printChar("0.1-1.1", CHARSET_ASCII, true);
        printChar("0.2-1.2", CHARSET_ISO_8859_1, false);
        printChar("0.2-1.2", CHARSET_ISO_8859_1, true);
        printChar("0.3-2.1", CHARSET_UTF_16, false);
        printChar("0.3-2.1", CHARSET_UTF_16, true);
        printChar("0.4-2.2", CHARSET_UTF_8, false);
        printChar("0.4-2.2", CHARSET_UTF_8, true);
        printChar("0.5-3.1", CHARSET_GB2312, false);
        printChar("0.5-3.1", CHARSET_GB2312, true);
        printChar("0.6-3.2", CHARSET_GBK, false);
        printChar("0.6-3.2", CHARSET_GBK, true);
        printChar("0.7-3.3", CHARSET_GB18030, false);
        printChar("0.7-3.3", CHARSET_GB18030, true);

        pool.shutdown();
        while (!pool.isTerminated()) {
            log.warn("pool.ActiveCount=={}", pool.getActiveCount());
            ThreadUtil.sleep(10000);
        }
        log.info("用时=={}", timer.intervalPretty());
    }

    /**
     * 输出所有字符
     *
     * @param isMarkDown 是否markdown格式
     */
    private static void printChar(String sortNo, Charset charset, boolean isMarkDown) {
        pool.submit(() -> {
            log.warn(StrUtil.fillAfter("name==" + charset, ' ', 30) + ", aliases=={}", charset.aliases());
            File file;
            if (isMarkDown) {
                file = new File(BASE_DIR, sortNo + "." + charset + ".md");
            } else {
                file = new File(BASE_DIR, sortNo + "." + charset + ".txt");
            }
            FileUtil.del(file);   // 删文件
            StringBuilder sbLine = StrUtil.builder();
            StringBuilder sbLines = StrUtil.builder();
            if (isMarkDown) {
                sbLines.append("""
                    |-0.5byte| 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | a | b | c | d | e | f |
                    |-------:|:-:|:-:|:-:|:-:|:-:|:-:|:-:|:-:|:-:|:-:|:-:|:-:|:-:|:-:|:-:|:-:|
                      """);
            }
            /*
             ASCII/ISO_8859_1       占1              个字节。
             GB2312/GBK             占1/2            个字节。汉字   占2个字节
             GB18030                占1/2/4          个字节。汉字主要占2个字节
             UTF-16                 占  2/4          个字节。汉字   占2个字节。暂没找到3个字节以上的字符,不输出了
             UTF-8                  占1/2/3/4/5/6    个字节。汉字   占3个字节。暂没找到3个字节以上的字符,不输出了
             */
            long iMax;
            if (StrUtil.equalsAnyIgnoreCase(charset.name(), "US-ASCII", "ISO-8859-1")) {
                iMax = 1L << 8;
            } else if (StrUtil.equalsAnyIgnoreCase(charset.name(), "GB2312", "GBK")) {
                iMax = 1L << 16;
            } else if (StrUtil.equalsAnyIgnoreCase(charset.name(), "UTF-16", "GB18030")) {
                iMax = 1L << 32;
            } else if (StrUtil.equalsAnyIgnoreCase(charset.name(), "UTF-8")) {
                iMax = 1L << 32;
            } else {
                iMax = 1L;
                log.warn("字符集的长度未指定, charset==[{}]", charset);
            }

            // 实测字符范围
            long minVal = 1L << 24;
            long maxVal = 1L << 32;
            if (charset.equals(CHARSET_GB18030)) {   // 0x81308130L~0x8431a43fL, 0x90308130L~0x9939f73fL
                minVal = 0x81308130L;
                maxVal = 0x9939f73fL;
            } else if (charset.equals(CHARSET_UTF_16)) {
                minVal = 0xd800dc00L;
                maxVal = 0xd87adfefL;
            } else if (charset.equals(CHARSET_UTF_8)) {
                minVal = 0xf0908080L;
                maxVal = 0xf0aeafafL;
            }
            for (long i = 0; i < iMax; i++) {
                if (i >= 1 << 24 && !(i >= minVal && i <= maxVal)) {
                    continue;
                }
                byte[] bytes = ByteUtil.longToBytes(i, ByteOrder.BIG_ENDIAN);
                int maxLen;  // 转16进制的最大长度
                if (charset.equals(CHARSET_UTF_16)) {
                    maxLen = 4 * ((Long.toBinaryString(i).length() - 1) / 16 + 1);
                    bytes = ArrayUtil.sub(bytes, bytes.length - maxLen / 2, bytes.length);    // 截取后2/4个字节
                } else {
                    maxLen = 2 * ((Long.toBinaryString(i).length() - 1) / 8 + 1);
                    bytes = removeBefore0(bytes);
                }
                String idxStr = StrUtil.fillBefore(Long.toHexString(i), '0', maxLen);    // 总次数-16进制格式
                // 拼接行头
                if (i % 16 == 0) {
                    String linePre = StrUtil.format("|{}|", idxStr);
                    if (isMarkDown) {
                        linePre = StrUtil.format("|{}|", StrUtil.fillBefore(idxStr, ' ', 8));
                    }
                    sbLine.append(linePre);
                }
                String charStr;         // 最终字符
                charStr = new String(bytes, charset);
                // 多字节的字符又不是1个字符或者为"�"则置空
                if ((StrUtil.equalsAny(charStr, "�") || charStr.codePointCount(0, charStr.length()) != 1)
//                && !charset.equals(CHARSET_UTF_16)
                ) {
                    charStr = "";
                }
                // 转义几个字符
                if (charStr.equals("\b")) {
                    charStr = "\\b";        // BS
                } else if (charStr.equals("\n")) {
                    charStr = "\\n";        // LF
                } else if (charStr.equals("\r")) {
                    charStr = "\\r";        // CR
                } else if (charStr.equals("|") && isMarkDown) {
                    charStr = "&#124;";     // | 转义markdown分隔符
                }
                // 拼接行
                String lineText = StrUtil.format("{}|", charStr);
                if (isMarkDown) {
                    lineText = StrUtil.format(" {} |", charStr);
                }
                sbLine.append(lineText);
                // 拼接行尾
                if ((i + 1) % 16 == 0) {
                    sbLine.append("\r\n");
                    if (StrUtil.containsAny(sbLine, "||||||||||||||||", "|  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |")) {  // 全行都为空则全行置为空
                        sbLine = StrUtil.builder();
                    }
                    sbLines.append(sbLine);
                    sbLine = StrUtil.builder();
                }
                // 写文件
                if ((i + 1) % (1 << 24) == 0) {
                    log.info(idxStr + "@" + file.getName());
//                    System.out.print(sbLines);
                    FileUtil.appendUtf8String(sbLines.toString(), file);
                    sbLines = StrUtil.builder();
                }
            }
//            System.out.print(sbLines);
            FileUtil.appendUtf8String(sbLines.toString(), file);
        });
    }

    /** 将字节数组中前面的0干掉(知道第一个不是0的结束) */
    private static byte[] removeBefore0(byte[] bytes) {
        byte[] bytesNew = new byte[1];
        for (int i = 0; i < bytes.length; i++) {
            if (bytes[i] != 0) {
                bytesNew = ArrayUtil.sub(bytes, i, bytes.length);
                return bytesNew;
            }
        }
        return bytesNew;
    }

    /** 输出所有支持的字符集 及 对应的别名 */
    private static void printAvailableCharsets() {
        SortedMap<String, Charset> availableCharsets = Charset.availableCharsets();
        for (Charset charset : availableCharsets.values()) {
            Console.log(StrUtil.fillAfter("name==" + charset, ' ', 30) + ", aliases=={}", charset.aliases());
        }
    }

}

3.对比

3.1. GB2312与GBK

  • 1在这里插入图片描述
  • 2
    在这里插入图片描述

3.2. GBK与GB18030(只比对前2个字节)

  • 1
    在这里插入图片描述

  • 2
    在这里插入图片描述

  • 3
    在这里插入图片描述

  • 1
    在这里插入图片描述

  • 2
    在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

茅坑的小石头

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值