Archive for July, 2016

解决乱码思路—Java读写文件中遇到的问题

July 26th, 2016

最近在做一个大型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对数据进行读写,拥有更强大的功能。

20160808200550

OutputStream常用类如下,直接写的是节点流,浅色的是处理流。

20160808200918

二进制读写的时候要使用Stream的方式进行,而读写字符就使用Reader、Writer。

20160808201307

20160808201400

总结一下两种类型,节点流为以下表格,通过这些对象直接对数据源进行读写。

类型 字符流 字节流
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

新的篇章开始了

July 15th, 2016

        距离我上一篇文章已经过去了好几个月,这几个月家里面发生了一些变故,导致我心情比较低落。正好又赶上了我硕士毕业,种种原因导致我几个月没有更新blog。每次打开看到时间停滞在5月17日,就觉得这几个月有些荒废了,所以我要开始慢慢更新blog了。

       虽然我硕士做的东西跟我现在工作做的风马牛不相及,但是我仍然会关注操作系统学界和虚拟化学界的动态,关注开源社区的发展状况。

        以后我会在blog中慢慢开始写一些和Java相关的知识总结,我知道用baidu搜出来的Java基本可以满足我的需求,但是很多blog都有很严重的抄袭问题,很多内容没有自己的理解。我连续打开好几个blog,内容竟然写的一模一样,而我打开Stackoverflow发现有些答案正是我所需要的,我会结合自身遇到的问题,将解决方案和背后的原因总结出来,一方面是为了巩固自己所学的,另一方面也是为了强迫自己继续保持硕士期间的良好作风。

 

PS:虽然人在工作后就会有变懒的取向,但是我知道一个人只有在保持高度的自制力和行动力基础上,才会有更大的提升!克服懒惰,拥抱变化,拒绝做温水中的青蛙!