一文搞懂java中的数据流处理

数据流转

在前端的javascrit中,我们几乎是没有能力对计算机上的某个文件进行读写操作的,毕竟js只是一个网页脚本。但是,nodejs是可以实现文件读写操作的。

在java中,对数据进行读写操作非常容易!下图展示了Java中数据流转的大致过程:

数据从数据源 通过管道 (stream)流转之数据目的地,管道的入口我们称之为input ,出口称之为out ,因此,数据流转操作也称之为IO操作。java中就封装了IO类帮助我们操作文件。

文件流操作

要操作一个文件,我们必须先创建文件对象,使用文件路径关联系统文件。

如图,我们Data文件夹有一个名 为testData的txt文件

想要获得文件路径,我们可以按照下图的操作方式获取

关联好文件路径,我们就可以对文件进行一些操作了:

文件基础操作

csharp 复制代码
import java.io.File;

public class Data_IO {
    public static void main(String[] args) {
        // 文件流操作 基于 java.io
        // 使用文件路径关联系统文件
        String filePath = "D:\Code\JAVA\collection\src\Data\testData.txt";

        //  创建文件对象
        File file = new File(filePath);
        
        // 文件对象的操作

        // 判断当前文件对象是否文件(File对象也可能是文件夹)
        System.out.println(file.isFile());            // true
        // 判断文件对象是否为文件夹
        System.out.println(file.isDirectory());       // false
        // 判断文件是否关联成功
        System.out.println(file.exists());            // true
        // 获取文件名称
        System.out.println(file.getName());           // testData.txt
        // 获取文件长度
        System.out.println(file.lastModified());      // 1698977593862
        // 获取文件的绝对路径
        System.out.println(file.getAbsolutePath());   // D:\Code\JAVA\collection\src\Data\testData.txt
    }
}

文件夹基础操作

文件夹的一些基础方法和文件是一致的。但有几个方法是文件夹操作独有的,如

  • 获取文件夹内的数据列表
  • 获取文件夹中的文件对象
ini 复制代码
File[] files = file.listFiles();
for (File file1 : files) {
    System.out.println(file1 );  // testData.txt
}

文件复制

在java中,实现文件复制是一个稍微复杂的过程。现在,我们通过一个示例来演示下代码。我们需要在Data文件夹下创建一个testData 的副本文件testData_copy.txt

java中,对象复制的流程大致如下模型

首先,我们需要数据源对象数据目的对象:

java 复制代码
import java.io.*;

public class IO_Copy {
    public static void main(String[] args) {
        // 数据源对象
        File srcFile = new File("D:\Code\JAVA\collection\src\Data\testData.txt");
        // 数据目的地对象
        File destFile = new File("D:\Code\JAVA\collection\src\Data\testData_copy.txt");
    }
}
  • 数据目的对象的路径并不存在,是我们自己定义的要生成的文件路径。

然后,我们要创建文件输入管道1文件输出管道2:

java 复制代码
import java.io.*;

public class IO_Copy {
    public static void main(String[] args) {
        // 数据源对象
        File srcFile = new File("D:\Code\JAVA\collection\src\Data\testData.txt");
        // 数据目的地对象
        File destFile = new File("D:\Code\JAVA\collection\src\Data\testData_copy.txt");
        // 文件输入流(管道对象)
        FileInputStream in = null;
        // 文件输出流(管道对象)
        FileOutputStream out = null;
    }
}

接下来,我们就要打开阀门1阀门2进行数据流转了

ini 复制代码
in = new FileInputStream(srcFile);
out = new FileOutputStream(destFile);
// 打开阀门,流转数据(输入端)
int data = in.read();
// 打开阀门,流转数据(输出端)
out.write(data);

数据流转完毕,我们需要关闭管道

go 复制代码
in.close();
out.close();

当然,数据操作过程中,存在很多异常情况,比如找不到数据源文件等等,所以实际代码中,我们需要进行异常处理。比较完成的代码如下:

java 复制代码
import java.io.*;

