原文地址:http://blog.csdn.net/jackpk/article/details/5702964/

Java读取UTF-8的txt文件第一行出现乱码“?”及解决

test.txt文件内容:
A中
2国
3
4
5
6

test.txt文件采用写字板保存为UTF-8格式
保存并关闭后使用写字板再次打开该UTF-8文档,中文、字母正常显示

测试代码:

[java] view plaincopy
  1. import java.io.BufferedReader;
  2. import java.io.File;
  3. import java.io.FileInputStream;
  4. import java.io.InputStreamReader;
  5. public class ReadTxtFile {
  6. public static void main(String[] args) {
  7. try {
  8. String charsetName = "UTF-8";
  9. String path = "D:/to_delete/test.txt";
  10. File file = new File(path);
  11. if (file.isFile() && file.exists())
  12. {
  13. InputStreamReader insReader = new InputStreamReader(
  14. new FileInputStream(file), charsetName);
  15. BufferedReader bufReader = new BufferedReader(insReader);
  16. String line = new String();
  17. while ((line = bufReader.readLine()) != null) {
  18. System.out.println(line);
  19. }
  20. bufReader.close();
  21. insReader.close();
  22. }
  23. } catch (Exception e) {
  24. System.out.println("读取文件内容操作出错");
  25. e.printStackTrace();
  26. }
  27. }
  28. }

程序执行结果:
?A中
2国
3
4
5
6

我的解决办法:

使用UltraEdit将上边的txt文件另存为UTF-8无BOM格式;

或者

使用Notepad++打开上边的txt文件执行如下操作“格式-->以UTF-8无BOM格式编码”,修改后将txt文本进行保存。

网上有篇非常好的文章,论述了问题出现的原因及解决办法

Java读带有BOM的UTF-8文件乱码原因及解决方法

url:http://daimojingdeyu.javaeye.com/blog/397661

关键字: java 读utf-8, java写utf-8, 编码, utf-8 乱码

最近在处理文件时发现了同样类型的文件使用的编码可能是不同的。所以想将文件的格式统一一下(因为UTF-8的通用性,决定往UTF-8统一),遇见的第一个问题是:如何查看现有文件的编码方式。

上网找了一下,找到几篇比较好文章,这里就不转载啦把链接搞过来。 
文件编码问题集锦 
字符串编码(charset,encoding,decoding)问题原理 
Java编码浅析 
判定文件编码或文本流编码的方法 
上面的几篇文章可以看成认识编码问题的“从入门到精通”

如果你看完了上面的文章,一定了解到了,在java中,class文件采用utf8的编码方式,JVM运行时采用utf16。Java的字符串是永远都是unicode的,采用的是UTF-16的编码方式。

想测试一下,java对UTF-8文件的读写的能力,结果发现了一个很郁闷的问题,如果通过java写的UTF-8文件,使用Java可以正确的读,但是如果用记事本将相同的内容使用UTF-8格式保存,则在使用程序读取是会从文件中多读出一个不可见字符。 
测试代码如下:

Java代码 
  1. import java.io.BufferedReader;
  2. import java.io.File;
  3. import java.io.FileInputStream;
  4. import java.io.IOException;
  5. import java.io.InputStreamReader;
  6. public class UTF8Test {
  7. public static void main(String[] args) throws IOException {
  8. File f  = new File("./utf.txt");
  9. FileInputStream in = new FileInputStream(f);
  10. // 指定读取文件时以UTF-8的格式读取
  11. BufferedReader br = new BufferedReader(new InputStreamReader(in, "UTF-8"));
  12. String line = br.readLine();
  13. while(line != null)
  14. {
  15. System.out.println(line);
  16. line = br.readLine();
  17. }
  18. }
  19. }

utf.txt通过记事本创建,另存时使用指定utf-8编码,其内容为:

引用

This is the first line. 
This is second line. 

正常的测试结果应该是直接输出utf.txt的文本内容。可是实际上却输出了下面的内容:

引用

?This is the first line. 
This is second line. 

