Java--IO基本流

news/2024/10/5 1:49:02 标签: java, 开发语言

IO流

概述

        生活中,你肯定经历过这样的场景。当你编辑一个文本文件,忘记了`ctrl+s` ,可能文件就白白编辑了。当你电脑上插入一个U盘,可以把一个视频,拷贝到你的电脑硬盘里。那么数据都是在哪些设备上的呢?键盘、内存、硬盘、外接设备等等。

        我们把这种数据的传输,可以看做是一种数据的流动,按照流动的方向,以内存为基准,分为`输入input` 和`输出output` ,即流向内存是输入流,流出内存的输出流。

        Java中I/O操作主要是指使用java.io包下的内容,进行输入、输出操作。输入也叫做读取数据,输出也叫做作写出数据

IO的分类

根据数据的流向分为:输入流和输出流。

输入流 :把数据从`其他设备`上读取到`内存`中的流。

程序 - > 文件

输出流 :把数据从`内存` 中写出到`其他设备`上的流。

文件 - > 程序

格局数据的类型分为:字节流和字符流。

字节流 :以字节为单位,读写数据的流。

字符流 :以字符为单位,读写数据的流。

注意:

纯文本文件:能用Windows自带的记事本打开能读懂的文件

如md和txt

IO流的体系

字节流基本用法

FileOutputStream类

        当你创建一个流对象时,必须传入一个文件路径。该路径下,如果没有这个文件,会创建该文件。如果有这个文件,会清空这个文件的数据。

构造方法:

        public FileOutputStream(File file):创建文件输出流以写入由指定的 File对象表示的文件。

        public FileOutputStream(String name): 创建文件输出流以指定的名称写入文件。

 代码示例:

java">public class FileOutputStreamConstructor throws IOException {
    public static void main(String[] args) {
   	 	// 使用File对象创建流对象
        File file = new File("a.txt");
        FileOutputStream fos = new FileOutputStream(file);
      
        // 使用文件名称创建流对象
        FileOutputStream fos = new FileOutputStream("b.txt");
    }
}
 写出数据的三种方法

写出字节数据 : write(int b) 方法

代码演示:

java">public class FOSWrite {
    public static void main(String[] args) throws IOException {
        // 使用文件名称创建流对象
        FileOutputStream fos = new FileOutputStream("fos.txt");     
      	// 写出数据
      	fos.write(97); // 写出第1个字节
      	fos.write(98); // 写出第2个字节
      	fos.write(99); // 写出第3个字节
      	// 关闭资源
        fos.close();
    }
}
输出结果:
abc

1.创建字节输出流对象细节:

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

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

        细节3:如果文件已经存在,则会清空文件

2.写数据:

        细节:write方法的参数是整数,但是实际上写到本地文件中的是整数在ASCII上对应的字符

3.释放资源

        每次使用完流之后都要释放资源(不然文件一直处于占用状态)

写出字节数组:write(byte[] b)
java">public class FOSWrite {
    public static void main(String[] args) throws IOException {
        // 使用文件名称创建流对象
        FileOutputStream fos = new FileOutputStream("fos.txt");     
      	// 字符串转换为字节数组
      	byte[] b = "黑马程序员".getBytes();
      	// 写出字节数组数据
      	fos.write(b);
      	// 关闭资源
        fos.close();
    }
}
输出结果:
黑马程序员

写出指定长度字节数组:write(byte[] b, int off, int len)
java">public class FOSWrite {
    public static void main(String[] args) throws IOException {
        // 使用文件名称创建流对象
        FileOutputStream fos = new FileOutputStream("fos.txt");     
      	// 字符串转换为字节数组
      	byte[] b = "abcde".getBytes();
		// 写出从索引2开始,2个字节。索引2是c,两个字节,也就是cd。
        fos.write(b,2,2);
      	// 关闭资源
        fos.close();
    }
}
输出结果:
cd

