首页 > 其他分享 >IO流(基本流)

IO流(基本流)

时间:2023-04-09 12:55:05浏览次数:23  
标签:基本 读取 java String IO import public 字节

IO流概述

IO流:存储和读取数据的解决方法。用于读写文件中的数据(可以读写文件,或者网络中的数据)

在IO流中以程序为参照进行读写操作,即程序对文件进行读取和写入。

IO流的分类:

  1. 按照流的方向分为:

    输入流:读取本地文件中的数据。

    输出流:写入本地文件中的数据。

  2. 按照操作文件类型分为:

    字节流:可以操作所有类型的文件。

    字符流:只能操作纯文本文件。

IO流的体系

IO

image

字节流

字节流使用场景:拷贝任意类型的文件。

image

FileOutputStream

操作本地文件的字节输出流,可以把程序中的数据写到本地文件中。

书写步骤:

  1. 创建字节输出流对象
  2. 写数据
  3. 释放资源
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
public class IOtext {
    public static void main(String[] args)throws IOException {
        FileOutputStream out=new FileOutputStream("a.txt");//这个构造方法可以是File对象,也可也是path路径
        out.write(65);//写入数据
        out.close();//关闭输出传输通道
    }
}

FileOutputStream第一步创建对象,就是把对应路径的文件和我们所创建的对象建立连接通道

第二部写入数据,就是通过这个数据传输通道把指定数据传输进去

第三步就是关闭这个数据传输通道

书写细节:

  • 创建字节输出流对象:

    细节1:参数是字符串表示的路径,也可也是File对象表示的路径

    细节2:如果文件不存在会创建一个新的文件,但是要保证父级路径是存在的。

    细节3:如果文件已经存在,构造方法会情况文件内部数据。

  • 写数据

    细节1:write方法的参数是整数,单实际是这个字符在ASCII表上的字符

  • 释放资源

    每次使用完流后都要释放资源。

    用处就是解除资源占用。

FileOutputStream写数据的3中方法

方法名 说明
void write(int b) 一次写一个字节数据
void write(byte[] b) 一次写一个字节数组的数据
void write(byte[] b,int off,int len) 一次写一个字节数组中从off开始的长度为len的数据

换行写

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
public class IOtext {
    public static void main(String[] args)throws IOException {
        FileOutputStream out=new FileOutputStream("a.txt");
        String str1="hello world \r\n";
        String str2="hi world";
        out.write(str1.getBytes());
        out.write(str2.getBytes());
        out.close();
    }
}

不同的操作系统换行符号不一样:

windows:\r\n

Linux:\n

Mac:\r

但是,在Windows操作系统中,java对回车换行进行了优化,虽然完整的是\r\n但是只需要写一个\r或\n即可。

java在底层会自动补全。

续写

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;

public class IOtext {
    public static void main(String[] args)throws IOException {
        FileOutputStream out=new FileOutputStream("a.txt",true);//在创建对象的时候,把续写开关打开即可
        String str1="hello world \n";
        String str2="hi world";
        out.write(str1.getBytes());
        out.write(str2.getBytes());
        out.close();
    }
}

FileInputStream

作用:操作本地文件的字节输入流,可以把本地文件中的数据读取到程序中。

和FileOutputStream一样,创建对象的时候可以传递Stream类型的oath地址,或者File类型的path地址

书写步骤:

  1. 创建字节输入流对象
  2. 读取数据
  3. 释放数据
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
public class IOtext {
    public static void main(String[] args)throws IOException {
        FileInputStream f=new FileInputStream("a.txt");//建立连接通道
        int read = f.read();//读取数据
        System.out.println(read);
        f.close();//关闭连接通道
    }
}

FileInputStream书写细节

  1. 创建字节输入流对象

    细节:如果文件不存在,则直接报错。

  2. 读取数据

    细节1:一次读一个字节,读出来是数据在ASCII表上对应的数字

    细节2:读到文件末尾了,read方法返回-1

    细节3:read表示读取数据,而且读取一个数据就移动一次指针。

  3. 释放资源

    细节:每次使用完流后都需要释放资源

FileInputStream循环读取

import java.io.FileInputStream;
import java.io.IOException;
public class IOtext {
    public static void main(String[] args)throws IOException {
        FileInputStream f=new FileInputStream("a.txt");
        int b;
        while( (b=f.read()) != -1){
            System.out.print((char)b);
        }
        f.close();
    }
}

文件拷贝

思路:通过FileInputStream和FileOutputStream进行文件拷贝。