第一行多出了一个问号。 
通过上面的几篇文章应该可以想到是Java读取BOM(Byte Order Mark)的问题,在使用UTF-8时,可以在文件的开始使用3个字节的"EF BB BF"来标识文件使用了UTF-8的编码,当然也可以不用这个3个字节。 
上面的问题应该就是因为对开头3个字节的读取导致的。开始不太相信这个是JDK的Bug,后来在多次试验后,问题依然存在,就又狗狗了一下,果然找到一个如下的Bug: 
Bug ID:4508058 
不过在我关掉的一些页面中记得有篇文件说这个bug只在jdk1.5及之前的版本才有,说是1.6已经解决了,从目前来看1.6只是解决了读取带有BOM文件失败的问题,还是不能区别处理有BOM和无BOM的UTF-8编码的文件,从Bug ID:4508058里的描述可以看出,这个问题将作为一个不会修改的问题关闭,对于BOM编码的识别将由应用程序自己来处理,原因可从另处一个bug处查看到,因为Unicode对于BOM的编码的规定可能发生变化。也就是说对于一个UTF-8的文件,应用程序需要知道这个文件有没有写BOM,然后自己决定处理BOM的方式。

在上面的while循环中可加入下面的代码,测试一下读出内容:

Java代码 
  1. byte[] allbytes = line.getBytes("UTF-8");
  2. for (int i=0; i < allbytes.length; i++)
  3. {
  4. int tmp = allbytes[i];
  5. String hexString = Integer.toHexString(tmp);
  6. // 1个byte变成16进制的,只需要2位就可以表示了,取后面两位,去掉前面的符号填充
  7. hexString = hexString.substring(hexString.length() -2);
  8. System.out.print(hexString.toUpperCase());
  9. System.out.print(" ");
  10. }

输出结果如下:

引用

EF BB BF 54 68 69 73 20 69 73 20 74 68 65 20 66 69 72 73 74 20 6C 69 6E 65 2E 
?This is the first line. 
54 68 69 73 20 69 73 20 73 65 63 6F 6E 64 20 6C 69 6E 65 2E 
This is second line.

红色部分的"EF BB BF"刚好是UTF-8文件的BOM编码,可以看出Java在读文件时没能正确处理UTF-8文件的BOM编码,将前3个字节当作文本内容来处理了。

使用链接中提供的代码可以解决碰到的乱码问题: 
http://koti.mbnet.fi/akini/java/unicodereader/

修改测试代码中的输入流后:

Java代码 
  1. BufferedReader br = new BufferedReader(new UnicodeReader(in, Charset.defaultCharset().name()));

执行,可以看到正确的结果。

