Java 中通过 java.io.File 类来对一个文件(包括目录)进行抽象的描述。注意,有 File 对象,并不
代表真实存在该文件
我们先来看看 File 类中的常见属性、构造方法和方法
方法
修饰符及返回 值类型 |
方法签名 | 说明 |
String | getParent() | 返回 File 对象的父目录文件路径 |
String | getName() | 返回 FIle 对象的纯文件名称 |
String | getPath() | 返回 File 对象的文件路径 |
String | getAbsolutePath() | 返回 File 对象的绝对路径 |
String | getCanonicalPath() | 返回 File 对象的修饰过的绝对路径 |
boolean | exists() | 判断 File 对象描述的文件是否真实存在 |
boolean | isDirectory() | 判断 File 对象代表的文件是否是一个目录 |
boolean | isFile() | 判断 File 对象代表的文件是否是一个普通文件 |
boolean | createNewFile() | 根据 File 对象,自动创建一个空文件。成功创建后返 回 true |
boolean | delete() | 根据 File 对象,删除该文件。成功删除后返回 true |
void | deleteOnExit() | 根据 File 对象,标注文件将被删除,删除动作会到 JVM 运行结束时才会进行 |
String[] | list() | 返回 File 对象代表的目录下的所有文件名 |
File[] | listFiles() | 返回 File 对象代表的目录下的所有文件,以 File 对象 表示 |
boolean | mkdir() | 创建 File 对象代表的目录 |
boolean | mkdirs() | 创建 File 对象代表的目录,如果必要,会创建中间目 录 |
boolean | renameTo(File dest) |
进行文件改名,也可以视为我们平时的剪切、粘贴操 作 |
boolean | canRead() | 判断用户是否对文件有可读权限 |
boolean | canWrite() | 判断用户是否对文件有可写权限 |
在标准库中,提供的读写文件的流对象,归结到两个大类中
每次读写的最小单位是"字节"
返回值看起来是int,实际上也是byte.0-255之间的数据,使用-1表示读到文件的末尾
public class Demo10 {
public static void main(String[] args) {
try(InputStream inputStream = new FileInputStream ( "D:/test.txt" )) {
byte[] buffer = new byte[1024];
int n = inputStream.read(buffer);
System.out.println ("n="+n);
for (int i = 0; i < n; i++) {
System.out.printf ("%x\n",buffer[i]);
}
} catch (IOException e) {
e.printStackTrace ();
}
}
}
public class Demo11 {
public static void main(String[] args) {
try(OutputStream outputStream = new FileOutputStream ( "d:/test.txt",true )){//和Writer类似,OutputStream打开一个文件,就会清空文件原有的内容,如果不想清空,就写入追加写方式(在构造方法中,第二个参数传入true)
String str = "你好世界";
outputStream.write(str.getBytes( StandardCharsets.UTF_8 ));
} catch (IOException e) {
e.printStackTrace ();
}
}
}
每次读写的最小单位是"字符"
本质上是针对字节流进行有一层封装
字符流,就能自动把文件中几个相邻的字节,转换成一个字符
GBK,一个中文字符 两个字节
UTF8,一个中文字符 三个字节
无参read:一次读一个字符
一个参数read:一次读取若干个字符,会把参数指定的cbuf数组给填充满
三个参数read:一次读取若干个字符,会把参数指定的cbuf数组,中的off这个位置开始,到len这么长的范围填满
cbuf是"输出型参数":read里转入的是一个空的字符数组,然后read方法内部,对这个数组进行填充
在java标准库内部,对于字符编码进行了很多的处理工作
如果只使用char,此时使用的是字符集,固定是unicode
如果使用String,此时就会自动的把每个字符的unicode转为utf8
public class Demo7 {
public static void main(String[] args) throws IOException {
Reader reader = new FileReader ( "D:/test.txt" );
//1.一次read读一个字符
while (true) {
int c = reader.read ();
if (c == -1) {
break;
}
char ch = (char) c;
System.out.println ( "c=" + c );
System.out.println ( ch );
}
//2.一次read读多个字符
try {
while (true) {
char[] cbuf = new char[1];
int n = reader.read ( cbuf );
if (n == -1) {
break;
}
System.out.println ( "n=" + n );
for (int i = 0; i < n; i++) {
System.out.println ( cbuf[i] );
}
}
} finally {
reader.close();
}
//3.一个文件使用完后,记得要close
//reader.close();//一旦上面的逻辑抛出异常,就会导致close执行不到
//使用close方法,最主要的目的是为了释放文件描述符
//PCB包含很多属性
//1.pid
//2.内存指针
//3.文件描述符表 顺序表(数组) 一个进程每次打开一个文件,就需要在这个表里分配一个元素,这个数组的长度是存在上限的
//这就是文件资源泄露问提
//java有GC,不需要手动释放
//但是文件还是要手动释放
}
}
public class Demo8 {
public static void main(String[] args) throws IOException {
try(Reader reader = new FileReader ( "D:/test.txt" )) {//try with recourses 这个语法目的就是()定义的变量会在try代码块结束时(正常结束,还是抛出异常)自动调用其中的close方法
//要求写到()里的对象必须要实现Closeable接口
while (true) {
char[] cbuf = new char[1];
int n = reader.read ( cbuf );
if (n == -1) {
break;
}
System.out.println ( "n=" + n );
for (int i = 0; i < n; i++) {
System.out.println ( cbuf[i] );
}
}
}
}
}
一次写一个字符
一次写一个字符串
一次写多个字符(字符数组)
public class Demo9 {
public static void main(String[] args) throws IOException {
try(Writer writer = new FileWriter ( "d:/test.txt",true )){
writer.write("hello world");//Writer写入文件,默认会把原有的文件内容清空掉,如果不想清空就要在构造方法中多加个参数
}
}
}
java标准库中支持的流流对象,种类很多,功能也非常丰富,上述介绍的,只是基础的四个流对象的使用
构造方法 | 说明 |
Scanner(InputStream is, String charset) | 使用 charset 字符集进行 is 的扫描读取 |
public class Demo12 {
public static void main(String[] args) {
try(InputStream inputStream =new FileInputStream ( "D:/test.txt" )) {
//此时scanner就是从文件读取了
Scanner scanner = new Scanner ( inputStream );
String s = scanner.next();
System.out.println (s);
} catch (IOException e) {
e.printStackTrace ();
}
}
}
上述,我们其实已经完成输出工作,但总是有所不方便,我们接来下将 OutputStream 处理下,使用PrintWriter 类来完成输出,因为PrintWriter 类中提供了我们熟悉的 print/println/printf 方法
public class Demo13 {
public static void main(String[] args) {
try(OutputStream outputStream =new FileOutputStream ( "d:/test.txt" )) {
//这相当于把字节流转换为字符流了
PrintWriter writer = new PrintWriter ( outputStream );
writer.println ("你好");
} catch (IOException e) {
e.printStackTrace ();
}
}
}
程序已经写入字符串,但文件还是空着的,为什么呢?
这涉及到"缓冲区"
PrintWriter这样的类,在进行写入的时候,不一定是直接写入硬盘,而是先把数据写入到一个内存构成的"缓冲区"(buffer)中
引入缓冲区是为了提高效率
相当于垃圾堆成一堆再一起去楼下扔掉
当我们把数据写入缓冲区后,如果还没来得及把缓冲区的数据写入硬盘,进程就结束了,此时数据就丢失了.
为了能够确保数据确实被写入硬盘
就应该在合适的时机使用flush方法,手动刷新缓冲区
public class Demo13 {
public static void main(String[] args) {
try(OutputStream outputStream =new FileOutputStream ( "d:/test.txt" )) {
//这相当于把字节流转换为字符流了
PrintWriter writer = new PrintWriter ( outputStream );
writer.println ("你好");
writer.flush ();
} catch (IOException e) {
e.printStackTrace ();
}
}
}
需要手动使用flush方法确保数据确实被写入硬盘--"刷新缓冲区"
扫描指定目录,并找到名称中包含指定字符的所有普通文件(不包含目录),并且后续询问用户是否要删除该文件
public class Demo14 {
public static void main(String[] args) {
Scanner scanner = new Scanner ( System.in );
//1.先让用户输入一个要扫描的目录
System.out.println ("请输入要扫描的根目录:");
String path = scanner.next ();
File rootPath = new File ( path );
if (!rootPath.isDirectory ()){
System.out.println ("输入的文件路径有误");
return;
}
//2.再让用户输入一个要查询的关键词
System.out.println ("请输入要找的文件名中的字符:");
String word = scanner.next ();
//3.可以进行递归扫描
//通过这个方法进行递归
scanDir(rootPath,word);
}
private static void scanDir(File rootPath , String word) {
//1.先列出rootPath中所有的文件和目录
File[] files = rootPath.listFiles ();
if(files == null){
//当前目录为null,就可以直接返回了
return;
}else{
for(File file:files){
System.out.println (file.getAbsolutePath ());
if(file.isDirectory ()){
scanDir ( file,word );
}else{
checkDelete(file,word);
}
}
}
}
private static void checkDelete(File file , String word) {
if(file.getName ().contains ( word )){
System.out.println ("当前文件为"+file.getAbsolutePath ());
System.out.println ("请确认是否要删除(y/n):");
Scanner scanner = new Scanner ( System.in );
String choice = scanner.next ();
if (choice.equals ( "Y" )||choice.equals ( "y" )){
file.delete ();
System.out.println ("删除完毕");
}else{
System.out.println ("取消删除");
}
}else{
return;
}
}
}
进行普通文件的复制
public class Demo15 {
public static void main(String[] args) throws IOException {
Scanner scanner = new Scanner ( System.in );
System.out.println ("请输入要复制的文件路径:");
String srcPath = scanner.next ();
File srcFile = new File ( srcPath );
if(!srcFile.exists ()){
System.out.println ("文件不存在,请确认路径是否正确");
return;
}
if(!srcFile.isFile ()){
System.out.println ("文件不是普通文件,请确认路径是否正确");
return;
}
System.out.println ("请输入要复制到的目标路径:");
String destPath = scanner.next ();
File destFile = new File ( destPath );
if(destFile.exists ()){
if(destFile.isDirectory ()){
System.out.println ("目标路径已经存在,并且是一个目录,请确认路径是否正确");
return;
}
if(destFile.isFile ()){
System.out.println ("目标路径已经存在,是否进行覆盖?y/n");
String choice = scanner.next ();
if(!choice.toLowerCase ().equals ( "y" )){
System.out.println ("停止复制");
return;
}
}
}
try(InputStream inputStream = new FileInputStream ( srcFile )){
try(OutputStream outputStream = new FileOutputStream ( destFile )){
byte[] buf = new byte[1024];
int len;
while(true){
len = inputStream.read (buf);
if (len==-1){
break;
}
outputStream.write ( buf ,0,len);
}
outputStream.flush ();
}
}
System.out.println ("复制完成");
}
}
扫描指定目录,并找到名称或者内容中包含指定字符的所有普通文件(不包含目录)
public class Demo17 {
public static void main(String[] args) throws IOException {
Scanner scanner = new Scanner ( System.in );
System.out.print ( "请输入要扫描的根目录(绝对路径 OR 相对路径): " );
String rootDirPath = scanner.next ();
File rootDir = new File ( rootDirPath );
if (!rootDir.isDirectory ()) {
System.out.println ( "您输入的根目录不存在或者不是目录,退出" );
return;
}
System.out.print ( "请输入要找出的文件名中的字符: " );
String token = scanner.next ();
List result = new ArrayList<> ();
// 因为文件系统是树形结构,所以我们使用深度优先遍历(递归)完成遍历
scanDir ( rootDir , token , result );
System.out.println ( "共找到了符合条件的文件 " + result.size () + " 个,它们分别 是" );
for (File file : result) {
System.out.println ( file.getCanonicalPath () );
}
}
private static void scanDir(File rootDir , String token , List result) {
File[] files = rootDir.listFiles ();
if (files == null || files.length == 0) {
return;
}
for (File file : files) {
if (file.isDirectory ()) {
scanDir ( file , token , result );
} else {
if (file.getName ().contains ( token )) {
result.add ( file.getAbsoluteFile () );
}
}
}
}
}