IO字符流与异常处理

java中的编码采用的就是unicode编码,源文件使用的是utf-8编码,jvm虚拟机找的.class编译文件使用的是utf-16编码格式。

说到编码,得先从ASCII编码讲起。ASCII编码是由美国人发明,美国的字符不超过255个,所以ASCII编码使用了8bit 即一个字节来存储字符。由于汉字的数量远超255个,所以中国自己发明了一个GB2312编码来表示汉字,一般的汉字使用2个字节,对于一些生僻的汉字则使用更多的字节来表示,当然,GB2313编码是可以兼容ASCII码的。

​ 然后,日本,韩国等等国家也自己发明了一套编码方法,这时候又出现了一个新的问题。如果一篇文章里面,即有中文,又有日文的话,无论使用中文的编码方法还是使用日文的编码方法都会出现乱码。随后,unicode编码便应运而生。unicode编码对文字的编码进行了统一,当然,unicode只是一种编码规范,它有多个版本,常用的unicode编码使用了16位来存储字符,16位的存储空间足以容纳世界上所有书面字符(对于汉字来说,一共有6万多个,只能包含其中的一些常用汉字,所以unicode编码对于汉字的兼容性并不是特别好)。unicode编码兼容了ASCII码,ASCII码转unicode编码时,保持后8位不变,前8位只需要用0去补全即可。

​ 使用了unicode编码后,又有新的问题出现。因为unicode编码是用两个字节来存储字符,如果一篇文章中,大部分都是英文,使用unicode编码就会造成空间的浪费,对英文部分使用ASCII码只需要一个字节就可以了。这时候,utf-8解决了这个问题。utf-8是一种可变长的字符编码,当存储英文时只使用一个字节,节省了一半的空间,而存储中文字符时,长度还是不变。utf-8虽然压缩了存储空间,但是如果在内存中存储,使用utf-8却由于它的长度不固定,带来了很大的不便,使得在内存处理字符变得复杂。应对这个问题的解决策略是:在内存中存储字符时还是使用unicode编码,因为unicode编码的长度固定,处理起来很方便。而在文件的存储中,则使用utf-8编码,可以压缩内存,节省空间。这里一般有个自动转换的机制,即从文件中读取utf-8编码到内存时,会自动转换为unicode编码,而从内存中将字符保存到文件时,则自动转换为utf-8编码。

  1. GB2312,由中华人民共和国政府制定的,简体汉字编码规范,大陆所有计算机中的简体中文,都使用此种编码格式。与此对应的还有BIG5,是中华民国政府制定的,繁体汉字的编码规范,一般应用于海外计算机的繁体中文显示。所谓的繁体中文Windows,简体中文Windows,指的就是采用BIG5和GB2312编码格式的操作系统。这两种编码方式不兼容,如果使用一种编码的文本阅读器来读另一种编码的文本,就会出现乱码。比如在简体中文Windows上读BIG5编码的文件,就是乱码,反之亦然。使用简体浏览器浏览的时候,到了繁体中文网站,如果不改变码制,也是乱码。
  2. GBK,又称GBK大字符集,简而言之就是将所有亚洲文字的双字节字符,包括简体中文,繁体中文,日语,韩语等,都使用一种格式编码,兼容所有平台的上的语言。GBK大字符集包含的汉字数量比GB2312和BIG5多,使得汉字兼容足够使用。

当使用字节流读取文本文件时,可能会有些歌小问题,就是遇到中文字符时,可能不会显示完整的字符,那是因为一个中文字符可能占用多个字节存储.所以java提供一些字符流,以字符为单位读写数据,专门用于处理文本文件

使用字节流读取中文文件

GBK一个中文占用俩个字节

UTF-8占用三个字节

Reader

抽象类.全部字符输入流的超类

方法

abstract void close()关闭该流并释放与之关联的所有资源

void mark(int readAheadLimit)标记流当中的位置

boolean markSupported()判读此流是否支持mark()操作

int read()读取单个字符