将用到的测试代码及UTF-8读取乱码解决(http://koti.mbnet.fi/akini/java/unicodereader)的源码放在了附件中

  • code.zip (3 KB)
  • 下载次数: 411

压缩包中的源代码

UnicodeInputStream.java

[java] view plaincopy
  1. /**
  2. version: 1.1 / 2007-01-25
  3. - changed BOM recognition ordering (longer boms first)
  4. Original pseudocode   : Thomas Weidenfeller
  5. Implementation tweaked: Aki Nieminen
  6. http://www.unicode.org/unicode/faq/utf_bom.html
  7. BOMs in byte length ordering:
  8. 00 00 FE FF    = UTF-32, big-endian
  9. FF FE 00 00    = UTF-32, little-endian
  10. EF BB BF       = UTF-8,
  11. FE FF          = UTF-16, big-endian
  12. FF FE          = UTF-16, little-endian
  13. Win2k Notepad:
  14. Unicode format = UTF-16LE
  15. ***/
  16. import java.io.*;
  17. /**
  18. * This inputstream will recognize unicode BOM marks and will skip bytes if
  19. * getEncoding() method is called before any of the read(...) methods.
  20. *
  21. * Usage pattern: String enc = "ISO-8859-1"; // or NULL to use systemdefault
  22. * FileInputStream fis = new FileInputStream(file); UnicodeInputStream uin = new
  23. * UnicodeInputStream(fis, enc); enc = uin.getEncoding(); // check and skip
  24. * possible BOM bytes InputStreamReader in; if (enc == null) in = new
  25. * InputStreamReader(uin); else in = new InputStreamReader(uin, enc);
  26. */
  27. public class UnicodeInputStream extends InputStream {
  28. PushbackInputStream internalIn;
  29. boolean isInited = false;
  30. String defaultEnc;
  31. String encoding;
  32. private static final int BOM_SIZE = 4;
  33. UnicodeInputStream(InputStream in, String defaultEnc) {
  34. internalIn = new PushbackInputStream(in, BOM_SIZE);
  35. this.defaultEnc = defaultEnc;
  36. }
  37. public String getDefaultEncoding() {
  38. return defaultEnc;
  39. }
  40. public String getEncoding() {
  41. if (!isInited) {
  42. try {
  43. init();
  44. } catch (IOException ex) {
  45. IllegalStateException ise = new IllegalStateException(
  46. "Init method failed.");
  47. ise.initCause(ise);
  48. throw ise;
  49. }
  50. }
  51. return encoding;
  52. }
  53. /**
  54. * Read-ahead four bytes and check for BOM marks. Extra bytes are unread
  55. * back to the stream, only BOM bytes are skipped.
  56. */
  57. protected void init() throws IOException {
  58. if (isInited)
  59. return;
  60. byte bom[] = new byte[BOM_SIZE];
  61. int n, unread;
  62. n = internalIn.read(bom, 0, bom.length);
  63. if ((bom[0] == (byte) 0x00) && (bom[1] == (byte) 0x00)
  64. && (bom[2] == (byte) 0xFE) && (bom[3] == (byte) 0xFF)) {
  65. encoding = "UTF-32BE";
  66. unread = n - 4;
  67. } else if ((bom[0] == (byte) 0xFF) && (bom[1] == (byte) 0xFE)
  68. && (bom[2] == (byte) 0x00) && (bom[3] == (byte) 0x00)) {
  69. encoding = "UTF-32LE";
  70. unread = n - 4;
  71. } else if ((bom[0] == (byte) 0xEF) && (bom[1] == (byte) 0xBB)
  72. && (bom[2] == (byte) 0xBF)) {
  73. encoding = "UTF-8";
  74. unread = n - 3;
  75. } else if ((bom[0] == (byte) 0xFE) && (bom[1] == (byte) 0xFF)) {
  76. encoding = "UTF-16BE";
  77. unread = n - 2;
  78. } else if ((bom[0] == (byte) 0xFF) && (bom[1] == (byte) 0xFE)) {
  79. encoding = "UTF-16LE";
  80. unread = n - 2;
  81. } else {
  82. // Unicode BOM mark not found, unread all bytes
  83. encoding = defaultEnc;
  84. unread = n;
  85. }
  86. // System.out.println("read=" + n + ", unread=" + unread);
  87. if (unread > 0)
  88. internalIn.unread(bom, (n - unread), unread);
  89. isInited = true;
  90. }
  91. public void close() throws IOException {
  92. // init();
  93. isInited = true;
  94. internalIn.close();
  95. }
  96. public int read() throws IOException {
  97. // init();
  98. isInited = true;
  99. return internalIn.read();
  100. }
  101. }

UnicodeReader.java

[java] view plaincopy
  1. /**
  2. version: 1.1 / 2007-01-25
  3. - changed BOM recognition ordering (longer boms first)
  4. Original pseudocode   : Thomas Weidenfeller
  5. Implementation tweaked: Aki Nieminen
  6. http://www.unicode.org/unicode/faq/utf_bom.html
  7. BOMs:
  8. 00 00 FE FF    = UTF-32, big-endian
  9. FF FE 00 00    = UTF-32, little-endian
  10. EF BB BF       = UTF-8,
  11. FE FF          = UTF-16, big-endian
  12. FF FE          = UTF-16, little-endian
  13. Win2k Notepad:
  14. Unicode format = UTF-16LE
  15. ***/
  16. import java.io.*;
  17. /**
  18. * Generic unicode textreader, which will use BOM mark to identify the encoding
  19. * to be used. If BOM is not found then use a given default or system encoding.
  20. */
  21. public class UnicodeReader extends Reader {
  22. PushbackInputStream internalIn;
  23. InputStreamReader internalIn2 = null;
  24. String defaultEnc;
  25. private static final int BOM_SIZE = 4;
  26. /**
  27. *
  28. * @param in
  29. *            inputstream to be read
  30. * @param defaultEnc
  31. *            default encoding if stream does not have BOM marker. Give NULL
  32. *            to use system-level default.
  33. */
  34. UnicodeReader(InputStream in, String defaultEnc) {
  35. internalIn = new PushbackInputStream(in, BOM_SIZE);
  36. this.defaultEnc = defaultEnc;
  37. }
  38. public String getDefaultEncoding() {
  39. return defaultEnc;
  40. }
  41. /**
  42. * Get stream encoding or NULL if stream is uninitialized. Call init() or
  43. * read() method to initialize it.
  44. */
  45. public String getEncoding() {
  46. if (internalIn2 == null)
  47. return null;
  48. return internalIn2.getEncoding();
  49. }
  50. /**
  51. * Read-ahead four bytes and check for BOM marks. Extra bytes are unread
  52. * back to the stream, only BOM bytes are skipped.
  53. */
  54. protected void init() throws IOException {
  55. if (internalIn2 != null)
  56. return;
  57. String encoding;
  58. byte bom[] = new byte[BOM_SIZE];
  59. int n, unread;
  60. n = internalIn.read(bom, 0, bom.length);
  61. if ((bom[0] == (byte) 0x00) && (bom[1] == (byte) 0x00)
  62. && (bom[2] == (byte) 0xFE) && (bom[3] == (byte) 0xFF)) {
  63. encoding = "UTF-32BE";
  64. unread = n - 4;
  65. } else if ((bom[0] == (byte) 0xFF) && (bom[1] == (byte) 0xFE)
  66. && (bom[2] == (byte) 0x00) && (bom[3] == (byte) 0x00)) {
  67. encoding = "UTF-32LE";
  68. unread = n - 4;
  69. } else if ((bom[0] == (byte) 0xEF) && (bom[1] == (byte) 0xBB)
  70. && (bom[2] == (byte) 0xBF)) {
  71. encoding = "UTF-8";
  72. unread = n - 3;
  73. } else if ((bom[0] == (byte) 0xFE) && (bom[1] == (byte) 0xFF)) {
  74. encoding = "UTF-16BE";
  75. unread = n - 2;
  76. } else if ((bom[0] == (byte) 0xFF) && (bom[1] == (byte) 0xFE)) {
  77. encoding = "UTF-16LE";
  78. unread = n - 2;
  79. } else {
  80. // Unicode BOM mark not found, unread all bytes
  81. encoding = defaultEnc;
  82. unread = n;
  83. }
  84. // System.out.println("read=" + n + ", unread=" + unread);
  85. if (unread > 0)
  86. internalIn.unread(bom, (n - unread), unread);
  87. // Use given encoding
  88. if (encoding == null) {
  89. internalIn2 = new InputStreamReader(internalIn);
  90. } else {
  91. internalIn2 = new InputStreamReader(internalIn, encoding);
  92. }
  93. }
  94. public void close() throws IOException {
  95. init();
  96. internalIn2.close();
  97. }
  98. public int read(char[] cbuf, int off, int len) throws IOException {
  99. init();
  100. return internalIn2.read(cbuf, off, len);
  101. }
  102. }

UTF8Test.java

[java] view plaincopy
  1. import java.io.BufferedReader;
  2. import java.io.File;
  3. import java.io.FileInputStream;
  4. import java.io.IOException;
  5. import java.io.InputStreamReader;
  6. import java.nio.charset.Charset;
  7. public class UTF8Test {
  8. public static void main(String[] args) throws IOException {
  9. File f  = new File("./utf.txt");
  10. FileInputStream in = new FileInputStream(f);
  11. // 指定读取文件时以UTF-8的格式读取
  12. //      BufferedReader br = new BufferedReader(new InputStreamReader(in, "UTF-8"));
  13. BufferedReader br = new BufferedReader(new UnicodeReader(in, Charset.defaultCharset().name()));
  14. String line = br.readLine();
  15. while(line != null)
  16. {
  17. System.out.println(line);
  18. line = br.readLine();
  19. }
  20. }
  21. }

第二篇文章:

Java读取utf-8文件注意有无BOM

原文地址:http://53873039oycg.iteye.com/blog/2030463

各位看标题就知道我要写什么了,先写结论,读取utf-8格式的文件时候,注意文件开头可能含有BOM标识符,结论写完了,下面的没必要看了,我记录下问题解决方法而已。

虽然早知道utf-8文件可能含有bom标识符,我一直没碰到过,知道今天,我测试时候发现字符串长度不对劲,如下所示:


    长度不对劲,我就开始怀疑我碰上了传说中的BOM标识符了,下面可是验证,上代码:

Java代码  
  1. public class UTFBOM文件处理 {
  2. public static void main(String[] args) throws Exception {
  3. List<String> resultList = readFileByLine("f:/saveFile/temp/name2.txt");
  4. String tmpStr = resultList.get(0);
  5. System.out.println(tmpStr + "----len=" + tmpStr.length());
  6. String tmpStr2 = new String(tmpStr.substring(0, 1));
  7. System.out.println(tmpStr2 + "----hex=" + strtoHex(tmpStr2));
  8. }
  9. public static String strtoHex(String s) {
  10. String str = "";
  11. for (int i = 0; i < s.length(); i++) {
  12. int ch = (int) s.charAt(i);
  13. String s4 = Integer.toHexString(ch);
  14. str = str + s4;
  15. }
  16. return "0x" + str;// 0x表示十六进制
  17. }
  18. public static List<String> readFileByLine(String filename) throws Exception {
  19. List<String> nameList = new ArrayList<String>();
  20. File file = new File(filename);
  21. InputStreamReader isr = new InputStreamReader(
  22. new FileInputStream(file), "UTF-8");
  23. BufferedReader reader = new BufferedReader(isr);
  24. String tmp = reader.readLine();
  25. while (tmp != null && tmp.trim().length() > 0) {
  26. nameList.add(tmp);
  27. tmp = reader.readLine();
  28. }
  29. reader.close();
  30. return nameList;
  31. }
  32. }

结果为:

Java代码  
  1. 掬水月在手----len=6
  2. ----hex=0xfeff

看到好熟悉的feff。

使用16进制打开文件,可以看到BOM标识符了。


    BOM是什么,可以参考链接:

http://mindprod.com/jgloss/bom.html

ef bb bf

写道
UTF-8 endian, strictly speaking does not apply, though it uses big-endian most-significant-bytes first representation.

解决方法:

http://koti.mbnet.fi/akini/java/unicodereader/

Java代码  
  1. public static List<String> readFileByLineWithOutBom(String filename)
  2. throws Exception {
  3. List<String> nameList = new ArrayList<String>();
  4. File file = new File(filename);
  5. InputStream in = new FileInputStream(file);
  6. BufferedReader reader = new BufferedReader(new UnicodeReader(in,
  7. "utf-8"));
  8. String tmp = reader.readLine();
  9. while (tmp != null && tmp.trim().length() > 0) {
  10. nameList.add(tmp);
  11. tmp = reader.readLine();
  12. }
  13. reader.close();
  14. return nameList;
  15. }

带BOM标识符的文件怎么产生的呢?

系统默认另存为utf-8保存的就是带bom的,以前一直用nodepad++另存为的,今天犯二了,忘了。


    全文完。

Java读取UTF-8格式txt文件第一行出现乱码及解决;Java读带有BOM的UTF-8文件乱码原因及解决方法(转载)...相关推荐

  1. txt doc rtf html,JAVA读取WORD,EXCEL,PDF,TXT,RTF,HTML文件文本内容的方法示例.docx

    JAVA读取WORD,EXCEL,PDF,TXT,RTF,HTML文件文本内容的方法示例 JAVA读取WORD,EXCEL,PDF,TXT,RTF,HTML文件文本内容的方法示例??2012-06-2 ...

  2. Java读带有BOM的UTF-8文件乱码原因及解决方法(转)

    转载:http://www.linuxidc.com/Linux/2012-12/76707.htm 最近在处理文件时发现了同样类型的文件使用的编码可能是不同的.所以想将文件的格式统一一下(因为UTF ...

  3. Java读带有BOM的UTF-8文件乱码原因及解决方法

    Java读带有BOM的UTF-8文件乱码原因及解决方法 Java读带有BOM的UTF-8文件乱码原因及解决方法 - daimojingdeyu - ITeye技术网站 Java读带有BOM的UTF-8 ...

  4. 解决pom文件第一行报错(unknown)-亲测有效

    原文链接:https://blog.csdn.net/u010947534/article/details/93743582 问题: Eclipse导入maven项目时,或者新建一个springboo ...

  5. eclipse中maven项目pom文件第一行报错解决方法

    eclipse中maven项目pom文件第一行报错解决方法 参考文章: (1)eclipse中maven项目pom文件第一行报错解决方法 (2)https://www.cnblogs.com/wxym ...

  6. linux编写脚本读取txt文件,用bash脚本或者批处理 bat脚本 读取一个目录下.txt文件第一行内容存放到新文件a.txt...

    起因:我把cmd 控制台说成了dos! 发展:人类生气了,跟我扯了一堆 dos 还有什么玩意儿,在我第二次还说那个是dos界面以后:( 后续:人类要我写 批处理bat 还有linux bash 脚本去 ...

  7. java读取utf-8文件第一行多一个问号

    2019独角兽企业重金招聘Python工程师标准>>> 之前写了一个偏低层的项目,需要解压jar=>修改jar文件>打包jar 所以会经常io读写文件,当时还不清楚utf ...

  8. Java读取修改xlsm格式表格_Android Excel电子表格API – 在Android应用程序中读取编辑XLS CSV XLSX XLSM HTML格式...

    Android Excel Spreadsheet API 更多高级特征 具备格式化工作表,行,列,单元格等能力 Array,ArrayList 和 Recordset / Resultset数据导入 ...

  9. python读取csv文件第一行_尝试读取CSV文件的第一行返回['/']

    我通过Django上传了一个CSV文件,我试着读它的第一行.文件存储在服务器上的/tmp/csv_file/test.csv 文件如下所示: ^{pr2}$ 我正在尝试获取文件的标题,例如:absol ...

最新文章

  1. mysql 按照范围选择_选择MySQL范围内的特定行?
  2. mysql回表_到底什么情况下mysql innodb会发生回表操作?
  3. iframe引用页面中的js操作父窗口标签
  4. leetcode -- 279. Perfect Squares
  5. 如何查看某个employee被assign到了哪个sales organization上
  6. 大工14秋《c/c++语言程序设计》在线作业三,大工《CC++语言程序设计》课程考试模拟试卷A...
  7. Synergy,一个软件团队质量改进之路之一 --- 规划
  8. 【BZOJ】1579: [Usaco2009 Feb]Revamping Trails 道路升级
  9. Win10电脑如何查看本机mac地址
  10. hdu 1908数据结构水题
  11. 台式计算机诞生,生物电脑 ①1946年,世界上第一台计算机诞生,这个庞然大物开创了一个电脑时代。从那时候起,人们不断革新、创造,把庞然大物的体积一减再减,现在我们所见的台式电脑、掌上电脑...
  12. 关于GraphEasy的简单捣腾
  13. python爬取基金净值_Python爬虫周记之案例篇——基金净值爬取(上)
  14. java编写个人所得税_个人所得税JAVA算法
  15. python chr黑方格_Python中的chr()
  16. nifty bootstrap_nifty admin_nifty admin 下载-一个高端的bootstrap后台管理模板
  17. 【数据结构】格洛克怎么上膛?栈的结构帮你解答!
  18. 计算机主板尺寸,电脑主板大中小三个等级的尺寸是多少?
  19. 太阳能灭蚊灯方案评估
  20. matlab 实验七 低层绘图操作,matlab实验内容答案

热门文章

  1. Idea 2021新建项目没有spring选项
  2. Monkey King【大根堆】
  3. 数据库设计-SQL Server开发实现学习
  4. PHP7.2与apache环境安装部署详细流程
  5. NBD Network Block Device
  6. Android 手机自动化测试工具有哪几种?
  7. 简述锂离子电池的分类及结构
  8. Fiddler4 手机抓包
  9. Update From 用法
  10. 老笔记本机械硬盘换固态装系统,再战10年!