数据追加续写

        经过以上的演示,每次程序运行,创建输出流对象,都会清空目标文件中的数据。如何保留目标文件中数据,还能继续添加新数据呢?

        public FileOutputStream(File file, boolean append): 创建文件输出流以写入由指定的 File对象表示的文件。  

        public FileOutputStream(String name, boolean append): 创建文件输出流以指定的名称写入文件。  

        这两个构造方法,参数中都需要传入一个boolean类型的值,`true` 表示追加数据,`false` 表示清空原有数据。这样创建的输出流对象,就可以指定是否追加续写了,

代码演示:

java">public class FOSWrite {
    public static void main(String[] args) throws IOException {
        // 使用文件名称创建流对象
        FileOutputStream fos = new FileOutputStream("fos.txt",true);     
      	// 字符串转换为字节数组
      	byte[] b = "abcde".getBytes();
		// 写出从索引2开始,2个字节。索引2是c,两个字节,也就是cd。
        fos.write(b);
      	// 关闭资源
        fos.close();
    }
}
文件操作前:cd
文件操作后:cdabcde
写出换行

换行符:

        Windows:\r\n

        Linux系统:\n

        Mac: \r

细节:

        在windows系统中,Java对回车换行进行了优化

        虽然完整的是\r\n,但是我们写其中一个\r或者\n,

        java也可以实现换行,因为Java在底层会自动补全

建议:

        不要省略,还是写全

代码演示:

java">public class FOSWrite {
    public static void main(String[] args) throws IOException {
        // 使用文件名称创建流对象
        FileOutputStream fos = new FileOutputStream("fos.txt");  
      	// 定义字节数组
      	byte[] words = {97,98,99,100,101};
      	// 遍历数组
        for (int i = 0; i < words.length; i++) {
          	// 写出一个字节
            fos.write(words[i]);
          	// 写出一个换行, 换行符号转成数组写出
            fos.write("\r\n".getBytes());
        }
      	// 关闭资源
        fos.close();
    }
}

输出结果:
a
b
c
d
e

FileInputStream类

        java.io.FileInputStream 类是文件输入流,从文件中读取字节。

构造方法:        

        FileInputStream(File file): 通过打开与实际文件的连接来创建一个 FileInputStream ,该文件由文件系统中的 File对象 file命名。

        FileInputStream(String name): 通过打开与实际文件的连接来创建一个 FileInputStream ,该文件由文件系统中的路径名 name命名。  

        当你创建一个流对象时,必须传入一个文件路径。该路径下,如果没有该文件,会抛出FileNotFoundException。

代码演示:

java">public class FileInputStreamConstructor throws IOException{
    public static void main(String[] args) {
   	 	// 使用File对象创建流对象
        File file = new File("a.txt");
        FileInputStream fos = new FileInputStream(file);
      
        // 使用文件名称创建流对象
        FileInputStream fos = new FileInputStream("b.txt");
    }
}

读取字节数据:

字节输入流的细节:

1.创建字节输入流对象

        细节1:如果文件不存在,就直接报错

        Java为什么会这么设计呢?

        输出流:不存在,创建

                把数据写到文件当中

        

        输入流:不存在,则是报错呢?

                读取的时候,数据在文件当中

                所以Java就没有这种无意义的逻辑,文件不存在直接报错

         程序中最重要的是:数据

        

2.写数据

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

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

3.释放资源

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

read方法

        读一次数据,就移动一次指针指向下一个元素

代码演示:

java">public class FISRead {
    public static void main(String[] args) throws IOException{
      	// 使用文件名称创建流对象
       	FileInputStream fis = new FileInputStream("read.txt");
      	// 读取数据,返回一个字节
        int read = fis.read();
        System.out.println((char) read);
        read = fis.read();
        System.out.println((char) read);
        read = fis.read();
        System.out.println((char) read);
        read = fis.read();
        System.out.println((char) read);
        read = fis.read();
        System.out.println((char) read);
      	// 读取到末尾,返回-1
       	read = fis.read();
        System.out.println( read);
		// 关闭资源
        fis.close();
    }
}
输出结果:
a
b
c
d
e
-1

循环改进读取方式,代码使用演示:

java">public class FISRead {
    public static void main(String[] args) throws IOException{
      	// 使用文件名称创建流对象
       	FileInputStream fis = new FileInputStream("read.txt");
      	// 定义变量,保存数据
        int b ;
        // 循环读取
        while ((b = fis.read())!=-1) {
            System.out.println((char)b);
        }
		// 关闭资源
        fis.close();
    }
}
输出结果:
a
b
c
d
e

