文章目录
一、文件概述
广义的文件
文件不仅仅是普通意义上的文件,网卡/显示器/键盘,都被抽象成了文件,因此文件操作在网络编程方面,应用非常广泛
文件的分类
文件可以分为二进制文件和文本文件,文本文件用字符保存(底层还是二进制),二进制文件用字节保存
相对路径和绝对路径
- 绝对路径:以盘符开头
- 相对路径:以.或者…开头的,.表示当前路径(比如./java.exe),…表示当前路径的父目录(上级路径)(比如…/src.txt)
相对路径的基准路径如何确认
- 如果是通过命令行的方式,那么执行命令所在的目录,就是基准目录
- 如果是通过IDEA的方式来运行程序,此时基准路径就是当前 java 项目所在的路径
- 把一个 java 打成 war 包,放到 tomcat 上运行。这种情况基准路径就是 tomcat 的 bin 目录
操作文件
- 文件系统相关的操作
是指通过“文件资源管理器”能够完成的一些功能,比如- 列出目录中有哪些文件
- 创建文件
- 创建目录
- 删除文件
- 重命名文件
二、File类
Java 中通过 java.io.File
类来对一个文件(包括目录)进行抽象的描述。注意,有 File 对象,并不代表真实存在该文件
(一)概述
- File类的一个对象,代表一个文件或者一个文件目录(文件夹)
- File类声明在java.io包下
- File类中涉及到关于文件或文件目录的创建、删除、重命名、修改时间、文件大小等方法,并未涉及到写入或读取文件内容的操作,如果需要读取或写入文件内容,必须使用io流来完成
- 后续File类的对象通常会作为参数传递到流的构造器中,指明读取或写入的终点
1. 创建实例
包括相对路径的创建和绝对路径的创建,以及查看文件路径和文件名,父文件名
public static void main(String[] args) throws IOException {
File file = new File("d:/test.txt");
System.out.println(file.getParent()); //获取到文件父目录
System.out.println(file.getName()); //获取到文件的名字
System.out.println(file.getPath()); //获取到文件路径
System.out.println(file.getAbsolutePath()); //获取到文件绝对路径
System.out.println(file.getCanonicalPath()); //获取到文件绝对路径(需要处理异常)
System.out.println("============");
File file2 = new File("./test.txt");
//如果文件是使用相对路径,那么父路径就是.
System.out.println(file2.getParent()); //获取到文件父目录
System.out.println(file2.getName()); //获取到文件的名字
//相对路径
System.out.println(file2.getPath()); //获取到文件路径
//这种得到绝对路径的方式是把前面的绝对路径和当前相对路径拼接起来
//比如:D:\study\gitee上传代码\java-se_-study\J-FileTest\.\test.txt
System.out.println(file2.getAbsolutePath()); //获取到文件绝对路径
//这种得到绝对路径的方式是化简过的绝对路径
//比如:D:\study\gitee上传代码\java-se_-study\J-FileTest\test.txt
System.out.println(file2.getCanonicalPath()); //获取到文件绝对路径(需要处理异常)
}
2.常用方法
/*
public String getAbsolutePath():获取绝对路径
public String getPath():获取路径
public String getName():获取名称
public String getParent():获取上层文件目录路径。若无,返回null
public long length():获取文件长度(即字节数)。不能获取目录长度
public long lastModified():获取最后一次的修改时间,毫秒值
适用于文件目录
public String list():获取指定目录下的所有文件或文件目录的名称数组
public File[] listFiles():获取指定目录下的所有文件或文件目录的File数组
*/
@Test
public void test2(){
File file=new File("C:\\Users\\86130\\IdeaProjects\\untitled2\\src\\learning way");
String[] list= file.list();
for(String i:list){
System.out.println(i);
}
System.out.println("******************");
File[] files=file.listFiles();
for(File f:files){
System.out.println(f);
}
}
@Test
public void test3(){
/*
public boolean renameTo(File dest):把文件重命名为指定的文件路径
比如:file1.renameTo(file2)为例:
想要保证返回true,需要file1在硬盘中是存在的,且在file2不能在硬盘中存在
*/
File file1=new File("hello.txt");//模块下的文件
File file2=new File("C:\\Users\\86130\\IdeaProjects\\untitled2\\src\\learning way\\hi.txt");
boolean renameTo=file2.renameTo(file1);
System.out.println(renameTo);
}
/*
public boolean idDirectory():判断是否是我呢见目录
public boolean isFile():判断的是否是文件
Public boolean exists():判断是否存在
public boolean canRead():判断是否可读
public boolean canWrite():判断是否可写
public boolean isHidden():判断是否隐藏
*/
/*
创建硬盘中对应的文件或文件目录
public boolean createNewFile():创建文件。若文件存在,则不创建,返回false
public boolean mkdir():创建文件目录。如果此文件目录存在,就不创建,如果此文件目录的上层文件目录不存在,返回false
public boolean mkdirs():创建文件目录,如果上层文件目录不存在,一并创建
删除磁盘中的文件或文件目录
public boolean delete():删除文件或者文件夹
删除注意事项
java中的删除不走回收站
*/
三、文件内容的读写——数据流
针对文件的读写,Java标准库提供了一组类
字节流对象,针对二进制文件,以字节为单位进行读写
读 | ImputStream |
---|---|
写 | OutputStream |
字符流对象,针对文本文件,是以字符为单位进行读写的
读 | Reader |
---|---|
写 | Writer |
以上类都是抽象类,实际使用的往往是这些类的子类。而抽象类中的方法既可以针对普通文件的读写,也可以针对特殊文件(网卡,socket文件)进行读写,而下面这几个子类,只能针对普通文件进行读写
字节读 | FileImputStream |
---|---|
字节写 | FileOutputStream |
字符读 | FileReader |
字符写 | FileWriter |
(一)按照字节读文件
代码示例:
public static void main(String[] args) {
//构造方法中需要指定打开文件的路径
//此处的路径可以是绝对路径,相对路径,File对象
/*InputStream inputStream = null;
try {
//1. 创建对象,同时也是打开文件
inputStream = new FileInputStream("d:/test.txt");
//2. 尝试一个字节一个字节读,把整个文件都读完
while(true) {
int ret = inputStream.read();
//-1表示读到了文件末尾
if(ret == -1) {
break;
}
System.out.println(ret);
}
} catch (IOException e) {
throw new RuntimeException(e);
} finally {
//3. 关闭文件
try {
inputStream.close();
} catch (IOException e) {
throw new RuntimeException(e);
}
}*/
//一次读一个字节
//要想放到try的括号里面,需要实现Closeable这个interface
//这里所有的流对象,都实现了Closeable
/*try(InputStream inputStream = new FileInputStream("d:/test.txt")) {
while(true) {
int ret = inputStream.read();
if(ret == -1) {
break;
}
System.out.println(ret);
}
} catch (IOException e) {
throw new RuntimeException(e);
}*/
//一次读多个字节,读磁盘的速度很慢,因此这种比上面一个一个读的效率要高不少
try(InputStream inputStream = new FileInputStream("d:/test.txt")) {
while(true) {
byte[] buffer = new byte[1024];
//此时的返回值表示传输到buffer中的字节数
//buffer称为输出型参数
int len = inputStream.read(buffer);
//返回-1表示文件读取结束
if(len == -1) {
break;
}
for(int i = 0; i < len; i++) {
System.out.println(buffer[i]);
}
}
} catch (IOException e) {
throw new RuntimeException(e);
}
}
(二)按照字节写数据
代码示例:
public static void main(String[] args) {
//每次按照写方式打开文件,都会清空原有文件的内容
try(OutputStream outputStream = new FileOutputStream("d:/test.txt")) {
/*outputStream.write(99);
outputStream.write(100);*/
byte[] buffer = {99,100,101};
outputStream.write(buffer);
} catch (IOException e) {
e.printStackTrace();
}
}
(三)按照字符读文件
代码示例:
public static void main(String[] args) {
try(Reader reader = new FileReader("d:/test.txt")) {
while(true) {
char[] buffer = new char[20];
int len = reader.read(buffer);
if(len == -1) {
break;
}
/*for (int i = 0; i < len; i++) {
System.out.println(buffer[i]);
}*/
String s = new String(buffer, 0, len);
System.out.println(s);
}
} catch(IOException e) {
e.printStackTrace();
}
}
(四)按照字符写文件
代码示例:
public static void main(String[] args) {
try(Writer writer = new FileWriter("d:/test.txt")) {
writer.write("abcdef");
} catch(IOException e) {
e.printStackTrace();
}
}
(五)刷新缓冲区(.flash)
java中public void flush()
用于刷新流的缓冲,这个时候还可以继续写入/读入
缓冲区存在的意义就是为了提高效率,CPU读取内存的速度大大高于硬盘,因此与其一次读一点,不如把数据攒一堆,统一一次写完或读完,数据没有写到文件里,只写到缓冲区,一般close就会触发刷新缓冲区,但是标准库也为我们提供了方法,可以手动刷新
四、文件IO练习
(一)进行普通文件的复制
代码示例:
public static void main(String[] args) {
//1.输入两个路径
Scanner scanner = new Scanner(System.in);
System.out.print("请输入要拷贝的原路径:");
String src = scanner.nextLine();
File srcFile = new File(src);
if(srcFile.isDirectory()) {
System.out.println("输入的路径有误!");
return;
}
System.out.print("请输入目标路径:");
String dest = scanner.nextLine();
//不需要检查目标文件是否存在,OutputStream写文件时会自动创建不存在的文件
//2. 读取源文件,拷贝到目标文件中
try(InputStream inputStream = new FileInputStream(src)) {
try(OutputStream outputStream = new FileOutputStream(dest)) {
//把inputStream中的数据都读出来,写到outputStream中
byte[] buffer = new byte[1024];
while(true) {
int len = inputStream.read(buffer);
if(len == -1) {
//拷贝结束
System.out.println("拷贝完成!");
break;
}
//需要指定拷贝buffer中的范围
outputStream.write(buffer, 0, len);
}
}
} catch(IOException e) {
e.printStackTrace();
}
}
(二)扫描目录文件,进行文件内容的查找
- 找到名称或者内容中包含指定字符的所有普通文件。找到了就把对应的文件路径打印出来
代码示例:
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
//输入源路径
System.out.print("请输入目标目录:");
String src = scanner.nextLine();
File srcFile = new File(src);
if(!srcFile.isDirectory()) {
System.out.println("输入的文件目录有误");
return;
}
//输入要查找的内容
System.out.print("请输入查找内容:");
String dest = scanner.nextLine();
scanDir(srcFile, dest);
}
private static void scanDir(File srcFile, String dest) {
File[] files = srcFile.listFiles();
if(files == null) {
return;
}
for (File f:files) {
if(f.isDirectory()) {
scanDir(f, dest);
continue;
}
if(containsDest(f, dest)) {
try {
System.out.println(f.getCanonicalPath());
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}
}
private static boolean containsDest(File f, String dest) {
StringBuilder stringBuilder = new StringBuilder();
try(Reader reader = new FileReader(f)) {
String name = f.getName();
if(name.contains(dest)) {
return true;
}
char[] buffer = new char[1024];
while(true) {
int len = reader.read(buffer);
if(len == -1) {
//读取结束,未找到目标字符串
break;
}
stringBuilder.append(buffer, 0, len);
}
} catch (IOException e) {
e.printStackTrace();
}
return stringBuilder.indexOf(dest) != -1;
}