因为编程的原因,经成会遇到字符编码的问题。如开发工具使用的是UTF-8编码(推荐使用),然后需要导入一个从其它地方获取的工程项目,但是这个工程使用了GBK编码方式。这就导致了一个常见的问题——乱码。 这里对于文本文件要有一个理解,任何文件本质上都是字节文件,只不过文本文件使用了字符集编码。也就是说,打开文件显示的字符并不是文件里面存储的真实字符,文件里面存储的永远只是字节。 大致可以按照下图这样来比较简单理解这个过程,打开文件的过程是一个字符映射的过程。 从字节到符号的过程是解码,从符号到字节的过程是编码。 这里通过一段代码来演示编解码过程,理解这个话的,基本上就知道上面所说的东西了。 运行结果 这个结果是不是很有趣呢! 0xc1fa(字节)——>GBK解码——>龙(字符) 一个很自然的想法是,如果把上面这两个过程合起来,不就可以完成不同字符集编码的转换了吗? 通过上面的演示可以看出来,想要实现编码的转换,就是先将字节解码(本来的编码方式)为符号,再编码(希望的编码)为字节。 即:字节——解码——字符——编码——字节。 首先提供这样的两个文件,因为我的window的cmd使用是默认的编码,应该是GBK,但是我的这个两个文件都是UTF-8编码的,所以,这里打开是乱码。你也不知道里面写的是啥了。 简要说明 这里无论是读取还是写入都必须指定编码集合,否则是会使用系统默认的编码集,那样也是会出现问题的,因此建议对于任何的文本文件读取和写入都应该显示的指定编码集,以杜绝因此导致的问题。 例如:我最开始没有指定写入文件时的编码集,因为IDE使用的是UTF-8,但是我写入的是GBK编码的内容(我这里只是测试 UTF-8 转 GBK),导致打开文件仍然是乱码。这是一个需要注意的地方。! 这里我就不在cmd里面测试了,我直接使用IDE来测试了,注意下面的五个输入参数,以空格来分隔。 测试输出信息 测试输出文件是否转换成功 这里只选取Test1.java来测试即可 。 自己动手实现想法的过程中,也学到了不少东西,因为编码的过程中会遇到一些问题,可能某些概念掌握的不清楚,或者自认为掌握了,实现起来的时候,才会发现,其实不是自己想的那么回事,哈哈! 这是我最开始认为的实现方式,我还沾沾自喜呢。我以为我先按照srcCharset的编码方式解码,再将其按照desCharset的编码方式编码就行了。当时如果你理解上面的话,就会发现这个根本就是不对的嘛! 结果是得到了,完全的乱码文件了,使用notepad++怎么转换也不行了。(当然了,如果再反过来使用代码,也许还有救吧!) 后来我反应过来了(思索了一会儿),只需要 我以前一直对这个编码集和字节的关系感到很好奇,当我开始学习Java的IO流的时候,乱码伴随了我好久,例如一开始遇到:UTF-8 BOM 问题时,网上有人回答的是无法解决,我当时还是相信的了(可见错误的答案对人的误解),当时还是坚持去查找,最后发现了这个BOM这个关键字,也就解决了开头的那个文件内容输出开头的那个 ? 问题。编码转换本身是包含很多细枝末节的知识的,就算是现在的我也只是了解到了一些皮毛的知识,这些暂时是够用了,但是还是需要继续学习才行。 这个程序还可以继续完善,我这里没有涉及文件夹嵌套,例如一个目录里面含有多个文件夹,每个文件夹还含有需要转换的文件。对于通常的项目工程来说,很多的包是必备的,所以对于这种类型的转换直接使用上面的代码是不行的。上面的代码只是一个简单的技术实现,如果想要适应更复杂的情况,还需要继续完善,例如加一个递归就更完美了。有时间的话,也许会补上吧!
前言 preface
虽然这里的代码是不影响,但是中文注释全部都乱码了,这可不好玩了,很影响对于代码的阅读,尤其是那种比较多源文件的项目。通常,我的处理方式就是,对于每一个文件,使用记事本依次另存为另一种编码或者使用Notepad++的编码转换。这里推荐一下Notepad++,编码转换使用还是比较方便的。但是一个一个的转换,这都是重复性的操作,有时候也是挺烦的。所以,就萌生了一个使用代码来解决的想法。虽然是一个看似很简单的问题,但是其实是很考验我们对于基础知识的掌握的。想法 idea
我们看到的字符是字节经过编码映射,然后机器将特定的字符显示到屏幕上面。例如下面这副图片,以“龙”字符为例说明。
对于不同的编码集,字符”龙“具有不同的编码值,这里编码值指的是编码字节的值。
注意这里这个Unicode编码不是龙字的UTF-8编码,这里有点误导人了,但是我们可以去其它地方查询龙字的UTF-8编码,或者也不用舍近求远,如果你了解这方面的知识的话,直接使用代码输出就行了。演示 demo
这段代码需要一些关于字符、字节和编码的基础知识。package dragon; import java.io.ByteArrayOutputStream; import java.math.BigInteger; import java.nio.charset.Charset; public class TestMain { public static void main(String[] args) { int 龙_GBK = 0xC1FA; int 龙_UTF8 = 0xe9be99; ByteArrayOutputStream out = new ByteArrayOutputStream(); out.write((龙_GBK >>> 8) & 0xFF); //依次将GBK编码的龙和UTF-8编码的龙写入输出流,这里由于GBK out.write((龙_GBK >>> 0) & 0xFF); //是双字节编码, 因此只写入低两位即可, out.write((龙_UTF8 >>> 16) & 0xFF); //UTF-8是可变长的编码方式,这里的话 龙 是三字节编码。 out.write((龙_UTF8 >>> 8) & 0xFF); out.write((龙_UTF8 >>> 0) & 0xFF); byte[] data = out.toByteArray(); Charset GBK = Charset.forName("GBK"); Charset UTF_8 = Charset.forName("UTF-8"); System.out.println("不同编码显示同一字符:"); System.out.println("使用GBK编码表示的汉字: 龙--->" + new String(data, 0, 2, GBK)); System.out.println("使用UTF-8编码表示的汉字:龙--->" + new String(data, 2, 3, UTF_8)); System.out.println("同一字符编码为不同字符集表示:"); System.out.println("龙 使用GBK编码表示:" + new BigInteger(1, "龙".getBytes(GBK)).toString(16)); System.out.println("龙 使用UTF-8编码表示:" + new BigInteger(1, "龙".getBytes(UTF_8)).toString(16)); } }
小结 brief summary
龙(字符)——>UTF-8编码——>0xe9be99(字节)
当然了,注意前提是需要转换的部分的字符在两个编码集中都是存在的。毕竟你是无法把一个字符集中不存在的字符映射过去。
注意解码和编码对应的字符集不同,所以原始字节和最终生成的字节是不同的。
这里画的图是从UTF-8到GBK的转换,当然了,也可以反过来的。实现 implement
测试文件 test file
最终代码 final code
package dragon; import java.io.BufferedReader; import java.io.BufferedWriter; import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStreamReader; import java.io.OutputStreamWriter; import java.nio.charset.Charset; import java.util.Objects; import java.util.stream.Stream; /** * 编码转换 * UTF_8 * GBK * */ public class EncodeTranslate { private File[] files; //需要转码的文件名字符串 private File desDir; //转码后存放的文件目录 private Charset srcCharset; //文件当前编码 private Charset desCharset; //文件目标编码 /** * @param path 目标文件的路径 * @Param desPath 目的文件的存放路径 * @param type 目标文件的类型 * @param srcEncode 目标文件的编码 * @param desEncode 目的文件的编码 * * */ public EncodeTranslate(String path, String desPath, String type, String srcEncode, String desEncode) throws FileNotFoundException { File dirFile = new File(path); if (!dirFile.exists()) { throw new FileNotFoundException("文件路径不存在:" + path); } this.files = dirFile.listFiles((dir, name)->name.contains(type)); if (this.files.length == 0) { throw new FileNotFoundException("文件路径下不存在相关类型的文件:" + path + "->" + type); } desDir = new File(desPath); if (!desDir.exists()) { //目标文件夹不存在,就创建 if (!desDir.mkdirs()) { //目标文件夹创建失败,路径冲突 throw new FileNotFoundException("无法创建目标路径:" + desPath); } } else { if (!desDir.isDirectory()) { throw new FileNotFoundException("目标路径不是文件夹"); } } this.srcCharset = Charset.forName(srcEncode); this.desCharset = Charset.forName(desEncode); } /** * java 8 的 Stream 的简单应用 * */ public void encodeTranslate() { Stream.of(files).forEach(this::translate); } /** * 把需要转码文件的中字符串按行读取出来,然后对其使用它本身的字符集解码成字节数组, * 再对字节数组按照需要的字符集编码成字符串,然后写入目的文件中。 * */ public void translate(File file) { try (BufferedReader reader = new BufferedReader(new InputStreamReader(new FileInputStream(file), srcCharset))) { String fileName = file.getName(); File newFile = new File(desDir, fileName); try (BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(newFile), desCharset))) { String line = null; while (Objects.nonNull(line = reader.readLine())) { byte[] data = line.getBytes(desCharset); // 完成转码最为关键的两步 String newLine = new String(data, desCharset); writer.write(newLine); writer.newLine(); } } } catch (FileNotFoundException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } } } package dragon; import java.io.FileNotFoundException; public class Main { public static void main(String[] args) throws FileNotFoundException { if (args.length != 5) { System.out.println("请正确输入各个参数!"); return ; } String path = args[0]; String desPath = args[1]; String type = args[2]; String srcEncode = args[3]; String desEncode = args[4]; // 直接抛出异常,不做处理! EncodeTranslate encodeTranslate = new EncodeTranslate(path, desPath, type, srcEncode, desEncode); encodeTranslate.encodeTranslate(); System.out.println("File Encode Translate Success!"); } }
//使用指定的编码集读取文件 BufferedReader reader = new BufferedReader(new InputStreamReader(new FileInputStream(file), srcCharset)) //使用指定的编码集写入文件 BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(newFile), desCharset))
测试输入 test input
或者你也可以在cmd里面使用,也挺方便的。
测试结果 test result
测试输出文件
总结 summary
byte[] data = line.getBytes(srcCharset); // 完成转码最为关键的两步 String newLine = new String(data, desCharset);
byte[] data = line.getBytes(desCharset);
这样即可,把字符直接转成desCharset编码即可。并且对于这个字符、编码、字节的概念理解又多了一点,这才是写这篇博客最大的收获吧!
随便提一下,这里我主要使用的 InputStreamReader
和 OutputStreamWriter
正好是我刚开始解决编码,学会使用的两个类,哈哈!这也不算是巧合吧,应该说是一种积累!附 PS
本网页所有视频内容由 imoviebox边看边下-网页视频下载, iurlBox网页地址收藏管理器 下载并得到。
ImovieBox网页视频下载器 下载地址: ImovieBox网页视频下载器-最新版本下载
本文章由: imapbox邮箱云存储,邮箱网盘,ImageBox 图片批量下载器,网页图片批量下载专家,网页图片批量下载器,获取到文章图片,imoviebox网页视频批量下载器,下载视频内容,为您提供.
阅读和此文章类似的: 全球云计算