> 小贴士:

>

> 1. 虽然读取了一个字节,但是会自动提升为int类型。

> 2. 流操作完毕后,必须释放系统资源,调用close方法,千万记得。

使用字节数组读取:read(byte[] b)

        每次读取b的长度个字节到数组中,返回读取到的有效字节个数,读取到末尾时,返回`-1`

代码演示:

java">public class FISRead {
    public static void main(String[] args) throws IOException{
      	// 使用文件名称创建流对象.
       	FileInputStream fis = new FileInputStream("read.txt"); // 文件中为abcde
      	// 定义变量,作为有效个数
        int len ;
        // 定义字节数组,作为装字节数据的容器   
        byte[] b = new byte[2];
        // 循环读取
        while (( len= fis.read(b))!=-1) {
           	// 每次读取后,把数组变成字符串打印
            System.out.println(new String(b));
        }
		// 关闭资源
        fis.close();
    }
}

输出结果:
ab
cd
ed

        错误数据`d`,是由于最后一次读取时,只读取一个字节`e`,数组中,上次读取的数据没有被完全替换,所以要通过`len` ,获取有效的字节,代码使用演示:

java">public class FISRead {
    public static void main(String[] args) throws IOException{
      	// 使用文件名称创建流对象.
       	FileInputStream fis = new FileInputStream("read.txt"); // 文件中为abcde
      	// 定义变量,作为有效个数
        int len ;
        // 定义字节数组,作为装字节数据的容器   
        byte[] b = new byte[2];
        // 循环读取
        while (( len= fis.read(b))!=-1) {
           	// 每次读取后,把数组的有效字节部分,变成字符串打印
            System.out.println(new String(b,0,len));//  len 每次读取的有效字节个数
        }
		// 关闭资源
        fis.close();
    }
}

输出结果:
ab
cd
e

> 小贴士:

>

> 使用数组读取,每次读取多个字节,减少了系统间的IO操作次数,从而提高了读写的效率,建议开发中使用。

练习:小文件拷贝
java">        FileOutputStream fos = new FileOutputStream("a.txt");
        FileInputStream fis = new FileInputStream("b.txt");
        byte[] bytes = new byte[1024];
        int len;
        long start = System.currentTimeMillis();
        while((len = fis.read(bytes)) != -1) {
            fos.write(bytes, 0, len);
        }
        fos.close();
        fis.close();
        //统计拷贝时间
        long end = System.currentTimeMillis();
        System.out.println("拷贝时间:" + (end - start) + "ms");

> 小贴士:

>

> 流的关闭原则:先开后关,后开先关。

字符集

字符集(GBK)

字符集(Unicode)

为什么会有乱码

原因1:

        读取数据时未读完整个汉字

原因2:

        编码和解码时的方式不一样

如何不产生乱码?

1、不要用字节流读取文本文件

2、编码解码时使用同一个码表,同一个编码方式

扩展:

疑问:字节流读取中文会乱码,但是为什么拷贝不会乱码呢?

因为拷贝时,数据源没有丢失,用记事本(采用对应的解码方式)打开不会出现乱码

Java中的编码和解码

字符流

字符流的底层其实就是字节流

字符流=字节流+字符集

特点:

        输入流:一次读一个字节。遇到中文时,一次读多个字节

        输出流:底层会把数据按照指定的编码方式进行编码,变成字节再写到文件中

使用场景

        对于出纯文本文件进行读写操作

FileReader

无参read()代码演示 

java">        FileReader fileReader = new FileReader("a.txt");
        //读取数据read()
        //字符流的底层也是字节流,默认也是一个字节一个字节的读取的
        //如果遇到中文就会一次读取多个,GBK一次读两个字节,UTF-8一次读3个字节

        //read()细节
        //在读取之后,方法的底层还会进行解码并转成十进制
        //  最终把这个十进制作为返回值
        //  这个十进制的数据也表示在字符集上的数字
        //  英文:文件里面二进制数据  0110 0001
        //          read()方法进行读取,转成十进制97
        //  中文:文件里面二进制数据  11100110 10110001 10001001
        //          read()方法进行读取,转成十进制27721
        //  我想看到中文汉字,就是把这些十进制数据,再进行强转就行了
        int ch = 0;
        while ((ch = fileReader.read()) != -1) {
            System.out.print((char)ch);
        }
        fileReader.close();

