最近在做一个大型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 |
Print |
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