int read(char[] cbuf)将字符读入数组

abstract int read(char[] bcuf ,int off,int len)将字符读入指定的字符缓冲区

booean ready()怕那不得那是否转呗读取此流

void reset()重置该流

long skip(long n)跳过字符

直接子类

  • BufferedReader 带缓冲的
  • CharArrayReader 读取字符数组
  • FilterReader带过滤的
  • InputStream转换流
  • PipedReader管道流
  • StringReader字符串流

FileReader

java.io.FileReader extends InputStreamReader extends Reader

FileReader文件字符输入流

作用把硬盘文件中的数据已字符的方式读取到内存中

构造方法

  • FileReader(File file)在给定从中读取数据的File的情况下创建一个新FileReader
  • FileReader(FileDescriptor fd)在给定从中读取数据的FileDescriptor的情况下创建一个新FileReader
  • FileREader(String fileName)在给定从中读取数据的文件名的情况下创建一个新FileReader

构造方法作用:创建一个FileReader对象,会把FileReader对象指向要读取的文件

package com.company;
import java.io.FileReader;
import java.io.IOException;

public class Main {

    //a.txt内容为 你好abc##
    public static void main(String[] args) throws IOException {
        //创建fileREader对象,构造方法中绑定要读取的数据
        FileReader fr=new FileReader(".\\a.txt");
        //使用FileReader对象中的方法read读取文件
        //int read读取单个字符返回,一个字符一个字符的读取
//         int len=0;
//         while ((len=fr.read())!=-1){
//             System.out.print((char)len);//你好abc##
//         }

        //int read(char[] bhar[]cbuf)一次读取多个字符,将字符读入数组
        char[]cs =new char[1024];//存储读取到的对个字符
        int len=0;//
        while ((len=fr.read(cs))!=-1) {
            System.out.println(new String(cs,0,len));//你好abc##
        }


         //释放资源
        fr.close();
    }

}

Writer

字符输出流的超类,是一个抽象类

方法

  • Writer append(char c)将制定字符添加到此writer
  • writer append(charSequence csq)将制定字符序列添加到此writer
  • Writer append(CharSequence csq,int start ,int end)将制定的字符序列添加到此 write.Appedable
  • abstract close()关闭此流,但要先刷新它
  • abstract void flush()刷新改流的缓冲
  • abstract write(char[] cbuf)写入字符数组
  • abstract write(int c cbuf,int off,int len)写入字符数组的某一部分
  • void write(int c)写入单个字符
  • void write(String str)写入字符串
  • void write(STring str,int off,int len)写入字符串的某一部分

直接子类

BuffereWriter,CharArrayWriter,FilterWriter,OutputStreamWriter,PipeWriter,PrintWriter,StringWriter

FileWriter

java.io.FileWriter extends OutputStreamWriter extends Writer

FileWriter:文件字符输出流

作用:把内存当中的字符写入到文件中

构造方法

  • FileWriter(File file) 根据给定的File对象构造一个FileWriter对象
  • FileWriter(File file ,博哦了安append)根据给定的File对象构造一个FileWriter对象
  • FileWriter(FileDescirptor fd)根据与构成某个文件描述相关联的FileWriter对象
  • FileWriter(STring fileName)给定的文件名构造一个FileWriter对象
  • FileWriter(String fileName,boolean append)根据给定的文件名以及是否附加写入数据的boolean值来构造FileWriter对象

参数:写入数据的目的地

String fileName:文件的路径

File file是一个文件

构造方法作用:

会创建一个FileWriter对象

会根据构造方法传递的文件/文件的路径,创建文件

会把FileWriter对象指向创建好的文件

package com.company;
import java.io.FileWriter;
import java.io.IOException;

public class Main {