//1.小文件拷贝
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
public class IOtext {
    public static void main(String[] args)throws IOException {
        //创建对象
        String path1="C:\\Users\\WDADWA\\Desktop\\learn_file\\java\\jdk1.8中文版.CHM";
        String path2="C:\\Users\\WDADWA\\Desktop\\learn_file\\jdk1.8中文版.CHM";
        FileInputStream in=new FileInputStream(path1);
        FileOutputStream out=new FileOutputStream(path2);
        //拷贝
        //核心思想:边读边写
        int b;
        while((b=in.read()) != -1){
            out.write(b);
        }
        //释放资源
        //规则:先开的流最后关闭
        out.close();
        in.close();
    }
}

文件拷贝速度很慢的原因:FileInputStream一次只读取一个字节,当字节数量为100的时候就代表要循环100次.

解决方法:FileInputStream一次性读取多个字节

方法名 说明
public int read() 一次读取一个字节数据
public int read(byte[] buffer) 一次读取一个字节数字的数据
public int read(byte[] buffer,int off,int len) 一次读取一个字节数字的数据中起始位置长度为len的数据

public int read(byte[] buffer) 的细节:

  1. 一次读取一个字节数组的数据,每次读取会尽可能把数组装满。

  2. 故数组越长读取越快,但是数组太长会导致内存存不下,导致崩溃。

  3. 一般创建数组会使用1024的整数倍

  4. read方法返回的是读取数据的长度,数组直接存储到了buff数组中

  5. 在buff数组中多次读取的时候,最新一次读取的数据会覆盖掉上一次读取的数据。

    假设读取abcde字符串,且byte数组长度为2

    第一次:ab

    第二次:cd

    第三次:ed(采用覆盖读取,此时只读取到了e,故覆盖了0索引)

    解决方法:使用public int read(byte[] buffer,int off,int len)

    public int read(byte[] buffer,0,len)这样即可解决第三次出现d的情况了

  6. 规定了read方法没有读取到数据的时候返回-1

import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
public class IOtext {
    public static void main(String[] args)throws IOException {
        //创建对象
        String path1="C:\\Users\\WDADWA\\Desktop\\learn_file\\java\\java语言学习\\0正则表达式.md";
        String path2="C:\\Users\\WDADWA\\Desktop\\learn_file\\0正则表达式.md";
        FileInputStream in=new FileInputStream(path1);
        FileOutputStream out=new FileOutputStream(path2);
        //拷贝
        //核心思想:边读边写
        int b;
        byte[] buff=new byte[1024*1024*5];//创建了5MB数组
        while((b=in.read(buff)) != -1){
            out.write(b);
        }
        //释放资源
        //规则:先开的流最后关闭
        out.close();
        in.close();
    }
}

IO流中不同版本JDK捕获异常的方式

在以后编写代码的时候一般不会使用throws抛出异常,而是采用捕获异常的方式处理。

try...catcg异常处理

import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
public class IOtext {
    public static void main(String[] args)throws IOException {
        //创建对象
        FileOutputStream fos=null;//这里使用外界定义是因为如果定义在try中那么这个是局部变量,则无法使用在finally中
        try{
            fos=new FileOutputStream("a.txt");
            fos.write(97);
        }catch(IOException e){
            e.printStackTrace();
        }finally{
            if(fos != null){//这里使用if是因为可能fos找不到对应的文件,则会为null,这样会报错
                try {//这里使用try catch包裹是因为close方法也是存在异常的
                    fos.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }

        }
    }
}

上述代码过于复杂阅读性很低,故java在不同版本中给出了方法来简化。

java推出了一个AutoCloseable接口

特点:凡是实现了这个接口的类,在特定情况下,可以自动释放资源。

  1. 基本做法(上述写法)

    try{
        可能出现异常的代码;
    }catch(异常类名 变量名){
        异常处理代码;
    }finally{
        执行资源释放操作;
    }
    
  2. JDK7方案(这个方式使得括号内部代码太复杂,故jdk9简化了)

    try(创建流对象1;创建流对象2;...;创建流对象n){
        可能出现异常的代码;
    }catch(异常类名 变量名){
        异常的处理代码
    }
    //资源用完后会自动释放
    

    上述代码简化为:

    import java.io.FileInputStream;
    import java.io.FileOutputStream;
    import java.io.IOException;
    public class IOtext {
        public static void main(String[] args)throws IOException {
            try( FileOutputStream f= new  FileOutputStream("a.txt")){
                f.write(97);
            }catch(IOException e){
                e.printStackTrace();
            }
        }
    
    
    
  3. JDK9方案

    创建流对象1;
    创建流对象2;
    ...
    创建流对象n;
    try(流1;流2;...;流n){
        可能出现异常的代码;
    }catch(异常类名 变量名){
        异常的处理代码;
    }
    //资源用完后会自动释放
    

