目录
问题来了~为什么通过记事本将ANSI编码格式转换为UTF-8编码格式后,出现错误: 非法字符: ‘\ufeff’
前言
小编最近在写javaDemo时,使用的是文本文档,后缀名为.java的形式,代码中因为有中文以及特殊符号存在,导致DOS编码后出现错误:“编码GBK 的不可映射字符”。
javaDemo代码:
/*
* 运算符之一:算术运算符
* + - * / % (前)++ (后)++ (前)-- (后)-- +(连接符)
*/
class Test {
public static void main(String[] args) {
//除号:/
int num1=12;
int num2=5;
int result=num1/num2;
System.out.println(result); // 2
int result2=num1/num2*num2;
System.out.println(result2); //10
double result3=num1/num2; //int转double
System.out.println(result3); //2.0
double result4=num1/num2+0.0; //2.0: int转double
double result5=num1/(num2+0.0); //2.4 : 12/5.0=2.4
System.out.println(result5);
double result6=(double)num1 / num2; //2.4:double12/int5=2.4
System.out.println(result6);
double result7=(double)(num1/num2); //2.0:int2转为double
System.out.println(result7);
}
}
使用javac命令编译,出现的问题:
一、透过现象看本质,发现问题找原因
既然问题描述显示为 java “错误:编码GBK 的不可映射字符”,那便确定应该是编码格式的原因。进一步思考,为什么会出现编码格式的问题呢?——查看cmd默认的编码格式与文件的编码格式是否一致?
1.查看cmd的默认编码格式:
- 通过cmd命令查看:chcp
- 通过属性查看:
通过两种方式查看cmd的默认编码格式都是936,936是什么呢?
CP936其实就是GBK,IBM在发明Code Page的时候将GBK放在第936页,所以叫CP936。
2.查看代码文件的保存格式:
通过记事本,打开代码文件:
查看代码文件的格式,发现文件的编码格式默认是UTF-8,与cmd的默认编码格式不同。
关于GBK和UTF-8编码格式的区别:
GBK全称《汉字内码扩展规范》(GBK即“国标”、“扩展”汉语拼音的第一个字母,英文名称:Chinese Internal Code Specification) ,中华人民共和国全国信息技术标准化技术委员会1995年12月1日制订,国家技术监督局标准化司、电子工业部科技与质量监督司1995年12月15日联合以技监标函1995 229号文件的形式,将它确定为技术规范指导性文件。这一版的GBK规范为1.0版。
而UTF-8: UTF-8(8-bit Unicode Transformation Format)是一种针对Unicode的可变长度字符编码,又称万国码。由Ken Thompson于1992年创建。现在已经标准化为RFC 3629。UTF-8用1到6个字节编码UNICODE字符。用在网页上可以同一页面显示中文简体繁体及其它语言(如英文,日文,韩文)。
所以GBK和UTF-8简单的来说,
区别就是编码方式不同,表示的文字范围不同。
二、分析问题原因,寻找解决方案
既然发现cmd默认的编码格式与文件的编码格式不一致,那便解决编码不一致的问题,可以从两方面入手:
1.指定DOS命令的编码格式:
根据代码文件的格式(UTF-8),指定DOS命令的编码格式为UTF-8:javac -encoding UTF-8 Test.java
编码通过:
2.修改代码文件的编码格式:
根据cmd的默认编码格式,记事本打开java源文件,另存为文件编码格式选择ANSI(
埋雷、踩坑!!!
):
再使用cmd默认的编码格式执行javac命令,编译成功:
说明:
ANSI:美国国家标准协会,系统预设的标准文字储存格式。简体中文编码GB2312,实际上它是ANSI的一个代码页936
UTF-8:通用字集转换格式,这是为传输而设计的编码,2进制,以8位为单元对Unicode进行编码,如果使用只能在同类位元组内支持8个位元的重要资料一类的旧式传输媒体,可选择UTF-8格式。
在UTF-8里,英文字符仍然跟ASCII编码一样,因此原先的函数库可以继续使用。而中文的编码范围是在0080-07FF之间,因此是2个字节表示(但这两个字节和GB编码的两个字节是不同的),用专门的Unicode处理类可以对UTF编码进行处理。
通过以上两种方式将javac执行命令的编码格式与文件编码格式统一,这样便可以成功编译!
三、后续——踩坑~
虽然通过
记事本打开java源文件,另存为文件编码格式选择ANSI,这种方式可以实现javac成功编译代码文件,但是会导致代码中的中文和特殊字符显示为乱码,如下图所示;
既然如此,那小编想把ANSI编码格式的文件重新另存为UTF-8,然后采取指定DOS为UTF-8的方式解决编码问题,结果出现了新的问题:
指定DOS命令的编码格式为UTF-8:javac -encoding UTF-8 Test.java,出现新的问题:
Test.java:1: 错误: 非法字符: ‘\ufeff’
问题来了~为什么通过记事本将ANSI编码格式转换为UTF-8编码格式后,出现
错误: 非法字符: ‘\ufeff’
寻找原因:通过不同的代码工具打开该文件,查看该文件的编码格式是否成功转换为UTF-8格式:
①通过Windows记事本打开,显示为UTF-8:
②通过IDEA打开该文件,显示为UTF-8:
③通
过Visual Studio Code 打开,发现该文件并没有转换成了UTF-8格式,而是UTF-8 with BOM格式:
为什么使用不同的编译器查看到的该文件编码格式不一致呢?
原来这是因为Windows记事本在修改UTF-8文件时自作聪明地在文件开头添加BOM导致的,而IDEA没有智能的把UTF-8+BOM文件转为普通的UTF-8文件的功能,所以在IDEA中显示的是UTF-8,但是IDEA却不能正确读取.java文件,从而程序出错。
而在VSCode中可以看到通过记事本将ANSI编码格式转换为UTF-8编码格式后,实际是转换为UTF-8 with BOM,这也证明了
通过记事本转换编码格式是存在问题的!!!
填坑~解决方案:
1.在编辑器IDEA中将文件编码更改为UTF-16,再改回UTF-8即可,其实就相当于刷新了一下文件编码。
2.直接通过VSCode或者其他可以区分UTF-8和UTF-8 with BOM的编译器(如EditPlus、Notepad++)修改文件编码格式为UTF-8,避免使用记事本(记事本存在编码转换问题!!!)。