    public static void main(String[] args) throws IOException {
        //创建FileWriter对象,构造方法中绑定要写入的数据的目的地
        FileWriter fw = new FileWriter(".\\a.txt");
        //使用FileWriter中的方法write,把数据写入到内存缓冲区中(字符串转换为字节的过程)
        //void write(int c)写入单个字符
        fw.write(97);
        //使用FileWriter中的方法flush,把内存缓冲区中的数据,刷新到文件中
        fw.flush();
        //释放资源(会先把内存缓冲区中的数据刷新到文件中)
        fw.close();
    }

}

字符流与字节流的最大区别是字符流先把数据写入到内存中

如果不调用flush或者close方法是不会把数据由内存中写到硬盘中的

flush:刷新缓冲区,流对象可以继续使用

close:新刷新缓冲区,如何通知系统释放资源.流对象不可用再被使用了

续写和换行

续写,追加写,使用俩个参数的构造方法

FileWriter(String fileName,boolean append)

FileWriter(File file,boolean append)

参数

String fileName,File file 写入数据的目的地

boolean append:续写开关true:不会创建写的文件覆盖源文件,false:创建新的文件覆盖源文件

换行windowsrn

macr

linuxn

package com.company;
import java.io.FileWriter;
import java.io.IOException;

public class Main {

    public static void main(String[] args) throws IOException {
        FileWriter fw=new FileWriter("a.txt",true);
        for(int i = 0;i<10;i++){
            fw.write("helloworld"+i+"\r\n");
        }
        fw.close();
    }

}

io异常处理

在jdk1.7中使用try catch finally处理流中的异常

package com.company;
import java.io.FileWriter;
import java.io.IOException;

public class Main {

    public static void main(String[] args) {
        FileWriter fw = null;
       try {
           fw=new FileWriter("w:\\a.txt",true);
           for(int i = 0;i<10;i++){
               fw.write("helloworld"+i+"\r\n");
           }
       }catch (IOException e){
            System.out.println(e);
       }finally {
           //一定会执行的
           //创建对象失败了,fw的默认值就是null,null是不能调用方法的,会抛出一个空指针异常,需要增加一个判断,如果不是null才把资源释放
           if(fw!=null){
               try {
                   //fw.close声明跑出了IOExption异常对象,所以我们要处理这个异常对象要么throws要么try catch
                   fw.close();
               }catch(IOException e){
                    e.printStackTrace();
               }
           }

       }
    }

}

jdk7的新特性

,在try的后边可以增加一个(),在括号中可以定义流对象

name这个流对象的作用域就在try中有效

try中的代码执行完毕自动会把流对象释放,不用谢finally

格式try(){}

package com.company;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;

public class Main {

    public static void main(String[] args) {

        try(FileInputStream fis=new FileInputStream(".\\1.png");
            FileOutputStream fos =new FileOutputStream(".\\2.png");){//流对象使用完之后会自动释放,作用域这在try大括号中
            byte[]bytes=new byte[1024];
            int len=0;
            while ((len=fis.read(bytes))!=-1){
                //使用字节输出流的方法write,把读取到的字节写入到目的文件中
                fos.write(bytes,0,len);
            }
        }catch(IOException e){
            System.out.println(e);
        }

    }

}

jdk9新特性

try的前边可以定义流对象

在try后边的()可以直接定义流对象的名称(变量名)

在try代码执行完毕之后,流对象也可以释放掉,不用谢finaly

A a=new A()

B b=new B()

try(a;b){

}catch{}

package com.company;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;

public class Main {

    public static void main(String[] args) throws IOException {
        FileInputStream fis=new FileInputStream(".\\1.png");
        FileOutputStream fos =new FileOutputStream(".\\2.png");
        try(fis,fos){//流对象使用完之后会自动释放,作用域这在try大括号中
            byte[]bytes=new byte[1024];
            int len=0;
            while ((len=fis.read(bytes))!=-1){
                //使用字节输出流的方法write,把读取到的字节写入到目的文件中
                fos.write(bytes,0,len);
            }
        }catch(IOException e){
            System.out.println(e);
        }

    }

}

总结还是7方便!

Last modification:April 22, 2022
如果觉得我的文章对你有用,请随意赞赏