public class IO_Copy {
    public static void main(String[] args) {
        // 数据源对象
        File srcFile = new File("D:\Code\JAVA\collection\src\Data\testData.txt");
        // 数据目的地对象
        File destFile = new File("D:\Code\JAVA\collection\src\Data\testData_copy.txt");

        // 文件输入流(管道对象)
        FileInputStream in = null;
        // 文件输出流(管道对象)
        FileOutputStream out = null;
        try {
            in = new FileInputStream(srcFile);
            out = new FileOutputStream(destFile);
            // 打开阀门1,流转数据(输入端)
            int data = in.read();
            // 打开阀门2,流转数据(输出端)
            out.write(data);
        } catch (IOException e) {
            throw new RuntimeException(e);
        } finally {
            if(in != null){
                try {
                    in.close();
                } catch (IOException e) {
                    throw new RuntimeException(e);
                }
            }
            if(out != null){
                try {
                    out.close();
                } catch (IOException e) {
                    throw new RuntimeException(e);
                }
            }
        }
    }
}

我们来运行看看

可以看到,文件复制的确完成了,但是为什么复制出来的文件只有一个H字符???

这其实和java的文件复制原理有关,文件流复制时,是这个过程:

复制时,阀门1 打开,H字符 进入管道1:

H字符 进入管道2 时,阀门1 关闭,阀门2打开

H字符 进入数据目的地 后,阀门2也随之关闭,复制完成

在上述代码中,我们的阀门1阀门2 只开启过一次,自然只能复制一个H字符

ini 复制代码
in = new FileInputStream(srcFile);
out = new FileOutputStream(destFile);
// 打开阀门1,流转数据(输入端)
int data = in.read();
// 打开阀门2,流转数据(输出端)
out.write(data);

所以很简单,我们只需要循环执行上述代码即可!那该执行几次呢?我们看下面代码:

我们将阀门多开启了一次,导致副本文件多了一个乱码字符,通过打印结果可见,当data值为-1前,终止这个循环即可!改写代码:

java 复制代码
import java.io.*;

public class IO_Copy {
    public static void main(String[] args) {
        // 数据源对象
        File srcFile = new File("D:\Code\JAVA\collection\src\Data\testData.txt");
        // 数据目的地对象
        File destFile = new File("D:\Code\JAVA\collection\src\Data\testData_copy.txt");

        // 文件输入流(管道对象)
        FileInputStream in = null;
        // 文件输出流(管道对象)
        FileOutputStream out = null;
        try {
            in = new FileInputStream(srcFile);
            out = new FileOutputStream(destFile);
            // 打开阀门,流转数据(输入端)
            int data = in.read();
            out.write(data);

            // 打开阀门,流转数据(输出端)
            while ((data = in.read()) != -1){
                out.write(data);
            }
        } catch (IOException e) {
            throw new RuntimeException(e);
        } finally {
            if(in != null){
                try {
                    in.close();
                } catch (IOException e) {
                    throw new RuntimeException(e);
                }
            }
            if(out != null){
                try {
                    out.close();
                } catch (IOException e) {
                    throw new RuntimeException(e);
                }
            }
        }
    }
}

缓冲流

上述的文件复制效率很低,每复制一个字符,都要开启关闭阀门一次,因此,java也提供了缓冲区的优化方式。

它的概念非常容易,就是在文件传输的管道中,增加了一个缓冲管道

其复制流程大致如下:

阀门1 打开,所有内容进入管道1

阀门1 关闭,所有内容进入缓冲区

缓冲区 内容进入管道2阀门2 打开

管道2 内容进入数据目的地 ,复制完成,阀门2关闭。

完整代码如下:

java 复制代码
import java.io.*;

public class IO_Copy {
    public static void main(String[] args) {
        // 数据源对象
        File srcFile = new File("D:\Code\JAVA\collection\src\Data\testData.txt");
        // 数据目的地对象
        File destFile = new File("D:\Code\JAVA\collection\src\Data\testData_copy.txt");

        // 文件输入流(管道对象)
        FileInputStream in = null;
        // 文件输出流(管道对象)
        FileOutputStream out = null;

        // 缓冲输入流
        BufferedInputStream bufferIn = null;
        BufferedOutputStream bufferOut = null;

        // 创建一个大小为1024字节的缓存区cache。这将用于存储从源文件读取的数据,然后再写入目标文件。
        byte[] cache = new byte[1024];

        try {
            in = new FileInputStream(srcFile);
            out = new FileOutputStream(destFile);
            // 缓冲输入流
            bufferIn = new BufferedInputStream(in);
            // 缓冲输出流
            bufferOut = new BufferedOutputStream(out);

            // 数据流转
            int data;
            // 当读取到文件末尾(即data为-1)时,循环结束。
            while ((data = bufferIn.read(cache)) != -1){
                bufferOut.write(cache,0,data);
            }

        } catch (IOException e) {
            throw new RuntimeException(e);
        } finally {
            if(bufferIn != null){
                try {
                    bufferIn.close();
                } catch (IOException e) {
                    throw new RuntimeException(e);
                }
            }
            if(bufferOut != null){
                try {
                    bufferOut.close();
                } catch (IOException e) {
                    throw new RuntimeException(e);
                }
            }
        }
    }
}

