1. 概述
非法字符编译错误本质是文件编码类型问题。当创建文件时使用了错误的编码方式,就会在Java等语言编译时触发此错误。本文将深入剖析该问题,分析常见踩坑场景,并提供解决方案。
2. 非法字符编译错误
2.1. 字节顺序标记(BOM)
理解BOM前需先了解UCS(Unicode)转换格式(UTF)。UTF是一种能编码所有Unicode字符的编码格式,其中UTF-8使用最广泛。
UTF-8采用8位变长编码,最大限度兼容ASCII。使用此编码时,文件可能以U+FEFF字节顺序标记(BOM)开头。正常情况下BOM不可见,但可能引发数据错误。
在UTF-8中,BOM并非必需。某些文本编辑器(如Windows记事本)会自动添加BOM,导致:
- 编译时出现非法字符错误
- 文件读取时产生不可见字符
现代IDE通常采用无BOM的UTF-8编码,可避免此问题。后续将通过示例说明。
2.2. 非法字符编译错误示例
虽然我们常用高级IDE,但有时会使用文本编辑器。某些编辑器保存文件时添加BOM,会导致Java编译报错。该错误在编译阶段即可发现,示例如下:
在记事本中创建测试类:
public class TestBOM {
public static void main(String ...args){
System.out.println("BOM Test");
}
}
使用javac
编译:
$ javac ./TestBOM.java
得到报错信息:
public class TestBOM {
^
.\TestBOM.java:1: error: illegal character: '\u00bf'
public class TestBOM {
^
2 errors
解决方案:
- 将文件另存为无BOM的UTF-8编码
- 使用
dos2unix
工具移除BOM - 始终检查文件编码是否包含BOM
3. 读取含BOM文件
下面分析读取含BOM文件的常见场景。首先创建测试文件,内容为"Hello world with BOM."。
3.1. 使用BufferedReader读取
@Test
public void whenInputFileHasBOM_thenUseInputStream() throws IOException {
String line;
String actual = "";
try (BufferedReader br = new BufferedReader(new InputStreamReader(file))) {
while ((line = br.readLine()) != null) {
actual += line;
}
}
assertEquals(expected, actual);
}
测试结果:
org.opentest4j.AssertionFailedError: expected: <Hello world with BOM.> but was: <Hello world with BOM.>
Expected :Hello world with BOM.
Actual :Hello world with BOM.
虽然字符串看起来相同,但实际值包含BOM字符。简单粗暴的解决方案:
@Test
public void whenInputFileHasBOM_thenUseInputStreamWithReplace() throws IOException {
String line;
String actual = "";
try (BufferedReader br = new BufferedReader(new InputStreamReader(file))) {
while ((line = br.readLine()) != null) {
actual += line.replace("\uFEFF", "");
}
}
assertEquals(expected, actual);
}
⚠️ 注意:处理大量文件时,replace方法可能影响性能。
3.2. 使用Apache Commons IO
Apache Commons IO库提供了BOMInputStream类,专门处理含BOM的文件:
@Test
public void whenInputFileHasBOM_thenUseBOMInputStream() throws IOException {
String line;
String actual = "";
ByteOrderMark[] byteOrderMarks = new ByteOrderMark[] {
ByteOrderMark.UTF_8, ByteOrderMark.UTF_16BE, ByteOrderMark.UTF_16LE, ByteOrderMark.UTF_32BE, ByteOrderMark.UTF_32LE
};
InputStream inputStream = new BOMInputStream(ioStream, false, byteOrderMarks);
Reader reader = new InputStreamReader(inputStream);
BufferedReader br = new BufferedReader(reader);
while ((line = br.readLine()) != null) {
actual += line;
}
assertEquals(expected, actual);
}
3.3. 使用Google Data (GData)
Google Data (GData)库是另一个处理BOM的有效工具(虽较老但实用):
@Test
public void whenInputFileHasBOM_thenUseGoogleGdata() throws IOException {
char[] actual = new char[21];
try (Reader r = new UnicodeReader(ioStream, null)) {
r.read(actual);
}
assertEquals(expected, String.valueOf(actual));
}
4. 总结
本文系统分析了Java中的非法字符编译错误:
- 核心问题:文件编码中的BOM导致编译/读取异常
- 常见场景:
- 使用记事本等编辑器创建Java文件
- 读取含BOM的文本文件
- 解决方案:
- 保存文件时选择无BOM的UTF-8编码
- 使用专业工具(如dos2unix)
- 采用Apache Commons IO或GData库处理文件读取
关键建议:始终检查文件编码,避免在代码中硬编码处理BOM。所有示例代码可在GitHub获取。