Java二进制及中文转码和校验
目录导读
- Java二进制及中文转码和校验
- 1.Java基本数据类型
- 1.1 基本数据类型占用的存储空间
- 1.2 Java二进制流
- 1.3 二进制转换说明
- 1.3.1 二进制与Base64互转
- 1.3.2 二进制与十六进制互转
- 2.Java魔数应用
- 2.1 文件魔数
- 2.2 协议魔数
- 3.Java Unicode编码应用
- 3.1 Java中文字符转换
- 3.2 Java中文字符校验
- 4.参考资料
Java二进制及中文转码和校验
- 自大学那会学习了Java的8大基础数据类型(boolean/byte/short/char/int/long/float/double)后,毕业后的很多年几乎都不涉及基础数据类型了,尽管选的是Java方向;
- 工作中的前若干年还尽量避免定义基础类型,比如int数组改成List等,心里一直在告诫自己,Java是面向对象的语言,应该尽量使用面向对象的API和语法,少使用原始的基础类型;
- 又过了一些年,现在对基础类型有了全新的、自洽的认识,不吐不快,还请诸君斧正;
- 本文重点关注char和byte类型的编码使用、转换,以及实际业务场景的应用;
1.Java基本数据类型
8大基本数据类型列表
基本类型 大小(字节) 默认值 取值范围 后缀 示例 boolean 1 false {false,true} - boolean sign=true; byte 1 0 [-128,127] - byte b=125; short 2 0 [-32768~32767] - short num=10; char 2 ‘\u0000’ [0,65535] - char ch1=‘k’;
char ch2=‘\u77be’;
char ch3=‘瞾’;int 4 0 [-2^ 31 , 2^31-1] - int count=100; long 8 0 [-2^63 , 2 ^63-1] L/l long a=100L; float 4 0.0f [-2^ 31 , 2^31-1] F/f float a=1.2f double 8 0.0d [-2^ 63,2^63-1] D/d double a=1.2d - char表示一个字符,是相对于utf-8编码而言,但是对unicode却并不是100%适用,因为有部分汉字需要4个字节(2个unicode)来表示,也就是说一个汉字字符实际上是2个char,即上表的示例
char ch3='瞾'
就不够严谨; - 我们来验证下
char ch2='\u77be';
,ch2表示1个字符,同时也可以用’\u77be’来定义,而77be
就是4个16进制位,1个16进制位表示4个二进制位(4b),则77be
表示16个二进制位(16b),即2个字节(2Byte=16bit);
- char表示一个字符,是相对于utf-8编码而言,但是对unicode却并不是100%适用,因为有部分汉字需要4个字节(2个unicode)来表示,也就是说一个汉字字符实际上是2个char,即上表的示例
1.1 基本数据类型占用的存储空间
- 一个byte数字占用1个byte字节应该很好理解;
- 我们刚才已经说明了1个常规字符(char类型),占用2个字节(2byte);
1.2 Java二进制流
- Java抽象的Stream流(InputStream/OutputStream)本质上都是都是byte数组的传输;
InputStream
官方的注释为This abstract class is the superclass of all classes representing an input stream of bytes.
;OutputStream
官方的注释为This abstract class is the superclass of all classes representing an output stream of bytes.
- 在此基础之上,实现类ArrayByteInputStream/ArrayByteOutputStream/FileInputStream/FileOutputStream等各种数据流,都是不同应用场景上的实现封装,其二进制数据的本质是一样的;
- 项目中读取二进制流的工具类IoUtil :
public final class IoUtil {/*** 读取文件流* <pre>* 1.文件支持从class外部读取(class调试模式)和jar内部读取(jar包使用模式)* 2.以此class是否在jar包为依据,当不在时,优先从外部读取(证明是class调试模式,根本就没有jar包)* 3.如果上述方式读不到文件,则遵从spring的读取规则(使用spring的API读取)** @param path 文件路径* @return 文件流*/public static InputStream readInputStream(String path){try{String clazzPath = IoUtil.class.getResource(FILE_SPLIT).getPath();boolean clazzInJar = clazzPath.startsWith(FILE_TYPE);boolean inClazzWithoutJar = clazzPath.startsWith(FILE_SPLIT) && !clazzInJar;boolean inJar = path.toLowerCase(Locale.US).startsWith(CLASSPATH) || clazzInJar;String realPath = path;//非jar包模式时,拼接全路径读取if (inClazzWithoutJar && !inJar){if (!realPath.contains(FILE_FULL_PATH_TYPE) && !realPath.startsWith(FILE_SPLIT)){realPath = clazzPath + realPath;}return new FileInputStream(realPath);}Resource resource = new PathMatchingResourcePatternResolver().getResource(path);boolean existFile = resource.exists();//jar包模式运行时,通过spring的api去读取if (existFile){return resource.getInputStream();}}catch (Exception e){throw new EncryptionException("read stream error.", e);}return null;}/*** 私有化构造方法*/private IoUtil(){}/*** 非jar形式的文件全路径标记*/public static final String FILE_FULL_PATH_TYPE = ":";/*** 文件类型*/public static final String FILE_TYPE = "file:";/*** 文件分割类型*/public static final String FILE_SPLIT = "/";/*** 配置类型*/private static final String CLASSPATH = "classpath:"; }
- 项目中读写文件二进制数据的工具类FileUtil 代码如下:
public final class FileUtil {/*** 读取文件的二进制内容** @param path 文件路径* @return 文件二进制内容*/public static byte[] read(String path){return read(IoUtil.readInputStream(path));}/*** 读取输入流报文** @param in 文件输入流* @return 二进制报文*/public static byte[] read(InputStream in){return read(in, Integer.MAX_VALUE);}/*** 读取限定文件大小的二进制流** @param in 二进制流* @param size 二进制流的大小上限* @return 文件二进制报文*/public static byte[] read(InputStream in, int size){try{if (size <= 0){size = Integer.MAX_VALUE;}byte[] data = IOUtils.toByteArray(in);if (null != data && data.length > size){LOGGER.error("failed to read limit data size:{}.", data.length);return null;}FileType fileType = FileType.getType(data);LOGGER.info("current read stream file type:{}", fileType);return data;}catch (IOException e){LOGGER.error("failed to read input stream.", e);}finally{IOUtils.closeQuietly(in);}return null;}/*** 读取指定编码的文件内容(未指定编码格式时,则默认读取UTF-8编码)** @param path 文件路径* @param charset 文件编码格式* @return 文件内容*/public static String read(String path, Charset charset){byte[] data = read(path);if (null == data){LOGGER.error("failed to read input stream:{}.", path);return null;}if (null == charset){charset = StandardCharsets.UTF_8;}return new String(data, charset);}/*** 把内容写入文件** @param data 二进制文件内容* @param path 目标文件路径*/public static void write(byte[] data, String path){OutputStream out = null;try{File file = new File(path).getCanonicalFile();if (!file.isFile() || !file.exists()){FileUtils.forceMkdirParent(file);LOGGER.info("current file path[{}] force created.", file.getParentFile().getCanonicalPath());}FileType fileType = FileType.getType(data);LOGGER.info("current write stream file type:{}", fileType);out = new FileOutputStream(file);IOUtils.write(data, out);}catch (IOException e){LOGGER.error("failed to write file.", e);}finally{IOUtils.closeQuietly(out);}}private FileUtil(){}/*** 日志句柄*/private static final Logger LOGGER = LoggerFactory.getLogger(FileUtil.class); }
1.3 二进制转换说明
- 在了解了Java世界的二进制本质之后,还需要了解下二进制在不同业务场景下的具体转换应用。比如:图片转换、数据加解密等;
1.3.1 二进制与Base64互转
- Base64本质上是二进制数据的封装,用于显示表示二进制的场景含义;
- 图片二进制转成Base64源码ImageUtil 如下:
public final class ImageUtil {/*** 从bas464中转换出图片二进制数据** @param base64 图片base64* @return 图片二进制数据*/public static byte[] toBytes(String base64){if (StringUtils.isEmpty(base64)){LOGGER.error("invalid base64.");return null;}try{if (base64.contains(Const.SPLIT)){//去掉base64中的文件头前缀(如:'data:image/png;base64,')base64 = base64.substring(base64.lastIndexOf(Const.SPLIT));}byte[] data = Base64.getMimeDecoder().decode(base64);LOGGER.info("current image file type:{}", FileType.getType(data));return data;}catch (Exception e){LOGGER.error("failed to get byte[] from base64.", e);}return null;}/*** 二进制图片数据转换成base64** @param data 图片二进制数据* @return 图片base64*/public static String toBase64(byte[] data){if (null == data){LOGGER.error("invalid image file data.");return null;}try{LOGGER.info("current image file type:{}", FileType.getType(data));String base64 = Base64.getEncoder().encodeToString(data);if (!StringUtils.isEmpty(base64) && base64.contains(Const.SPLIT)){//去掉base64中的文件头前缀(如:'data:image/png;base64,')base64 = base64.substring(base64.lastIndexOf(Const.SPLIT));}return base64;}catch (Exception e){LOGGER.error("failed to get base64 by byte[].", e);}return null;}/*** 图片文件转base64** @param path 文件路径* @return 文件的base64*/public static String toBase64(String path){return toBase64(FileUtil.read(path));} }
在图片场景下,Base64表示的图片字符串完全等同于图片文件;
- 秘钥二进制转成对应的Base64秘钥字符串,如:RSA/PGP生成的cer /pem /asc 等秘钥文件,形如效果:
-----BEGIN RSA PUBLIC KEY----- MIIBCgKCAQEAjsWd3QKjDCVU+H9jkkMlOAxAKpG/nT7N+0LOQ75/SxjNaVdmLOhj oLAtzFOnY72HoJvd62fFNllU0AkpQuotp2Ajt7W9bHfvtxS8N2EXyShSdBqr1eLo zNwgeRqeU/uZmGVi5ehdgBTliQKeUs80mbnBwGzP6e/FUSVlXRXxyn4Pl3hclAD6 ZM3vP6vSCXIhM4LoI4c... -----END RSA PUBLIC KEY-----
1.3.2 二进制与十六进制互转
- 在Java世界中,经常要用到二进制数组(byte[]),但是在接口传输、存储时,又基本上没有,二进制去哪儿了?
- 除了上面说的Base64外,还经常用到把二进制数组转换成十六进制(Hex),比如说SHA512/SHA256/MD5等摘要,也需要把加密密文由二进制转成十六进制,解密时把十六进制反转成二进制,示例代码SecurityFacade 如下:
public class SecurityFacade extends BaseEncryptorFacade {/*** 本地不可逆加密或者hash** @param data 原始数据* @return 摘要数据*/@Overridepublic String hash(String data){byte[] encBytes = this.getEncryptSecurity().hash(data.getBytes(StandardCharsets.UTF_8));return Hex.toHexString(encBytes);}/*** 本地可逆加密** @param data 原始报文* @return 加密后的报文*/@Overridepublic String encrypt(String data){byte[] encBytes = this.getEncryptSecurity().encrypt(data.getBytes(StandardCharsets.UTF_8));return Hex.toHexString(encBytes);}/*** 本地解密* <p>* 与上面加密对应** @param data 加密后的数据* @return 解密后的数据*/@Overridepublic String decrypt(String data){byte[] decBytes = this.getEncryptSecurity().decrypt(Hex.decode(data));return new String(decBytes, StandardCharsets.UTF_8);} }
2.Java魔数应用
- 在了解Java底层基本上都是二进制数据之后,还有一个概念和二进制息息相关:
魔数
。魔数
是用于识别文件格式或者协议类型的一段常量或者字符串。 - 此处讲的魔数仅限于上述的命名规范,不涉及Java语言魔法数字的检验规则。
2.1 文件魔数
- 文件魔数就是文件二进制/十六进制数据的特定起始标识位,此处封装常用文件检验的FileType 代码如下:
public enum FileType {/*** JEPG.*/JPEG("FFD8FF"),/*** PNG.*/PNG("89504E47"),/*** GIF.*/GIF("47494638"),/*** TIFF.*/TIFF("49492A00"),/*** Windows Bitmap.*/BMP("424D"),/*** CAD.*/DWG("41433130"),/*** Adobe Photoshop.*/PSD("38425053"),/*** Rich Text Format.*/RTF("7B5C727466"),/*** XML.*/XML("3C3F786D6C"),/*** HTML.*/HTML("68746D6C3E"),/*** Email [thorough only].*/EML("44656C69766572792D646174653A"),/*** Outlook Express.*/DBX("CFAD12FEC5FD746F"),/*** Outlook (pst).*/PST("2142444E"),/*** MS Word/Excel(兼容格式).*/XLS_DOC("D0CF11E0"),/*** XLSX(excel新版本)*/XLSX("504B030414000600080000002100"),/*** DOCX(word新版本)*/DOCX("504B03041400060008000000210077"),/*** MS Access.*/MDB("5374616E64617264204A"),/*** WordPerfect.*/WPD("FF575043"),/*** Postscript.*/EPS("252150532D41646F6265"),/*** Adobe Acrobat.*/PDF("255044462D312E"),/*** Quicken.*/QDF("AC9EBD8F"),/*** Windows Password.*/PWL("E3828596"),/*** ZIP Archive.*/ZIP("504B0304"),/*** RAR Archive.*/RAR("52617221"),/*** Wave.*/WAV("57415645"),/*** AVI.*/AVI("41564920"),/*** Real Audio.*/RAM("2E7261FD"),/*** Real Media.*/RM("2E524D46"),/*** MPEG (mpg).*/MPG("000001BA"),/*** Quicktime.*/MOV("6D6F6F76"),/*** Windows Media.*/ASF("3026B2758E66CF11"),/*** MIDI.*/MID("4D546864");/*** 获取最长的二进制文件格式对应的内容** @return 文件格式对应的最长的二进制长度*/public static int getMaxBytes(){return MAX_LEN.get();}/*** 获取文件类型** @param data 文件二进制* @return 文件类型对象*/public static FileType getType(byte[] data){if (null == data){LOGGER.warn("unknown file type by stream.");return null;}//1.截取最长的文件格式的二进制位数int maxLen = Math.min(data.length, MAX_LEN.get());byte[] suffixBytes = Arrays.copyOf(data, maxLen);//2.把最长的长度的二进制转成十六进制String suffixTypes = Hex.toHexString(suffixBytes);for (FileType fileType : SORTED_TYPES){if (suffixTypes.toUpperCase(Locale.US).contains(fileType.type.toUpperCase(Locale.US))){LOGGER.info("current file type by stream:{}.", fileType.name());return fileType;}}LOGGER.warn("unknown file type by stream.");return null;}/*** 获取到十六进制的类型** @return 十六进制的类型*/public String getType(){return this.type;}@Overridepublic String toString(){return this.name().toLowerCase(Locale.US);}/*** 构造方法** @param type 文件类型*/FileType(String type){this.type = type;}/*** 最大长度的文件二进制数位*/private static final AtomicInteger MAX_LEN = new AtomicInteger();/*** 排序后的文件类型集合*/private static final List<FileType> SORTED_TYPES = Lists.newArrayList();/*** 日志句柄*/private static final Logger LOGGER = LoggerFactory.getLogger(FileType.class);/*** 文件类型*/private String type;//初始化static{//1.获取最长的文件格式的二进制长度int maxHexLen = 0;int maxBytesLen = 0;for (FileType fileType : values()){int len = fileType.type.length();if (len > maxHexLen){maxBytesLen = Hex.decode(fileType.type.getBytes(StandardCharsets.UTF_8)).length;}maxHexLen = Math.max(maxHexLen, len);}//2.把遍历的最长的二进制前缀长度保存下来MAX_LEN.set(maxBytesLen);//3.把所有格式枚举保存起来SORTED_TYPES.addAll(Lists.newArrayList(values()));//4.把所有的格式枚举重新排序(从长到短,为了避免文件格式存在包含而取错的情况,比如:docx格式应该包含了doc)Collections.sort(SORTED_TYPES, new Comparator<FileType>(){@Overridepublic int compare(FileType o1, FileType o2){return o2.type.length() - o1.type.length();}});} }
注意:
文件魔数
可能会存在长的16进制编码包含了短的16进制编码的情况,本逻辑判断文件格式时,会优先匹配长的16进制编码,这样就不会误判;- 为了避免大文件解析,其实并不需要解析出整个文件的二进制/十六进制内容,仅需解析大概前200个byte即可判断文件格式。
2.2 协议魔数
- 在实际项目中,编写SM2协议实现时,就存在基于协议魔数来判断秘钥是否压缩的情况,Sm2Encryption 代码如下:
public class Sm2Encryption extends BaseSingleSignature {@Overridepublic PublicKey toPubKey(byte[] pubKey){try{String hexKey = Hex.toHexString(pubKey);KeyFactory kf = KeyFactory.getInstance(ALGORITHM, this.getProvider());if (hexKey.startsWith(STANDARD_HEX_KEY_PREFIX)){return kf.generatePublic(new X509EncodedKeySpec(pubKey));}else{// 获取SM2相关参数X9ECParameters ecParam = GMNamedCurves.getByName(SM2_VERSION);// 将公钥HEX字符串转换为椭圆曲线对应的点ECCurve ecCurve = ecParam.getCurve();ECPoint ecPoint = ecCurve.decodePoint(pubKey);// 椭圆曲线参数规格ECParameterSpec ecSpec = new ECParameterSpec(ecCurve, ecParam.getG(), ecParam.getN(), ecParam.getH());// 将椭圆曲线点转为公钥KEY对象return kf.generatePublic(new ECPublicKeySpec(ecPoint, ecSpec));}}catch (Exception e){throw new EncryptionException("failed to get sm2 pub key.", e);}}/*** 标准的秘钥hex前缀*/private static final String STANDARD_HEX_KEY_PREFIX = "30"; }
3.Java Unicode编码应用
unicode与char的关系
- Unicode,全称为Unicode标准(The Unicode Standard),官方机构使用的中文名称为统一码;
- unicode是个编码方案,Java语言默认的UTF-8字符集就是unicode编码方案的一种实现,后面所有unicode编码均指UTF-8的unicode实现;
- UTF-8的unicode是变长的,可以由2个或者4个字节来表示1个字符;
- 1个字节需要2个16进制字符来表示;
结论: unicode是变长的,可以是2个字节,也可以是4个字节来表示一个字符,unicode也可以说是4/8个16进制位来表示;
GB18030-2022字符规范 正式生效在即,也有必要和大家好好分析下中文字符在UTF-8编码的Unicode规范应用;
UTF-8 unicode中文字符集 如下:
字符集 字数 Unicode 编码 基本汉字 20902字 4E00-9FA5 基本汉字补充 90字 9FA6-9FFF 扩展A 6592字 3400-4DBF 扩展B 42720字 20000-2A6DF 扩展C 4154字 2A700-2B739 扩展D 222字 2B740-2B81D 扩展E 5762字 2B820-2CEA1 扩展F 7473字 2CEB0-2EBE0 扩展G 4939字 30000-3134A 扩展H 4192字 31350-323AF 康熙部首 214字 2F00-2FD5 部首扩展 115字 2E80-2EF3 兼容汉字 472字 F900-FAD9 兼容扩展 542字 2F800-2FA1D 汉字笔画 36字 31C0-31E3 汉字结构 12字 2FF0-2FFB 汉语注音 43字 3105-312F 注音扩展 32字 31A0-31BF 〇 1字 3007
3.1 Java中文字符转换
- 专门写了个CharUtil 来处理汉字的unicode与汉字转换:
public final class CharUtil {/*** 把unicode转换成字符串** @param ch unicode字符* @return 字符对应的字符串显示*/public static String fromUnicode(char[] ch){StringBuilder result = new StringBuilder(StringUtils.EMPTY);for (char c : ch){result.append(fromUnicode(c));}return result.toString();}/*** 把unicode转换成字符串** @param ch unicode字符* @return 字符对应的字符串显示*/public static String fromUnicode(char ch){return StringUtils.EMPTY + ch;}/*** 获取范围内的连续字符集合** @param start 起始字符位置* @param end 结束字符位置* @return 字符对应的文本*/public static List<String> fromUnicode(char[] start, char[] end){List<String> lists = Lists.newArrayList();char minChar = start[start.length - Const.ONE];char maxChar = end[end.length - Const.ONE];boolean multiUnicode = (start.length > Const.ONE);for (int index = minChar; index <= maxChar; index++){char[] chars = {(char)index};if (multiUnicode){chars = new char[] {start[0], (char)index};}String ch = CharUtil.fromUnicode(chars);lists.add(ch);}return lists;}/*** 把字符转换成unicode** @param ch 字符* @return 字符对应的unicode*/public static String toUnicode(String ch){StringBuilder result = new StringBuilder(StringUtils.EMPTY);if (!StringUtils.isEmpty(ch)){char[] chars = ch.toCharArray();for (char c : chars){String hex = Integer.toHexString(c);int fillLen = CHAR_HEX_LEN - hex.length();if (fillLen > 0){hex = StringUtils.repeat(StringUtils.EMPTY + 0, fillLen) + hex;}result.append(UNICODE_TAG).append(hex);}}return result.toString();}/*** 是否是中文** @param ch 中文字符集* @return true表示是中文*/public static boolean isChinese(String ch){return RegexUtil.match(ch, CHINESE_REGEX);}private CharUtil(){}/*** 一个字符对应的16进制位个数*/private static final int CHAR_HEX_LEN = 4;/*** UNICODE起始标记*/private static final String UNICODE_TAG = "\\u";/*** 中文名字等字符串匹配表达式*/private static final String CHINESE_REGEX ="^([\\u3400-\\u4dbf\\u4e00-\\u9fff\\uf900-\\ufaff\\x{20000}-\\x{323af}\\u2180-\\u2ef3\\u2f00-\\u2fd5\\u2ff0-\\u2ffb\\u3105-\\u312f\\u31a0-\\u31bf\\u31c0-\\u31e3\\u3007·])+$"; }
- 根据上述工具类,专门验证下 unicode中文字符集 中的编码;
- 测试代码CharUtilTest 如下:
public class CharUtilTest {@Testpublic void toUnicode2(){char[] starts2 = {'\u9FA6'};char[] ends2 = {'\u9FFF'};toUnicode(starts2, ends2);}private void toUnicode(char[] starts, char[] ends){List<String> chars = CharUtil.fromUnicode(starts, ends);int i = 0;for (String ch : chars){System.out.println(String.format("[%s]%s=%s", i, ch, CharUtil.toUnicode(ch)));i++;}System.out.println("******************************************total=" + chars.size());} }
- 测试效果如下,与标准的数量一致:
[0]龦=\u9fa6 [1]龧=\u9fa7 [2]龨=\u9fa8 [3]龩=\u9fa9 [4]龪=\u9faa [5]龫=\u9fab [6]龬=\u9fac [7]龭=\u9fad [8]龮=\u9fae [9]龯=\u9faf [10]龰=\u9fb0 [11]龱=\u9fb1 [12]龲=\u9fb2 [13]龳=\u9fb3 [14]龴=\u9fb4 [15]龵=\u9fb5 [16]龶=\u9fb6 [17]龷=\u9fb7 [18]龸=\u9fb8 [19]龹=\u9fb9 [20]龺=\u9fba [21]龻=\u9fbb [22]龼=\u9fbc [23]龽=\u9fbd [24]龾=\u9fbe [25]龿=\u9fbf [26]鿀=\u9fc0 [27]鿁=\u9fc1 [28]鿂=\u9fc2 [29]鿃=\u9fc3 [30]鿄=\u9fc4 [31]鿅=\u9fc5 [32]鿆=\u9fc6 [33]鿇=\u9fc7 [34]鿈=\u9fc8 [35]鿉=\u9fc9 [36]鿊=\u9fca [37]鿋=\u9fcb [38]鿌=\u9fcc [39]鿍=\u9fcd [40]鿎=\u9fce [41]鿏=\u9fcf [42]鿐=\u9fd0 [43]鿑=\u9fd1 [44]鿒=\u9fd2 [45]鿓=\u9fd3 [46]鿔=\u9fd4 [47]鿕=\u9fd5 [48]鿖=\u9fd6 [49]鿗=\u9fd7 [50]鿘=\u9fd8 [51]鿙=\u9fd9 [52]鿚=\u9fda [53]鿛=\u9fdb [54]鿜=\u9fdc [55]鿝=\u9fdd [56]鿞=\u9fde [57]鿟=\u9fdf [58]鿠=\u9fe0 [59]鿡=\u9fe1 [60]鿢=\u9fe2 [61]鿣=\u9fe3 [62]鿤=\u9fe4 [63]鿥=\u9fe5 [64]鿦=\u9fe6 [65]鿧=\u9fe7 [66]鿨=\u9fe8 [67]鿩=\u9fe9 [68]鿪=\u9fea [69]鿫=\u9feb [70]鿬=\u9fec [71]鿭=\u9fed [72]鿮=\u9fee [73]鿯=\u9fef [74]鿰=\u9ff0 [75]鿱=\u9ff1 [76]鿲=\u9ff2 [77]鿳=\u9ff3 [78]鿴=\u9ff4 [79]鿵=\u9ff5 [80]鿶=\u9ff6 [81]鿷=\u9ff7 [82]鿸=\u9ff8 [83]鿹=\u9ff9 [84]鿺=\u9ffa [85]鿻=\u9ffb [86]鿼=\u9ffc [87]鿽=\u9ffd [88]鿾=\u9ffe [89]鿿=\u9fff ******************************************total=90
注意:在屏幕上能看到多少个字符,是和你的终端操作系统装的字库集有关系;
- 测试代码CharUtilTest 如下:
- unicode与中文字符互转验证
- 字符转换测试代码CharUtilTest :
public class CharUtilTest {@Testpublic void fromUnicode(){char ch = '\u77be';System.out.println(CharUtil.fromUnicode(ch) + "=" + CharUtil.toUnicode("" + ch));}@Testpublic void toUnicode(){String ch = "瞾";String format = "%s[%s]=%s=>%s";String unicode = CharUtil.fromUnicode(ch.toCharArray());System.out.println(String.format(format, ch, ch.toCharArray().length, CharUtil.toUnicode(ch), unicode));String ch2 = "\uD84C\uDC7E";String unicode2 = CharUtil.fromUnicode(ch2.toCharArray());System.out.println(String.format(format, ch2, ch2.toCharArray().length, CharUtil.toUnicode(ch2), unicode2));} }
- 验证效果依次如下:
瞾=\u77be —————————————————————————————— 瞾[1]=\u77be=>瞾
Java二进制及中文转码和校验相关推荐
- js传中文参数 java取_js中文转码传输java后台 适用于用url传递中文参数
第一种方法: 解决方法如下: 1.在JS里对中文参数进行两次转码 var login_name = document.getElementById("loginname").val ...
- java中String中文转码思路之一
@简单记录 开始是成功的一半,希望能坚持走下去! 今天在使用ResourceBundle读取配置文件的时候遇到了中文乱码的问题,后来使用 new String(str.getBytes("i ...
- java毕业生设计中文网络小说平台系统计算机源码+系统+mysql+调试部署+lw
java毕业生设计中文网络小说平台系统计算机源码+系统+mysql+调试部署+lw java毕业生设计中文网络小说平台系统计算机源码+系统+mysql+调试部署+lw 本源码技术栈: 项目架构:B/S ...
- JAVA计算机毕业设计中文网络小说平台系统Mybatis+源码+数据库+lw文档+系统+调试部署
JAVA计算机毕业设计中文网络小说平台系统Mybatis+源码+数据库+lw文档+系统+调试部署 JAVA计算机毕业设计中文网络小说平台系统Mybatis+源码+数据库+lw文档+系统+调试部署 本源 ...
- Java Servlet API中文说明文档
Java Servlet API中文说明文档 目 录 1.... Servet资料 1.1 绪言 1.2 谁需要读这份文档 1.3 Java Servlet API的组成 ...
- 在Unix/Linux上令(java)JVM支持中文输出
在Unix/Linux上令(java)JVM支持中文输出 一.在Unix/Linux上令JVM支持中文输出 如 果用户使用的是UNIX的远程服务器,就会遇到中文字体在图像中输出的问题,特别是由于许多管 ...
- Java生成CRC16数据校验码
CRC即循环冗余校验码(Cyclic Redundancy Check[1] ):是数据通信领域中最常用的一种查错校验码,其特征是信息字段和校验字段的长度可以任意选定.循环冗余检查(CRC)是一种数 ...
- Java HashSet和HashMap源码剖析
转载自 Java HashSet和HashMap源码剖析 总体介绍 之所以把HashSet和HashMap放在一起讲解,是因为二者在Java里有着相同的实现,前者仅仅是对后者做了一层包装,也就是说Ha ...
- Java中识别二维码并且提高二维码的识别率
我们在Java开发的时候,发现对二维码的识别是不足的.所以我们需要提高识别率. 第一步.识别图片二维码.准备相应的jar包.我们在gradle+idea中开发. compile group: 'com ...
最新文章
- 从0到1,一步步开发React的loading组件,并发布到npm上
- IIS部署asp.net core webapi
- SOE服务的地址构建注意点_大小写
- linux jdk1.7 tomcat mysql_RedHat Linux 下安装JDK 1.7+MySQL 5.0+Tomcat 7.0.27过程
- HashMap在java并发中如何发生死循环
- android夜间模式揭露动画,Android Material Design系列之夜间模式
- node.js的下载和安装
- root用户安装的软件在普通用户不生效
- 浮动元素经常和标准流父级搭配使用(HTML、CSS)
- php自己遇到的一些问题
- python分类识别_Python构建图像分类识别器的方法
- uniapp背景色跟随轮播图改变 vue
- c语言计算函数零点个数,遗传算法-求函数零点-C语言代码.doc
- 微信小程序服务端调用--小程序码 wxacode.getUnlimited 接口调用,实现微信扫码直接跳转小程序页面
- 让笔记本的无线网卡指示灯不再狂闪的方法
- Java Excel文件内容替换
- 聚观早报 | 嘀嗒出行重启赴港IPO;饿了么到店业务将与高德合并
- 系统进程里的edpa.exe是什么?
- C语言32位正整数作为id进程,SOJ4453 Excel列数 进制转换
- Java中jdk1.8和jdk17相互切换
热门文章
- 实践篇(三):D2RQ SPARQL endpoint与两种交互方式
- ROS launch file 写法
- android模拟器运行出现异常java.lang.UnsatisfiedLinkError,couldn't find libhyphenate.so
- java学习笔记day07 成员变量与局部变量、形式参数、匿名对象、封装、private、this、构造方法、类详细讲解、static
- 马斯克“大脑改造计划”,还需要突破哪几道技术门槛?
- 2016杭州网易Java开发工程师内推面试
- Android——手写字体识别程序
- Python实现K临近法(KNN)回归(村里最笨的小鸡都可以学会)阅读预计20分钟
- mac如何彻底卸载Anaconda
- 我的第一个Java项目——客户管理
- js传中文参数 java取_js中文转码传输java后台 适用于用url传递中文参数
- 字符转换测试代码CharUtilTest :