有参read()代码演示

java">        FileReader fileReader = new FileReader("a.txt");
        int len;
        char[] chars = new char[1024];
        while((len=fileReader.read(chars))!=-1){
            System.out.println(new String(chars,0,len));
        }
        fileReader.close();

注意:

        无参的ch表示拿到的字符的十进制

        有参的len表示读取到字符的个数

FileWriter

基本写出数据

`write(int b)` 方法,每次可以写出一个字符数据,代码使用演示:

java">        FileWriter fw = new FileWriter("fw.txt");     
      	// 写出数据
      	fw.write(97); // 写出第1个字符
      	fw.write('b'); // 写出第2个字符
      	fw.write('C'); // 写出第3个字符
      	fw.write(30000); // 写出第4个字符,中文编码表中30000对应一个汉字。
      
      	/*
        【注意】关闭资源时,与FileOutputStream不同。
      	 如果不关闭,数据只是保存到缓冲区,并未保存到文件。
        */
        // fw.close();

输出结果:
abC田

> 小贴士:

>

> 1. 虽然参数为int类型四个字节,但是只会保留一个字符的信息写出。

> 2. 未调用close方法,数据只是保存到了缓冲区,并未写出到文件中。

关闭和刷新

因为内置缓冲区的原因,如果不关闭输出流,无法写出字符到文件中。但是关闭的流对象,是无法继续写出数据的。如果我们既想写出数据,又想继续使用流,就需要`flush` 方法了。

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

* `close `:先刷新缓冲区,然后通知系统释放资源。流对象不可以再被使用了。

代码演示:

java">public class FWWrite {
    public static void main(String[] args) throws IOException {
        // 使用文件名称创建流对象
        FileWriter fw = new FileWriter("fw.txt");
        // 写出数据,通过flush
        fw.write('刷'); // 写出第1个字符
        fw.flush();
        fw.write('新'); // 继续写出第2个字符,写出成功
        fw.flush();
      
      	// 写出数据,通过close
        fw.write('关'); // 写出第1个字符
        fw.close();
        fw.write('闭'); // 继续写出第2个字符,【报错】java.io.IOException: Stream closed
        fw.close();
    }
}

> 小贴士:即便是flush方法写出了数据,操作的最后还是要调用close方法,释放系统资源。

写出其他数据

1. **写出字符数组** :`write(char[] cbuf)` 和 `write(char[] cbuf, int off, int len)` ,每次可以写出字符数组中的数据,用法类似FileOutputStream,代码使用演示:

java">public class FWWrite {
    public static void main(String[] args) throws IOException {
        // 使用文件名称创建流对象
        FileWriter fw = new FileWriter("fw.txt");     
      	// 字符串转换为字节数组
      	char[] chars = "黑马程序员".toCharArray();
      
      	// 写出字符数组
      	fw.write(chars); // 黑马程序员
        
		// 写出从索引2开始,2个字节。索引2是'程',两个字节,也就是'程序'。
        fw.write(b,2,2); // 程序
      
      	// 关闭资源
        fos.close();
    }
}

2. **写出字符串**:`write(String str)` 和 `write(String str, int off, int len)` ,每次可以写出字符串中的数据,更为方便,代码使用演示:

java">public class FWWrite {
    public static void main(String[] args) throws IOException {
        // 使用文件名称创建流对象
        FileWriter fw = new FileWriter("fw.txt");     
      	// 字符串
      	String msg = "黑马程序员";
      
      	// 写出字符数组
      	fw.write(msg); //黑马程序员
      
		// 写出从索引2开始,2个字节。索引2是'程',两个字节,也就是'程序'。
        fw.write(msg,2,2);	// 程序
      	
        // 关闭资源
        fos.close();
    }
}