    上述代码简化为

    import java.io.FileInputStream;
    import java.io.FileOutputStream;
    import java.io.IOException;
    public class IOtext {
        public static void main(String[] args)throws IOException {
            FileOutputStream f= new  FileOutputStream("a.txt");
            try(f){
                f.write(97);
            }catch(IOException e){
                e.printStackTrace();
            }
        }
    }
    
    

字符集

ASCII字符集

在ASCII编码表中记录了128条数据,这128条数据对于欧洲国际来说够用了。这玩意主要给欧美那边用的。

计算机在存储英文的时候,只需要一个字节就足够了。

image

编码的原因是:计算机中最小存储单位是1个字节。

GB2312字符集

1980年发布,1981年5月1日实施的简体中文汉字编码国家标准。收录了7447个图形字符,包括6763个简体汉字。

BIG5字符集

台湾地区繁体中文标准字符集,收录13053个中文汉字,1984年实施。

GBK字符集

2000年3月17日发布,收录21003个汉字,包含国家标准GB13000-1中的全部中日韩汉字,和BIG5编码中的所有汉字。

windows系统默认使用的就是GBK,但系统显示:ANSI。因为每个操作系统使用的编码是不同的。

GBK完全兼容ASCII,故GBK存储英文和ASCII一样。

GBK的规则:

规则1:汉字用两个字节存储,第一个字节叫高位字节,第二个字节叫低位字节。(因为汉字太多了一个字节不够放

规则2:高位字节二进制一定用1开头,转成十进制后是一个负数(目的是为了和英文区分)、

总结:

GBK中一个英文字母占一个字节,二进制第一位是0.

GBK中一个汉字占两个字节,二进制第一位是1

image

image

Unicode字符集

Unicide字符集:又叫万过码,国际标准字符集,它将世界各种语言的每个字符定义一个唯一的编码,以满足跨语言,跨平台的文本信息交换。

英文借助Unicide字符集存储方式:

Unicode完全兼容AscII字符集,但是编码的时候不一样。常用的分为三种:

  • UTF-8编码规则:用1~4个字节保存(最常用)

  • UTF-16编码规则:用2~4个字节保存

  • UTF-32编码规则:使用固定4个字节保存

image

UTF-8编码规则规定:

  1. 如果是ASCII里面的英文字母,统一使用1个字节表示
  2. 如果是叙利亚,阿拉伯等文字,统一使用2个字节表示
  3. 如果是中日韩,中东,东南亚文字,统一使用3个字节表示
  4. 其他语言使用4个字节表示。

UTF-8对于不同的字节,采取不同的编码规则

1个字节的首位必须为0,其他位置为查询Unicode对应的二进制数。

2个字节的首位为110,第二个字节首位必须为10,其他位置为查询Unicode对应的二进制数。

3个字节的高位字节首位为1110,其他字节首位为10,其他位置为查询Unicode对应的二进制数。

4个字节的高位字节首位为11110,其他字节首位为10,其他位置为查询Unicode对应的二进制数。

image

故对于英文:

image

对于中文:

image

红色的是UTF-8编码规定的,黑色的由对应编码补齐。

乱码

乱码出现原因:

  1. 读取数据时没有读完整个汉字。
  2. 编码和解码方式不统一。

防止乱码产生:

  1. 不要用字节流去读取文本文件。
  2. 编码和界面时使用同一个码表,同一个编码方式。

java中编码和解码的代码实现

  1. java中编码的方法

    String类中的方法 说明
    public byte[] getBytes() 使用默认方式进行编码
    public byte[] getBytes(String charsetName) 使用指定方式进行编码

    idea默认使用Unicode中的UTF-8进行编码

    Eclipse默认使用GBK进行编码,charsetName是编码的名字,如UTF-16

  2. java中解码的方法

    String类中的方法 说明
    String(byte[] bytes) 使用默认方式进行解码
    String(byte[] bytes,String charsetName) 使用指定方式进行解码
import java.io.UnsupportedEncodingException;
import java.util.Arrays;

public class Text {
    public static void main(String[] args) throws UnsupportedEncodingException {
        //1.编码
        String str="你好a";
        byte[] bytes1 = str.getBytes();
        byte[] bytes2 = str.getBytes("GBK");
        System.out.println(Arrays.toString(bytes1));
        System.out.println(Arrays.toString(bytes2));
        //2. 解码
        String str1=new String(bytes1);
        String str2=new String(bytes2,"GBK");
        System.out.println(str1);
        System.out.println(str2);
    }
}

字符流

字符流使用场景:操作文本文件。

乱码产生的原因之一是:读取数据时未读取整个汉字。

java推出了字符流,当读取字母时,一个字节一个字节读取,当读取汉字时,根据对应的编码所决定一次读2个字节还是3个字节。

字符流的底层就是字节流。

字符类=字节流+字符集

特点:

  • 输入流:一次读一个字节,遇到中文时,一次读多个字节。
  • 输出流:底层会把数据按照指定的编码方式进行编码,变成字节再写到文件中。

字符流使用场景:对于纯文本进行读写操作。

字符流继承体系结构

image

FileReader

创建方法和使用方法都是和字节流一样的。

使用步骤

  1. 创建字符流输入对象

    构造方法 说明
    public FileReader(File file) 创建字符输入流关联本地文件
    public FileReader(String pathName) 创建字符输入流关联本地文件
    public FileReader(String pathName,Charset charsetName) 创建字符输入流关联本地文件并且使用指定字符编码

    第三个Charset是一个工具类。(JDK11新特性)

    new FileReader("a.txt",Charset.forName("GBK"));//这样来指定编码读取数据
    

    如果读取文件不存在,报错。

  2. 读取数据

    成员方法 说明
    public int read() 每次读取1字节数据,读到末尾时返回-1
    public int read(char[] buffer) 每次读取多个数据到buff数组中,读到末尾时返回-1
    public int read(char[] buffer,int off,int len) 每次读取多个数据到buff数组中len长度,读到末尾时返回-1

    空参read细节:

    1. 默认也是一个字节一个字节的读取,如果遇到中文就一次读取多个字节。
    2. 在读取之后,方法底层还会进行解码并转成十进制。最终把这个十进制作为返回值,这个十进制也表示在字符集上对应得数字。

    有参的read细节:

    • 读取数据,解码,强转三步合并了,把强转之后的字符放入char数组中了。
  3. 释放资源

    方法名 说明
    public int close() 关闭流

FileWrite

创建方法和使用方法都是和字节流一样的。

使用步骤

  1. 创建字符流输入对象

    构造方法 说明
    public FileWriter(File file) 创建字符输出流关联本地文件
    public FileWriter(String pathName) 创建字符输出流关联本地文件
    public FileWriter(File file,boolean append) 创建字符输出流关联本地文件,续写
    public FileWriter(String pathName,boolean append) 创建字符输出流关联本地文件,续写
    public FileWriter(String pathName,Charset charsetName) 创建字符输出流关联本地文件,续写+使用指定编码
    public FileWriter(String pathName,Charset charsetName,boolean append) 创建字符输出流关联本地文件,续写+使用指定编码

    细节1:参数是字符串表示的路径还是File对象都是可以的

    细节2:如果文件不存在会创建一个新的文件,但是要保证父级路径是存在的

    细节3:如果文件已经存在,则会清空文件,如果不想情况就在append中写入true打开续写开关。

  2. 读取数据

    成员方法 说明
    void write(int c) 写出一个字符
    void write(String str) 写出一个字符串
    void write(String str,int off,int len) 写出一个字符串的一部分
    void write(char[] cbuf) 写出一个字符数组
    void write(char cbuf,int off,int len) 写出一个字符数组的一部分

    如果write方法的参数是整数,实际上写到本地文件的是整数在字符集上对应的字符。

  3. 释放资源

    方法名 说明
    public int close() 关闭流

字符流原理解析

输入流:

  1. 创建字符输入流对象

    底层:关联文件,并创建缓冲区(长度为8192的字节数组)

  2. 读数据

    底层:

    1. 判断缓冲区中是否有数据可以读取

    2. 缓存区中没有数据:

      • 就从文件中获取数据,装到缓存区中,每次尽可能的装满缓冲区。
      • 如果文件中也没有数据了,就返回-1
    3. 缓存区中有数据:就冲缓存区中读取数据。

      空参的read方法,一次读取一个字节,遇到中文一次读取多个字节,把字节解码为十进制返回。

      有参的read方法:把读取字节,解码,强转三部合并了,强转之后的字符放到数组中。

FileWriter的关联会删除文件,但是reader同时对这个文件进行读取的时候可以读取到缓冲区中的数据哦~

(必须先Reader,再关联writer)

输出流:

  1. 创建字符输出流对象

    底层:关联文件,并创建缓冲区(长度为8192的字节数组)

  2. 写数据

    底层:把所有数据按照utf-8进行编码,并先写入缓冲区中。

    三个情况把缓冲区数据写入文件中。

    • 当缓冲区装满了
    • 手动刷新(使用flush方法)
    • 释放资源时

    fluse和clone的区别:flush刷新之后还可继续往文件中写数据。

标签:基本,读取,java,String,IO,import,public,字节
From: https://www.cnblogs.com/wdadwa/p/17300165.html

相关文章