🔥个人主页: 中草药
🔭一.文件和IO
认识文件
在计算机科学中,文件是一种存储在某种持久性存储介质(如硬盘、固态硬盘或闪存等)上的数据集合。文件可以包含各种类型的信息,包括文本、图像、音频、视频、程序代码等等。每个文件通常都有一个唯一的名称来标识它,并且可能有一个扩展名来表明它的类型。
文件是组织和管理数据的基本单位之一。它们由操作系统进行管理和控制,通过文件系统来实现。文件系统定义了如何命名、存储、检索以及组织文件的方法。以下是文件的一些基本概念:
- 文件名:用于唯一标识文件的名称。
- 路径:描述文件在目录结构中的位置。
- 目录(文件夹):用于组织文件的一种方式,可以包含其他文件或子目录。
- 属性:文件的元数据,例如创建日期、修改日期、大小、权限等。
- 权限:控制谁可以访问文件以及如何访问的设置。
文件的操作包括但不限于创建、读取、更新、删除、复制、移动等。不同类型的文件需要不同的应用程序或工具来打开和处理。例如,文本文件可以用任何文本编辑器打开,而图像文件则需要图像查看器或编辑软件来处理。
了解这些基本概念对于有效地使用计算机资源非常重要。如果您有特定类型的文件或与文件相关的操作想要了解,请告诉我,我可以提供更详细的信息。
目录
在计算机文件系统中,目录(有时也称为文件夹)是用于组织和管理文件的一种逻辑容器。目录可以看作是树形结构的一个应用实例,其中每个目录都可以包含文件和其他目录(子目录)。文件系统的目录结构遵循以下特点:
- 根目录:所有其他目录和文件的最高级目录,通常表示为
/
。 - 子目录:位于另一个目录内的目录。
- 路径:用于定位文件或目录的唯一标识符,由一系列目录名组成,目录之间用斜杠
/
分隔。- 绝对路径:从根目录开始的路径。
- 相对路径:相对于当前工作目录的路径。
假设我们有一个简单的文件系统树形结构如下所示:
/
├── Documents
│ ├── Report.txt
│ └── Photos
│ └── Holiday.jpg
└── Music
├── Song1.mp3
└── AlbumArt.jpg
在这个例子中:
/
是根目录。Documents
和Music
都是根目录下的直接子目录。Photos
是Documents
目录下的子目录。Report.txt
和Holiday.jpg
分别是Documents
和Photos
目录下的文件。
文件路径
文件路径是指在文件系统中用于唯一标识文件或目录的位置信息。路径通常由一系列目录名称组成,并使用分隔符(通常是斜杠 /
或反斜杠 \
)连接起来。文件路径有两种主要类型:绝对路径和相对路径。
绝对路径
绝对路径是从文件系统的根目录开始的完整路径,它明确地指示了文件或目录的确切位置。在大多数现代操作系统中,绝对路径通常以根目录符号 /
开始。
例子
在Windows中,绝对路径看起来像这样:
C:\Users\user\Documents\example.txt
相对路径
相对路径是从当前工作目录开始的路径。它使用相对于当前位置的目录名称来指定文件或目录的位置。相对路径不从根目录开始,而是从当前目录开始。
例子
假设当前工作目录为 /home/user/documents
,那么文件 example.txt
的相对路径可能是这样的:
example.txt
如果 example.txt
存在于同一目录下的子目录 subfolder
中,则相对路径将是:
subfolder/example.txt
路径中的特殊符号
在文件路径中还有一些特殊的符号用于表示特定的含义:
.
表示当前目录。..
表示上一级目录。
使用特殊符号的例子
假设当前工作目录为 /home/user/documents
,要访问 /home/user/
下的文件 notes.txt
,可以使用相对路径:
../notes.txt
IO
输入/输出 (I/O)
输入/输出(Input/Output,简称I/O)是指计算机系统与外部环境之间进行数据交换的过程。这种交互既可以是硬件层面的,也可以是软件层面的。I/O 涉及到计算机系统与外部设备之间的数据传输,这些外部设备可以是键盘、鼠标、显示器、打印机、磁盘驱动器等。
I/O 的分类
I/O 可以根据不同的标准进行分类:
-
按方向分类:
- 输入 (Input):数据从外部设备流向计算机系统。例如,用户通过键盘输入数据。
- 输出 (Output):数据从计算机系统流向外部设备。例如,计算机将数据发送给打印机以打印文档。
-
按设备类型分类:
- 标准输入/输出:
- 标准输入 (
stdin
):默认为键盘输入。 - 标准输出 (
stdout
):默认为屏幕输出。 - 标准错误输出 (
stderr
):默认为屏幕输出,通常用于显示错误信息。
- 标准输入 (
- 文件 I/O:涉及从文件读取数据或将数据写入文件。
- 网络 I/O:涉及通过网络协议读取或写入数据,如HTTP、FTP等。
- 设备 I/O:涉及与硬件设备的交互,如磁盘驱动器、打印机等。
- 标准输入/输出:
-
按操作类型分类:
- 同步 I/O:数据的读写操作会阻塞进程或线程直到操作完成。
- 异步 I/O:数据的读写操作不会阻塞进程或线程,允许在后台完成操作。
I/O 的实现
在不同的编程语言中,I/O 操作的实现方式各不相同。下面是一些常见的编程语言中 I/O 操作的例子:
-
C/C++:
- 使用
<stdio.h>
库进行标准输入输出。 - 使用
fopen
,fclose
,fread
,fwrite
等函数进行文件 I/O 操作。
- 使用
-
Java:
- 使用
java.io
包中的类,如InputStream
,OutputStream
,FileReader
,FileWriter
等。 - 使用
System.in
和System.out
进行标准输入输出。
- 使用
-
Python:
- 使用内置的
open()
函数进行文件 I/O 操作。 - 使用
sys.stdin
和sys.stdout
进行标准输入输出。
- 使用内置的
🧪二.File
在 Java 中,java.io.File
类是一个非常重要的类,用于处理文件和目录。它提供了许多方法来获取文件信息、创建和删除文件以及目录等。File
类本身并不直接读写文件内容,而是提供了文件和目录的元数据操作,以及与其他 I/O 类(如 InputStream
和 OutputStream
)的交互接口。
构造方法
方法 | 说明 |
File(File parent,String child) | 根据父目录+孩子文件路径,创建一个新的file实例 |
File(String pathname) | 根据文件路径创建一个新的file实例,路径可以是绝对路径或者相对路径 |
File(String pathparent,String chiid) | 根据父目录+孩子文件路径,创建一个新的file实例,父目录用路径表示 |
主要方法
方法名 | 描述 | 返回值类型 |
---|---|---|
boolean canExecute() | 判断此抽象路径名表示的文件是否可执行。 | boolean |
boolean canRead() | 判断此抽象路径名表示的文件是否可读。 | boolean |
boolean canWrite() | 判断此抽象路径名表示的文件是否可写。 | boolean |
long length() | 返回此抽象路径名表示的文件的长度(以字节为单位)。 | long |
String getName() | 返回此抽象路径名的文件名。 | String |
String getParent() | 返回此抽象路径名的父目录的路径名字符串,如果没有父目录,则返回 null 。 | String |
String getPath() | 返回此抽象路径名的路径名字符串。 | String |
String getAbsolutePath() | 返回此抽象路径名的绝对路径名字符串。 | String |
String getCanonicalPath() | 返回此抽象路径名的规范路径名字符串。 | String |
boolean createNewFile() | 创建由此抽象路径名表示的新空文件。 | boolean |
boolean delete() | 删除由此抽象路径名表示的文件或目录。 | boolean |
boolean mkdir() | 创建由此抽象路径名表示的目录。 | boolean |
boolean mkdirs() | 创建由此抽象路径名表示的目录及其所有不存在的父目录。 | boolean |
boolean renameTo(File dest) | 将由此抽象路径名表示的文件重新命名为此参数表示的文件。 | boolean |
boolean setExecutable(boolean executable) | 设置此抽象路径名表示的文件的可执行属性。 | boolean |
boolean setReadable(boolean readable) | 设置此抽象路径名表示的文件的可读属性。 | boolean |
boolean setWritable(boolean writable) | 设置此抽象路径名表示的文件的可写属性。 | boolean |
long lastModified() | 返回此抽象路径名表示的文件最后一次修改的时间。 | long |
boolean exists() | 测试由此抽象路径名表示的文件或目录是否存在。 | boolean |
boolean isDirectory() | 测试由此抽象路径名表示的文件是否是一个目录。 | boolean |
boolean isFile() | 测试由此抽象路径名表示的文件是否是一个普通文件。 | boolean |
boolean isHidden() | 测试由此抽象路径名表示的文件是否是一个隐藏文件。 | boolean |
String[] list() | 返回一个字符串数组,包含此抽象路径名表示的目录中的文件和目录。 | String[] |
File[] listFiles() | 返回一个 File 数组,包含此抽象路径名表示的目录中的文件和目录。 | File[] |
注意事项
- 异常处理:当执行文件操作时,通常需要处理
IOException
。 - 文件路径:在跨平台环境中使用
File
类时,需要确保路径正确处理不同操作系统中的路径分隔符(Windows 使用\
,Unix/Linux 使用/
)。 - 性能考虑:频繁的文件操作可能会导致性能下降,尤其是在高负载的应用场景中。
🔬三.文件内容的读写——数据流
在 Java 中,读写文件内容通常通过数据流(Data Streams)来完成。数据流是用于在源和目标之间传输数据的对象。Java 提供了多种流来处理不同的数据类型和操作,包括字节流和字符流。
字节流 vs. 字符流
-
字节流:处理原始字节数据,通常用于处理二进制文件。
InputStream
:读取字节流的基类。OutputStream
:写入字节流的基类。
-
字符流:处理字符数据,通常用于处理文本文件。
Reader
:读取字符流的基类。Writer
:写入字符流的基类。
InputStream
在 Java 中,InputStream
是所有字节输入流的基类。它定义了一系列方法用于从数据源读取字节,并提供了基本的字节流操作。InputStream
是一个抽象类,不能直接实例化,但它定义了所有子类必须实现的方法。
主要方法
方法名 | 描述 | 返回值类型 |
---|---|---|
int read() | 从输入流中读取下一个字节的数据。如果没有更多的字节可以读取,则返回 -1 。 | int |
int read(byte[] b) | 从输入流中读取最多 b.length 个字节的数据到字节数组 b 中。返回实际读取的字节数,如果没有更多的字节可以读取,则返回 -1 。 | int |
int read(byte[] b, int off, int len) | 从输入流中读取最多 len 个字节的数据到字节数组 b 中,从索引 off 开始存放。返回实际读取的字节数,如果没有更多的字节可以读取,则返回 -1 。 | int |
void close() | 关闭此输入流并释放与之关联的所有系统资源。 | void |
其他方法
方法名 | 描述 | 返回值类型 |
boolean markSupported() | 返回 true 如果此输入流支持标记和重置方法,否则返回 false 。 | boolean |
void mark(int readlimit) | 设置此输入流的标记点。参数 readlimit 指定在下一次调用 reset() 方法之前可以读取的字节数,而不使标记失效。 | void |
void reset() | 使此输入流重置到上次调用 mark() 方法设置的标记点。如果此流不支持标记和重置,则抛出 IOException 。 | void |
long skip(long n) | 跳过并丢弃输入流中的 n 个字节。返回实际跳过的字节数。 | long |
void transferTo(OutputStream out) | 将此输入流中的剩余内容传输到指定的输出流 out 。自 Java 7 起可用。 | void |
举例
public static void main(String[] args) {
try (InputStream inputStream = new FileInputStream("./test.txt")) {
while (true) {
byte[] buffer = new byte[1024];
// n 返回值表示 read 操作, 实际读取到多少个字节.
int n = inputStream.read(buffer);
if (n == -1) {
break;
}
for (int i = 0; i < n; i++) {
System.out.printf("0x%x\n", buffer[i]);
}
}
} catch (IOException e) {
e.printStackTrace();
}
}
子类
InputStream
类有许多子类,用于处理不同的数据源和流类型。这里列举几个常见的子类:
-
FileInputStream
:- 用于读取文件中的字节数据。
-
ByteArrayInputStream
:- 用于从字节数组中读取数据。
-
BufferedInputStream
:- 提供了一个带有缓冲区的
InputStream
实现,可以提高读取效率。
- 提供了一个带有缓冲区的
-
ObjectInputStream
:- 用于反序列化对象。
-
PipedInputStream
:- 与
PipedOutputStream
一起用于线程间的通信。
- 与
使用技巧
- 资源管理:使用
try-with-resources
语句可以自动关闭流,避免资源泄露。 - 异常处理:在处理文件流时,通常需要捕获和处理
IOException
。 - 缓冲:使用
BufferedInputStream
可以提高读取效率。
OutputStream
在 Java 中,OutputStream
类是所有字节输出流的基类。它定义了一系列方法用于向目的地写入字节数据,并提供了基本的字节流操作。OutputStream
是一个抽象类,不能直接实例化,但它定义了所有子类必须实现的方法。
主要方法
法名 | 描述 | 返回值类型 |
---|---|---|
void write(int b) | 向输出流写入指定的字节。参数 b 必须是一个 0 到 255 之间的整数。 | void |
void write(byte[] b) | 向输出流写入 b.length 个字节的数据。参数 b 是一个字节数组。 | void |
void write(byte[] b, int off, int len) | 向输出流写入 len 个字节的数据,从字节数组 b 的索引 off 开始。 | void |
void flush() | 清空此输出流并强制写出所有缓冲的输出字节。 | void |
void close() | 关闭此输出流并释放与之关联的所有系统资源。 | void |
重要:我们知道 I/O 的速度是很慢的,所以,大多的 OutputStream 为了减少设备操作的次数,在写数据的时候都会将数据先暂时写入内存的⼀个指定区域里,直到该区域满了或者其他指定条件时才真正将数据写入设备中,这个区域⼀般称为缓冲区。但造成⼀个结果,就是我们写的数据,很可能会遗留⼀部分在缓冲区中。需要在最后或者合适的位置,调用 flush(刷新)操作,将数据刷到设备中。
举例
import java.io.FileOutputStream;
import java.io.IOException;
public class OutputStreamExample {
public static void main(String[] args) {
try (FileOutputStream fos = new FileOutputStream("example.txt")) {
// 写入一些文本
byte[] data = "Hello, this is a test.".getBytes();
fos.write(data);
// 写入单个字节
fos.write(10); // ASCII for newline character
} catch (IOException e) {
e.printStackTrace();
}
}
}
子类
OutputStream
类有许多子类,用于处理不同的数据目的地和流类型。这里列举几个常见的子类:
-
FileOutputStream
:- 用于写入文件中的字节数据。
-
ByteArrayOutputStream
:- 用于向字节数组写入数据。
-
BufferedOutputStream
:- 提供了一个带有缓冲区的
OutputStream
实现,可以提高写入效率。
- 提供了一个带有缓冲区的
-
ObjectOutputStream
:- 用于序列化对象。
-
PipedOutputStream
:- 与
PipedInputStream
一起用于线程间的通信。
- 与
使用技巧
- 资源管理:使用
try-with-resources
语句可以自动关闭流,避免资源泄露。 - 异常处理:在处理文件流时,通常需要捕获和处理
IOException
。 - 缓冲:使用
BufferedOutputStream
可以提高写入效率。
🔭四.操作实例
实例1
复制初文件
代码
import java.io.*;
import java.util.Scanner;
public class demo2 {
/**
* 复制文件
* @param args
*/
public static void main(String[] args) {
//1.输入路径,并且做校验
Scanner sc = new Scanner(System.in);
System.out.println("请输入源文件的路径");
String srcPath = sc.nextLine();
File srcFile = new File(srcPath);
if (!srcFile.isFile()) {
System.out.println("源文件路径不存在");
return;
}
System.out.println("请输入目标文件的路径");
String destPath = sc.nextLine();
File destFile = new File(destPath);
if (!destFile.getParentFile().isDirectory()) {
System.out.println("目标文件路径不存在");
return;
}
//2.执行复制过程
try (InputStream inputStream = new FileInputStream(srcFile);
OutputStream outputStream = new FileOutputStream(destFile)) {
while(true){
byte[] buffer=new byte[1024];
int n=inputStream.read(buffer);
if(n==-1){
break;
}
outputStream.write(buffer,0,n);
}
} catch (IOException e) {
e.printStackTrace();
}
System.out.println("复制文件完成");
}
}
运行结果
初始
运行后
实例2
在目录中搜索含有相关关键字的文件
代码
import java.io.*;
import java.util.Scanner;
public class demo3 {
private static void scan(File currentFile,String key){
if (!currentFile.isDirectory()){
return;
}
File[] files=currentFile.listFiles();
if (files.length==0||files==null){
return;
}
for (File f:files) {
if (f.isFile()){
doSearch(f,key);
}else{
scan(f,key);
}
}
}
private static void doSearch(File f,String key){
StringBuilder stringBuilder=new StringBuilder();
try(Reader reader=new FileReader(f)) {
char[] buffer=new char[1024];
while(true){
int n= reader.read(buffer);
if (n==-1){
break;
}
String s=new String(buffer,0,n);
stringBuilder.append(s);
}
} catch (IOException e) {
e.printStackTrace();
}
if (stringBuilder.indexOf(key)==-1){
//未找到
return;
}
System.out.println("找到了匹配的文件:"+f.getAbsolutePath() );
}
public static void main(String[] args) {
Scanner sc=new Scanner(System.in);
System.out.println("请输入你要搜索的路径:");
String rootPath=sc.nextLine();
File rootFile=new File(rootPath);
if (!rootFile.isDirectory()){
System.out.println("要搜索的路径有误");
return;
}
System.out.println("请输入你要查询的内容");
String key=sc.next();
scan(rootFile,key);
}
}
运行结果
🧬五.总结与反思
大鹏一日同风起,扶摇直上九万里。
在深入学习Java中的文件操作之后,我对文件读写、文件流、路径处理等方面有了更深刻的理解。通过实践和探索,我对文件操作有了更全面的认识。
通过这次学习,我对Java中的文件操作有了更深刻的理解,并认识到在实际应用中需要综合考虑多种因素。在未来的开发工作中,我将继续深入研究这些知识点,并努力将它们应用到实践中去。学习文件操作不仅提高了我的编程技能,还让我意识到了在设计文件处理程序时需要考虑的诸多因素。
🍀🍀🍀🍀🍀🍀🍀🍀🍀🍀🍀🍀🍀🍀🍀🍀🍀🍀🍀🍀🍀🍀🍀🍀🍀🍀🍀🍀
以上,就是本期的全部内容啦,若有错误疏忽希望各位大佬及时指出💐
制作不易,希望能对各位提供微小的帮助,可否留下你免费的赞呢🌸