Java学习42-Java 流(Stream)、文件(File)和IO - FileReader\FileWriter的使用

IO流

IO流的概述,分类等

Java程序中,对于数据的输入输出以stream方式进行,可以看作是一种数据的流动。
IO流中的IO是Input和Output的缩写,是非常实用的技术,用于处理设备之间的数据传输。读写文件,网络通讯等。

(以下视角,都是站在程序(内存)内部角度观察的。)
输入Input:读取外部数据(磁盘,光盘等存储设备的数据)到程序(内存)中
输出Output:将程序(内存)数据输出到磁盘,光盘等存储设备中

流的分类:

java.io包下提供了各种“流”类和接口,用以获取不同种类的数据,并通过下列方法输入或输出数据。

  • 按数据的流向不同分为:输入流和输出流。

    • 输入流:把数据从其他设备上读取到内存中的流。(以InputStream / Reader结尾)
    • 输出流:把数据从内存中写出到其他设备上的流。(以OutputStream / Writer结尾)

知识回忆:
bit、Byte和char的区别:

  • bit(比特)是计算机中最小的数据单位,表示信息的最小单位,是二进制数的一位包含的信息或2个选项中特别指定1个的需要信息量。一般来说,n比特的信息量可以表现出2的n次方种选择。
  • Byte(字节)是计算机中用于计量存储容量的一种计量单位,也是存储空间的基本计量单位。它由8个二进制位(即8个bit)构成,用于存储和表示数据。一个字节可以储存一个英文字母或者半个汉字。
  • char(字符)是计算机中表示字符或符号的数据类型。在大多数编程语言和计算机系统中,一个char类型的数据通常由若干个字节(通常是1到多个字节)组成,具体取决于使用的字符编码方案。例如,在ASCII编码中,一个char类型的字符由一个字节表示;而在Unicode编码中,一个char类型的字符可能需要多个字节来表示。

综上所述,bit是计算机中最小的数据单位,Byte由多个bit组成,而char则用于表示字符或符号,其大小取决于所使用的字符编码方案,通常由一个或多个Byte组成。bit<Byte<=Char

  • 按操作数据单位的不同分为:此处按照 字节流(8 bit) 和 字符流(16 bit)理解。

    • 字节流: 以字节为单位,读写数据的流。字节Byte是计算机数据处理的基本单位,通常由8位(bit)组成。(以InputStream, OutputStream结尾)
    • 字符流:以字符为单位,读写数据的流。字符Character用于表示文本中的单个字母、数字或符号。在Java中,char是一个基本数据类型,char类型占用16位(2字节)。(以Reader Writer结尾)
  • 根据IO流的角色不同分为节点流和处理流。

    • 节点流:直接从数据源或目的地读写数据。

在这里插入图片描述

    • 处理流:不直接连接到数据源或目的地,而是连接在以存在的流(节点流或处理流)之上,通过对数据的处理为程序提供更为强大的读写功能。

在这里插入图片描述

流的分类图示:

在这里插入图片描述

流的API

java的IO流共涉及40多个类,实际上非常规则,都是从如下4个抽象类派生出来的。

抽象基类输入流输出流
字节流InputStreamOutputStream
字符流ReaderWriter

由这四个类派生出来的子类名称都是以其父类名作为子类名后缀。

分类字节输入流字节输出流字符输入流字符输出流
抽象基类InputStreamOutputStreamReaderWriter
访问文件FileInputStreamFileOutputStreamFileReaderFileWriter
访问数组ByteArrayInputStreamByteArrayOutputStreamCharArrayReaderCharArrayWriter
访问管道PipeInputStreamPipeOutputStreamPipedReaderPipedWriter
访问字符串StringReaderStringWriter
缓冲流BufferedInputStreamBufferedOutputStreamBufferedReaderBufferedWritter
转换流InputStreamReaderOutputStreamWriter
对象流ObjectInputStreamObjectOutputStream
FilterInputStreamFilterOutputStreamFilterReaderFilterWriter
打印流PrintStreamPrintWriter
推回输入流PushbackInputStreamPushbackReader
特殊流DataInputStreamDataOutputStream

