最近在做一个大型Java项目的移植工作,遇到大量字符编码问题,传统的Windows平台主要使用GBK编码,而linux上面为了保持字符的统一性,使用UTF8编码,这个就导致从windows迁移过来的配置文件和资源文件乱码。之前我写过一篇《字符编码浅析》,简单介绍了下这几种编码的不同。
这里我首先解决Linux平台下的乱码问题:如果只是要看一个不同编码的文件,我们可以用vim打开,然后:set fileencoding=utf8 gbk….的方式改变查看文档的编码。如果我们想转换文档编码形式,那么使用以下的命令(将一个GBK编码的文件转换成UTF-8编码):
$enconv -L zh_CN -x UTF-8 filename
文档编码转换完成,这里我从Java的角度阐述如果读写。因为网上关于Java读写的博文足够多,我主要来总结一下:
Java 的IO有两种流的类型:那就是字节流(InputStream、OutputStream)和字符流(Reader、Writer)。我们在读写中文的时候务必使用字符流,因为中文是2个字节组成,如果读取一个字节必然出现问题。
InputStream常用的类是如下,直接对读数据的是节点流,浅色的是处理流,依靠InputStream对数据进行读写,拥有更强大的功能。
OutputStream常用类如下,直接写的是节点流,浅色的是处理流。
二进制读写的时候要使用Stream的方式进行,而读写字符就使用Reader、Writer。
总结一下两种类型,节点流为以下表格,通过这些对象直接对数据源进行读写。
类型 | 字符流 | 字节流 |
File | FileReader、FileWriter | FileInputStream、FileOutputStream |
Memory Array | CharArrayReader、CharArrayWriter | ByteArrayInputStream、ByteArrayOutputStream |
Memory String | StringReader、StringWriter | – |
Pipe | PipedReader、PipedWriter | PipedInputStream、PipedOutputStream |
- 但是节点流提供功能单一,需要使用处理流对其进行拓展,其中尤其是 BufferedReader、BufferedWriter最为常用,先看一个表格:
处理类型 | 字符流 | 字节流 |
Buffering | BufferedReader、BufferedWriter | BufferedInputStream、BufferedOutputStream |
Filtering | FilterReader、FilterWriter | FilterInputStream、FilterOutputStream |
字节流字符流转换 | InputStreamReader、OutputStreamWriter | |
Object序列化 | ObjectInputStream、ObjectOutputStream | |
数据转换 | DataInputStream、DataOutputStream | |
计数 | LineNumberReader | LineNumberInputStream |
Peeking ahead | PushbackReader | PushbackInputStream |
PrintWriter | PrintStream |
- BufferedReader和BufferedWriter来读写文件(字符读写,只要不是二进制文件,都可以用字符,字节是给二进制文件使用的),这种方式读写效率最好(而且可以选择想要的字符集),这个主要将数据先缓冲起来,然后一起写入或者读取出来。经常使用的是readLine()方法,一次读取一行数据。其中我们可以根据装饰者模式修改BufferReader和BufferWriter中的类,比如new InputStreamReader(new FileInputStream(…), StandardCharsets.UTF_8),通过这种方式,就可以以UTF-8的方式读取文件,或者以new OutputStreamWriter(new FileOutputStream(“test.txt”), “UTF-8”)写文件
使用方式 FileReader->BufferedReader->readLine、FileWriter->BufferedWriter->write
private static void read() throws FileNotFoundException, IOException { File file = new File("a.txt");// 指定要读取的文件 // 获得该文件的缓冲输入流 BufferedReader bufferedReader = new BufferedReader(new FileReader(file)); String line = "";// 用来保存每次读取一行的内容 while ((line = bufferedReader.readLine()) != null) { System.out.println(line); } bufferedReader.close();// 关闭输入流 } private static void write() throws IOException { File file = new File("a.txt");// 指定要写入的文件 if (!file.exists()) {// 如果文件不存在则创建 file.createNewFile(); } // 获取该文件的缓冲输出流 BufferedWriter bufferedWriter = new BufferedWriter(new FileWriter(file)); bufferedWriter.write("你好世界"); bufferedWriter.newLine(); bufferedWriter.write("hello world"); bufferedWriter.flush();// 清空缓冲区 bufferedWriter.close();// 关闭输出流 }
与他类似的就是BufferedInputStream和BufferedOuputStream,用这两个读取二进制文件,不同的在于读取的是二进制文件。
FileInputStream ——>BufferedInputStream -> read、FileOutputStream->BufferedOutputStream->write
// 指定要读取文件的缓冲输入字节流 BufferedInputStream in = new BufferedInputStream(new FileInputStream("a.jpg")); File file = new File("b.jpg"); if (file != null) { file.createNewFile(); } // 指定要写入文件的缓冲输出字节流 BufferedOutputStream out = new BufferedOutputStream(new FileOutputStream(file)); byte[] bb = new byte[1024];// 用来存储每次读取到的字节数组 int n;// 每次读取到的字节数组的长度 while ((n = in.read(bb)) != -1) { out.write(bb, 0, n);// 写入到输出流 } out.close();// 关闭流 in.close();
有的时候我们还要使用File对象,这个时候可以查看FileInputStream、FileOutputStream、FileReader、FileWriter的构造方法。类似于FileReader(File file)。
字符流到字节流的转换也很重要,类似于:
InputStreamReader isr = new InputStreamReader(System.in); BufferedReader br = new BufferedReader(isr); String s = null; try { s = br.readLine(); ... OutputStreamWriter osw = new OutputStreamWriter( new FileOutputStream("....")); osw.write("mircosoftibmsunapplehp");
Java中io类太多了,有时候容易眼花,不知道用哪个,这个时候只要记住字符流和字节流的异同,合理使用类,并查询java api即可。
参考:
http://blog.csdn.net/dangnianmingyue_gg/article/details/47361323