字符流操作

文件流的操作是基于文件的字节 实现的。字符流的操作提供了另一种通过一行字符的形式操作数据。

再文件复制时,它会将一整行的字符一次性复制过去。

它的语法流程如下

首先,定义两个File对象,srcFile和destFile。这两个对象分别代表了源文件(我们要从中读取数据的文件)和目标文件(我们要写入数据的文件)。

arduino 复制代码
// 数据源对象
File srcFile = new File("D:\Code\JAVA\collection\src\Data\testData.txt");
// 数据目的地对象
File destFile = new File("D:\Code\JAVA\collection\src\Data\testData_copy.txt");

然后,创建了两个流对象,reader和writer。这两个对象分别用于从源文件读取数据和向目标文件写入数据。

ini 复制代码
BufferedReader reader = null;  
PrintWriter writer = null;

接着,使用reader读取源文件的内容,而writer会向目标文件写入内容。

php 复制代码
try {  
    reader = new BufferedReader(new FileReader(srcFile));  
    writer = new PrintWriter(destFile);  
    ...  
} catch (IOException e) {  
    throw new RuntimeException(e);  
}

在try块中,读取源文件的内容。每次读取一行,直到文件结束(也就是没有更多的行可以读取)。每读取一行,我们都会打印这一行(在控制台输出),并且写入到目标文件。

arduino 复制代码
while ((line = reader.readLine()) != null){  
    System.out.println(line);  // 打印到控制台  
    writer.println(line);      // 写入到目标文件  
}

在读取和写入操作结束后,我们调用writer.flush()方法,确保所有待写入的数据都被立即写入到目标文件。

ini 复制代码
writer.flush();

完整代码如下:

java 复制代码
import java.io.*;

public class IO_Copy {
    public static void main(String[] args) {
        // 数据源对象
        File srcFile = new File("D:\Code\JAVA\collection\src\Data\testData.txt");
        // 数据目的地对象
        File destFile = new File("D:\Code\JAVA\collection\src\Data\testData_copy.txt");

        // 字符输入流(管道对象)
        BufferedReader reader = null;
        // 字符输出流(管道对象)
        PrintWriter writer = null;

        try {
            reader = new BufferedReader(new FileReader(srcFile));
            writer = new PrintWriter(destFile);


            // 读取文件的一行数据(字符串)
            String line = null;

            while ((line = reader.readLine()) != null){
                System.out.println(line);  // Hello
                writer.println(line);
            }
            // 刷写数据
            writer.flush();

        } catch (IOException e) {
            throw new RuntimeException(e);
        } finally {
            if(reader != null){
                try {
                    reader.close();
                } catch (IOException e) {
                    throw new RuntimeException(e);
                }
            }
        }
    }
}
相关推荐
Harold几秒前
【用户访问鉴权】Openresty实现方案
后端
用户9704438781164 分钟前
PHP 函数的参数顺序,它们是随机的吗?
后端·程序员·代码规范
3学习分享吧5 分钟前
C++从0实现百万并发Reactor服务器(完结)
后端
lssjzmn23 分钟前
java中,synchronized 关键字与 ReentrantLock 重入锁的区别以及应用场景,注意事项
java·后端
南雨北斗31 分钟前
词性
后端
南雨北斗33 分钟前
动词的类型
后端
小厂永远得不到的男人36 分钟前
ioc 原理篇
java·后端
南雨北斗1 小时前
英语音标
后端
ClouGence1 小时前
三步搞定!GaussDB 实时数据入仓
数据库·后端
whitepure1 小时前
万字详解Java枚举
java·后端