续写和换行
java">public class FWWrite {
    public static void main(String[] args) throws IOException {
        // 使用文件名称创建流对象,可以续写数据
        FileWriter fw = new FileWriter("fw.txt",true);     
      	// 写出字符串
        fw.write("黑马");
      	// 写出换行
      	fw.write("\r\n");
      	// 写出字符串
  		fw.write("程序员");
      	// 关闭资源
        fw.close();
    }
}
输出结果:
黑马
程序员

> 小贴士:字符流,只能操作文本文件,不能操作图片,视频等非文本文件。

>

> 当我们单纯读或者写文本文件时  使用字符流 其他情况使用字节流

字符流综合练习

练习1:拷贝

        需求:拷贝一个文件夹,考虑子文件夹

java">public class Test01 {
    public static void main(String[] args) throws IOException {
        //拷贝一个文件夹,考虑子文件夹

        //1.创建对象表示数据源
        File src = new File("D:\\aaa\\src");
        //2.创建对象表示目的地
        File dest = new File("D:\\aaa\\dest");

        //3.调用方法开始拷贝
        copydir(src,dest);



    }

    /*
    * 作用:拷贝文件夹
    * 参数一:数据源
    * 参数二:目的地
    *
    * */
    private static void copydir(File src, File dest) throws IOException {
        dest.mkdirs();
        //递归
        //1.进入数据源
        File[] files = src.listFiles();
        //2.遍历数组
        for (File file : files) {
            if(file.isFile()){
                //3.判断文件,拷贝
                FileInputStream fis = new FileInputStream(file);
                FileOutputStream fos = new FileOutputStream(new File(dest,file.getName()));
                byte[] bytes = new byte[1024];
                int len;
                while((len = fis.read(bytes)) != -1){
                    fos.write(bytes,0,len);
                }
                fos.close();
                fis.close();
            }else {
                //4.判断文件夹,递归
                copydir(file, new File(dest,file.getName()));
            }
        }
    }
}

练习2:文件加密

        需求:为了保证文件的安全性,就需要对原始文件进行加密存储,再使用的时候再对其进行解密处理。

        加密原理:对原始文件中的每一个字节数据进行更改,然后将更改以后的数据存储到新的文件夹中

        解密原理:读取加密之后的文件,按照加密的规则反向操作,变成原始文件

java">public class Test02 {
    public static void main(String[] args) throws IOException {
        /*
            为了保证文件的安全性,就需要对原始文件进行加密存储,再使用的时候再对其进行解密处理。
            加密原理:
                对原始文件中的每一个字节数据进行更改,然后将更改以后的数据存储到新的文件中。
            解密原理:
                读取加密之后的文件,按照加密的规则反向操作,变成原始文件。

             ^ : 异或
                 两边相同:false
                 两边不同:true

                 0:false
                 1:true

               100:1100100
               10: 1010

               1100100
             ^ 0001010
             __________
               1101110
             ^ 0001010
             __________
               1100100

        */
    }

    public static void encryptionAndReduction(File src, File dest) throws IOException {
        FileInputStream fis = new FileInputStream(src);
        FileOutputStream fos = new FileOutputStream(dest);
        int b;
        while ((b = fis.read()) != -1) {
            fos.write(b ^ 2);
        }
        //4.释放资源
        fos.close();
        fis.close();
    }


}

练习3:修改文件中的数据

文本文件中有以下的数据:

        2-1-9-4-7-8

将文件中的数据进行排序,变成以下的数据:

        1-2-4-7-8-9

java">public class test {
    public static void main(String[] args) throws IOException {
        //读取数据
        FileReader fileReader =new FileReader("a.txt");
        StringBuilder stringBuilder=new StringBuilder();
        int ch;
        while ((ch=fileReader.read())!=-1){
            stringBuilder.append((char)ch);
        }
        fileReader.close();
        
        //排序
        String str=stringBuilder.toString();
        String[] str1=str.split("-");
        ArrayList<Integer> list=new ArrayList<>();

        for (String s : str1) {
            int i=Integer.parseInt(s);
            list.add(i);
        }
        Collections.sort(list);
        System.out.println(list);
        
        //写出
        FileWriter fileWriter=new FileWriter("b.txt");
        for (int i = 0; i < list.size(); i++) {
            if(i==list.size()-1){
                fileWriter.write(list.get(i)+"");
            }else {
                fileWriter.write(list.get(i)+"-");
            }
        }
        fileWriter.close();

    }
}