总结一下:

抽象基类(基本类)4个节点流(也称文件流,能处理文件)
InputStreamFileInputStream
OutputStreamFileOutputStream
ReaderFileReader
WriterFileWriter

说明:本章虽然涉及到的流很多,但是使用流进行数据的读写操作是非常标准和规范的。

练习1:读取hello.txt中的内容,显示在控制台上。
建议使用try-catch-finally的方式处理异常,确保流一定被关闭了,避免内存泄漏。

提示:
File f1= new File("xxx.txt");

FileReader myfr1 = new FileReader(f1)

int mydata = myfr1.read();

if(mydata != -1){ System.out.print((char)mydata); }

//提示:因为当读取数值为-1时候,是文件全部读取完成的标记。

package IOTest;

import org.junit.Test;

import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;

public class FileReaderWriterTest {
    /*
    * 需求:读取hello.txt中的内容,显示在控制台上。
    * 建议使用try-catch-finally的方式处理异常,确保流一定被关闭了,避免内存泄漏。
    * 举例见如下的test2
    * */
    @Test
    public void test1() throws IOException {
        //1.创建File类的对象,对应hello.txt文件
        File f1 = new File("hello.txt");
        //2.创建输入型字符流,用于读取数据
        FileReader fr1 = new FileReader(f1);
        //3.读取数据并且显示在控制台上

        //方式1:
        //   int readfr1 = fr1.read();
        //   while (readfr1 !=-1)
        //   //一旦等于-1说明读到最末尾了,读完毕了
        //   {
        //       System.out.print((char)readfr1);
        //       readfr1 = fr1.read();
        //   }


        /* 这种写法也是类似的,都可以用
        while(true){
            int ff1 = fr1.read();
            if(ff1 == -1) break;
            else System.out.print((char)ff1);
            }
        */

        //方式2:
        int data;
        while ((data = fr1.read()) != -1) {

            System.out.print((char) data);
        }
        //4.流资源的关闭操作(必须关闭,否则资源泄漏)
        fr1.close();

    }

