相关文章
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 = "|"; // | 转义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