简化写法

java">        //排序
        Integer[] integers = Arrays.stream(stringBuilder.toString().split("-"))
                .map(Integer::parseInt)
                .sorted()
                .toArray(Integer[]::new);
        System.out.println();
        //写出
        FileWriter fileWriter = new FileWriter("b.txt");
        String s = Arrays.toString(integers).replace(", ","-");
        String result = s.substring(1, s.length() - 1);
        System.out.println(result);
        fileWriter.write(result);
        fileWriter.close();

细节1:

        文件中的数据不要换行

细节2:bom头


http://www.niftyadmin.cn/n/5690569.html

相关文章

第二十一章 (动态内存管理)

1. 为什么要有动态内存分配 2. malloc和free 3. calloc和realloc 4. 常⻅的动态内存的错误 5. 动态内存经典笔试题分析 6. 总结C/C中程序内存区域划分 1.为什么要有动态内存管理 我们目前已经掌握的内存开辟方式有 int main() {int num 0; //开辟4个字节int arr[10] …

基于vue框架的大学生四六级学习网站设计与实现i8o8z(程序+源码+数据库+调试部署+开发环境)系统界面在最后面。

系统程序文件列表 项目功能&#xff1a;学生,训练听力,学习单词,单词分类,阅读文章,文章类型,学习课程 开题报告内容 基于Vue框架的大学生四六级学习网站设计与实现开题报告 一、研究背景与意义 随着全球化进程的加速和国际交流的日益频繁&#xff0c;英语作为国际通用语言…

rabbitmq消费者应答模式

1.应答模式 RabbitMQ 中的消息应答模式主要包括两种&#xff1a;自动应答&#xff08;Automatic Acknowledgement&#xff09;和手动应答&#xff08;Manual Acknowledgement&#xff09;。 自动应答&#xff1a; 不在乎消费者对消息处理是否成功&#xff0c;都会告诉队列删…

简单易懂的springboot整合Camunda 7工作流入门教程

简单易懂的Spring Boot整合Camunda7入门教程 因为关于Spring Boot结合Camunda7的教程在网上比较少&#xff0c;而且很多都写得有点乱&#xff0c;很多概念写得太散乱&#xff0c;讲解不清晰&#xff0c;导致看不懂&#xff0c;本人通过研究学习之后就写出了这篇教学文档。 介…

Netty:高性能异步网络编程框架全解析

Netty作为一个基于Java NIO技术的开源异步事件驱动网络编程框架,已经成为开发高性能、高可靠性网络应用的首选工具之一。本文将全面介绍Netty的核心特性、架构原理以及使用方法,帮助你快速掌握这个强大的框架。 Netty的主要特点 异步事件驱动模型 Netty采用异步非阻塞的IO模型…

若依--文件上传前端

前端 ry的前端文件上传单独写了一个FileUpload.Vue文件。在main.js中进行了全局的注册&#xff0c;可以在页面中直接使用文件上传的组件。全局导入 在main.js中 import 组件名称 from /components/FileUpLoadapp.compoent(组件名称) //全局挂载组件在项目中使用 组件命令 中…

音视频入门基础:FLV专题(9)——Script Tag简介

一、SCRIPTDATA 根据《video_file_format_spec_v10_1.pdf》第75页到76页&#xff0c;如果某个Tag的Tag header中的TagType值为18&#xff0c;表示该Tag为Script Tag&#xff08;脚本Tag&#xff0c;又称Data Tag、SCRIPTDATA tag&#xff09;。这时如果Filter的值不为1表示未加…

AIGC:生成式人工智能的5个层次

随着人工智能技术的迅猛发展&#xff0c;生成式人工智能在多个领域展现出巨大的应用潜力。为了帮助大家理解生成式人工智能的进化过程&#xff0c;我们借鉴“真格基金”的分类方式&#xff0c;将其应用分为五个层次&#xff0c;每个层次代表了 AI 与人类合作深度的不同。在这篇…