        @Test
        public void test2(){
            //1.创建File类的对象,对应hello.txt文件
            File f2 = new File("hello.txt");
            //2.创建输入型字符流,用于读取数据
            FileReader filereader1= null;
            //3.读取数据并且显示在控制台上(读取结果为int类型,当获得-1时说明读到了末尾)
            try {
                filereader1 = new FileReader(f2);
                int data2;
                while( ( data2=filereader1.read() ) != -1){

                    System.out.print((char)data2);
                }
            } catch (IOException e) {
                e.printStackTrace();
            } finally {
                //4.流资源的关闭操作(必须关闭,否则资源泄漏)
                try {
                    if(filereader1 != null)//这里是为了优化添加的,如果fread为null,说明某种原因创建fread失败了,直接关闭资源就可以了。
                        filereader1.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }


    /*
     * 需求:读取hello.txt中的内容,显示在控制台上。
     * 对test2()进行优化,每次读取多个字符串存放到字符数组中,减少与磁盘的交互次数,从而提高效率。
     *
     * */

    @Test
    public void test3() throws FileNotFoundException {
        FileReader filereader1= null;

        try {
            //1.创建File类的对象,对应hello.txt文件
            File f2 = new File("hello.txt");
            //2.创建输入型字符流,用于读取数据
            filereader1 = new FileReader(f2);

            //3.读取数据,并显示在控制台上
            char[] cbuffer =new char[5];
            int len= filereader1.read(cbuffer);
            //当filereader1.read(cbuffer)一般返回的是读取到数组char[]cbuffer里面的数据个数,当返回-1说明读完了
             while(len!=-1){
             //遍历数组
                //for (int i = 0; i < cbuffer.length; i++) {
                for (int i = 0; i < len; i++) {
                    System.out.print(cbuffer[i]);
                }
                len = filereader1.read(cbuffer);
            }




        } catch (IOException e) {
            throw new RuntimeException(e);
        } finally {
            //4.流资源的关闭操作(必须关闭,否则资源泄漏)
            try {
                if(filereader1 != null)//这里是为了优化添加的,如果fread为null,说明某种原因创建fread失败了,直接关闭资源就可以了。
                    filereader1.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }


    }


运行结果:

helloWorld123

举例2:往文件hello.txt中写入自定义的的内容。
比如原文件hello.txt文件内容为

this is line 1
this is line 2
this is line 3

代码结构类似这样

File myf1 = new File("hello.txt");
filewriter1=new FileWriter(myf1);
filewriter1.write("写进文件的内容");

//注意,如果文件不存在,系统会自动创建。

  • 如果想要覆盖原文件内容,直接写
    filewriter1=new FileWriter(myf1);
    或者写
    filewriter1=new FileWriter(myf1,flase);

  • 如果想要直接在原文件内容后面直接追加内容:
    filewriter1=new FileWriter(myf1,true);

参考代码:


    /*
    * 需求:将内存中的数据写入到指定文件中
    *
    * */

package IOTest;

import org.junit.Test;

import java.io.*;

public class FileReaderWriterTest {
    @Test
    public void test04()  {
        FileWriter wr1= null;
        try {
            //1.创建File类的对象,指明要写出的文件的名称
            File filedest= new File("hello.txt");
            //2.创建输出流

            //覆盖文件内容,使用构造器:
            //wr1 = new FileWriter(filedest);
            // 这一句等同于wr1 = new FileWriter(filedest,false);会覆盖文件内容

            //在现有的文件上追加内容使用构造器:
            wr1 = new FileWriter(filedest,true);
            //3.写出具体过程

            wr1.write("写入的内容");
            System.out.println("输出完毕");
        } catch (IOException e) {
            e.printStackTrace();
        } finally { //4.关闭资源
            try {
                if(wr1 != null)
                wr1.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }


    }


    }



输出结果:

输出完毕

Process finished with exit code 0

/*
*
* 需求:复制一份hello.txt文件(以及其内容),命名为hello_copy.txt
*
* */

提示:
1.创建file文件(src & desination)
File file_src = new File("src_file.txt");
File file_des = new File("des_file.txt");
2. 创建输入输出流
FileReader filereader1 = new FileReader(file_src);
FileWriter filewriter1 = new FileWriter(file_des);
3. 构建数据读入写出char当然这一步也可以不用
char [] cart = new char[5];
4. 读写文件

int len = filereader1.read(cart);
while(len !=-1){
	for(i=0;i<len;i++){
	filewriter1.write(cart,0,len);			
						}
len=filereader1.read(cart);
	}
filewriter1.close();
filereader1.close();

程序示例:

package IOTest;

import org.junit.Test;

import java.io.*;

public class FileReaderWriterTest {
@Test
    public void test05(){

        //替代方案(注意这种方法会将原始文件删除):
        //File myf1 = new File("hello.txt");
        //myf1.renameTo(new File("hello_copy.txt"));

        //方式2:
        FileReader filereader1 = null;
        FileWriter filewriter1 = null;
        try {
            //1.创建File类的对象
            File f_org = new File("hello_copy.txt");
            File f_dest = new File("hello_3.txt");
  //File f_dest = new File("hello_3.txt",true); //在现有的文件后面直接追加内容,而不是简单覆盖原文件内容

            //2.创建输入流,输出流
            filereader1 = new FileReader(f_org);
            filewriter1 = new FileWriter(f_dest);
            //3.数据的读入和写出的过程
            char[] charbuff = new char[5];

            //原本的程序如下:
            int len = filereader1.read(charbuff);//记录每次读到charbuff中的字符个数。
            while(len!=-1) {
                for (int i = 0; i < len; i++) {
                    System.out.print(charbuff[i]);
                    //filewriter1.write(charbuff[i]);
                   //filewriter1.write(charbuff,0,len);//这样写是错误的。因为这句其实没有用到i,在i=0到len的循环会一遍又一遍写入至少5次同样的信息。正确的写法,这一句话放在for循环的外面就行。
                    //filewriter1.write(charbuff);//这样写是也错误的。在不是5的倍数情况下,比如12345hi有可能读出12345hi345,
                    // 因为内存中的345还在,而hi后面又没有数据了。
//
                }
                filewriter1.write(charbuff,0,len);
                len=filereader1.read(charbuff);
            }

            //可优化为如下:len写入while循环括号内(但是确保是在for循环外面,或者直接不要for循环了)
           // int len;
           // while((len = filereader1.read(charbuff)) != -1){
           //     filewriter1.write(charbuff,0,len);//0角标开始写,一共写len个角标
           // }

            System.out.println("复制成功");
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            try {
                if(filereader1 != null)
                filereader1.close();

            } catch (IOException e) {
                e.printStackTrace();
            }

            try {
                if(filewriter1 !=null)
                filewriter1.close();
            } catch (IOException e) {
                e.printStackTrace();
            }

        }


    }

    }



运行结果提示,文件内容复制成功:

this is line 1
this is line 2
this is line 3
this is my writting stamp
一二三四物流七八
写入的内容复制成功

Process finished with exit code 0

总结:FileReader\FileWriter的使用
执行步骤:

  1. 创建读取或写出File类的对象
  2. 创建输入流或输出流
  3. 具体的读如或写出的过程
    读入:read(char[] charbuffer)
    写出:write(String str) / write(char[] chabuffer,0,len)
  4. 关闭流资源,避免内存泄漏

注意:

  • 因为涉及到流资源的关闭操作,所以出现异常的话,需要使用try-catch-finally的方式来处理异常。
  • 对于输入流来讲,要求File类的对象对应的物理磁盘上的文件必须存在。否则报错FileNotFoundException;
  • 对于输出流来讲,File类的对象对应的物理磁盘上的文件可以不存在。
    如果此文件不存在,则在此输出的过程中,会自动创建此文件,并写出数据到此文件中。
    如果此文件存在,使用FileWriter(File file)或 FileWriter(File file,false):输出数据过程中,会新建同名的文件,对现有的文件进行覆盖。
    如果用FileWriter(File file,true):表示输出数据过程中,会在现有的文件的末尾追加写出的内容。
  • 24
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
您好,感谢您的提问。根据您的要求和提供的数据,可以通过以下步骤实现: 1. 读取文本文件中的数据,并将其存储到一个List中; 2. 使用Java8新特性Stream对List中的数据进行处理,将它们按照从小到大的顺序排序并去重,得到一个新的List; 3. 将新的List中的数据按照需求格式化为字符串; 4. 最后将格式化后的字符串写回到文本文件中。 下面是代码示例: ```java import java.io.*; import java.util.*; public class Main { public static void main(String[] args) { List<Integer> dataList = readDataFromFile("data.txt"); List<Integer> sortedList = dataList.stream() .sorted() .distinct() .collect(Collectors.toList()); String output = sortedList.stream() .map(String::valueOf) .collect(Collectors.joining("-")); writeDataToFile(output, "output.txt"); } private static List<Integer> readDataFromFile(String filename) { List<Integer> dataList = new ArrayList<>(); try (BufferedReader reader = new BufferedReader(new FileReader(filename))) { String line; while ((line = reader.readLine()) != null) { String[] nums = line.split("-"); for (String num : nums) { dataList.add(Integer.parseInt(num)); } } } catch (IOException e) { e.printStackTrace(); } return dataList; } private static void writeDataToFile(String data, String filename) { try (BufferedWriter writer = new BufferedWriter(new FileWriter(filename))) { writer.write(data); } catch (IOException e) { e.printStackTrace(); } } } ``` 通过以上代码,您可以实现将输入文件中的数据2-1-9-4-7-8按照题目的要求进行格式化,并且写回到输出文件output.txt中,而且所有的处理都是通过Java8的Stream实现的。希望这个答案能够帮到您。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值