java IO

126 阅读5分钟

IO (1).png

编码

ASCII是1个字节,只能表示英文,以及少量其他字符,如+-*/,.()<>等

UTF-8,UTF-16,UTF-32都是unicode的不同版本实现,unicode早期是2个字节表示国际上大多数语言字符,之后又扩展到4个字节

GB2312,GBK,GB18030是中国为汉语制定的,也能表示英文,其中GBK占2个字节

UTF-8

UTF-8是变长字节,占1 ~ 4个字节,英文等价于ASCII,占1字节,中文占2 ~ 4字节,适合文件中英文占多数。如果文件中中文占多数,其实GBK更适合。

  • 只占一个字节的字符,8位字节第一位就是0
    0 X X X X X X X
  • 占用2个字节的字符,第一个字节的是以110开头,第二个字节以10开头
    1 1 0 X X X X X     1 0 X X X X X X
  • 占用3个字节的字符,第一个字节的是以1110开头,剩余字节都以10开头
    1 1 1 0 X X X X     1 0 X X X X X X     1 0 X X X X X X
  • 占用4个字节的字符,第一个字节的是以11110开头,剩余字节都以10开头
    1 1 1 1 0 X X X     1 0 X X X X X X     1 0 X X X X X X     1 0 X X X X X X

其实除了字节开头用于表示字节数的,其余的X就是unicode编码

UTF-16

UTF-16占用2个字节,对于英文浪费空间,使用不多。不过在java里的char类型占用2字节,就是采用UTF-16。字符流Reader的read()方法,读取一个字符,采用和文件编码对应的解码方法,可能读取多个字节,再将其转化为UTF-16编码,无论中英文都是2个字节,没有UTF-8前面那些固定的,可能表示不全所有中文(java如何处理的,待学习),最后返回这2个字节对应的小于65536的整数。

外部待读取的文件的编码,java程序内部char,string的数据编码,是两种不相关的编码,字符流Reader的read()方法就实现了两种编码的转换。当然java本身的代码文件.java也有自己的编码。

字节流

InputStream

抽象类InputStream的常用子类有FileInputStream,ObjectInputStream,DataInputStream,BufferedInputStream。

image.png

FileInputStream

一般用于处理图像等原始字节流,不适合处理文本字符串,尤其是含中文,因为中文不是以1个字节为单位

try (InputStream fis = new FileInputStream("input.txt")) {
    int content;
    while ((content = fis.read()) != -1) {
        System.out.print((char) content);
    }
} catch (IOException e) {
    e.printStackTrace();
}
try (InputStream fis = new FileInputStream("input.txt")) {
    int n;
    byte[] bytes = new byte[1024];
    while ((n = fis.read(bytes)) != -1) {
        String s = new String(bytes, 0, n);
        System.out.println(s);
    }
} catch (IOException e) {
    e.printStackTrace();
}

当input.txt是纯英文时还能正常读取。read()不涉及编码方式,(char) content强转时会把1字节用UTF-16编码为2字节的char

BufferedInputStream

BufferedInputStream和FileInputStream用法基本一样,内部多了个缓冲区,默认是8192字节,当每次读取的数据量不大时,可以提高效率,避免每次都从硬盘读

BufferedInputStream bufferedInputStream = new BufferedInputStream(new FileInputStream("input.txt"));

DataInputStream

感觉不太好用,如果文件里是1234,那么readInt()读出来的却不是1234,好像必须用DataOutputStream的writeInt写入文件的才能读,但直接打开文件却显示乱码

DataInputStream dataInputStream = new DataInputStream(new FileInputStream("input.txt"));
int i = dataInputStream.readInt();
boolean b = dataInputStream.readBoolean();

ObjectInputStream

用于反序列化

ObjectInputStream input = new ObjectInputStream(new FileInputStream("object.data"));
MyClass object = (MyClass) input.readObject();

OutputStream

抽象类OutputStream的常用子类有FileOutputStream,ObjectOutputStream,DataOutputStream,BufferedOutputStream。

image.png

FileOutputStream

try (FileOutputStream output = new FileOutputStream("output.txt")) {
    byte[] array = "JavaGuide".getBytes();
    output.write(array);
} catch (IOException e) {
    e.printStackTrace();
}

getBytes()会将字符串转换为字节数组,默认采用系统指定的编码格式,可以使用System.getProperty("file.encoding")查看系统编码格式,getBytes()也可以传入指定编码格式

BufferedOutputStream

字节缓冲输出流

BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream("output.txt"));

DataOutputStream

写入指定类型数据,不能单独使用,必须结合 FileOutputStream。但直接打开文件显示乱码

DataOutputStream dataOutputStream = new DataOutputStream(new FileOutputStream("out.txt"));
dataOutputStream.writeBoolean(true);
dataOutputStream.writeByte(1);

ObjectInputStream

序列化对象

ObjectOutputStream output = new ObjectOutputStream(new FileOutputStream("file.txt")
Person person = new Person("Guide哥", "JavaGuide作者");
output.writeObject(person);

字符流

Reader

抽象类Reader的常用子类有InputStreamReader,FileReader,BufferedReader

image.png

InputStreamReader

将字节流转换为字符流,还可以指定编码格式。如果读取的文件是字符串文本,就应该使用字符流

InputStreamReader isr = new InputStreamReader(new FileInputStream("input.txt"),"UTF-8");
int content = 0;
while((content = isr.read()) != -1){
    System.out.println((char)content);
}
isr.close();

read()只是读取一个字符所占据的多个字节,返回的并不是该字符char,而是该字符对应的unicode编码(即char对应的编码格式,应该是UTF-16)的十进制整数,需要再强转为char

FileReader

InputStreamReader的子类,可以直接传入文件路径,但不能指定编码格式,而是采用系统指定的编码

FileReader fileReader = new FileReader("input.txt");
int content;
while ((content = fileReader.read()) != -1) {
    System.out.print((char) content);
}

BufferedReader

带缓冲区的字符输入流,效率更高。用来装饰其他Reader,一般传入InputStreamReader或FileReader。

BufferedReader reader = new BufferedReader(new FileReader("input.txt"));

Writer

抽象类Writer的常用子类有OutputStreamWriter,FileWriter,BufferedWriter

image.png

OutputStreamWriter

将字符流转换为字节流,可以指定编码格式,需要传入其他OutputStream,指定输出的字节流

OutputStreamWriter writer = new OutputStreamWriter(new FileOutputStream("output.txt"),"UTF-8");
writer.write("hello");
writer.close();

FileWriter

OutputStreamWriter的子类,可以直接传入文件路径,但不能指定编码格式,采用系统自动编码

FileWriter writer = new FileWriter("output.txt");
writer.write("hello");
writer.close();

BufferedWriter

带缓冲区的字符输出流,效率更高。用来装饰其他Writer,一般传入OutputStreamWriter或FileWriter。

BufferedWriter writer = new BufferedWriter(new FileWriter("output.txt"));

设计模式

装饰器模式

FilterInputStream和FilterOutputStream应用了装饰器模式,比如,其子类BufferedInputStream就是对InputStream功能的增强,增加缓冲功能

适配器模式

InputStreamReader和OutputStreamWriter应用了适配器模式,实现了字节流和字符流之间的转换