一.文件的概念:
狭义的文件指:硬盘上的文件和目录
广义上的文件指:计算机中的很多软硬件资源
路径:
绝对路径:以c:d盘符开头的路径,比如c:/Intel/Logs/text.txt
相对路径:以当前所在的目录为基准,以.或者..开头(.)有时候可以省略,找到指定的路径
假设当前的工作目录是c:/MyDrivers,定位到update这个目录,就可以表示成./update
同样是定位到c:/相对路径写作./MyDrivers/update,如果工作目录是c:/MyDrivers,相对路径写作./update,如果工作目录是c:/MyDrivers/tmp,相对路径写作../update,如果工作目录是c:/MyDrivers/tmp/123,相对路径写作../../update。
二.Java对于文件的操作:
针对文件系统的操作(文件的创建,删除,重命名)
针对文件内容操作(文件的读和写)
java标准库,提供了一个File这个类
public static void main(String[] args)throws IOException {
File file = new File("c:/intel/Logs/cat.txt");//这个文件不一定是真实存在的
System.out.println(file.getName());//文件名
System.out.println(file.getPath());
System.out.println(file.getAbsolutePath());//文件的绝对路径
System.out.println(file.getParent());//除去文件名的路径
System.out.println(file.isFile());//判断是不是文件
System.out.println(file.isDirectory());//判断是不是目录
file.createNewFile();//创建这样的一个文件出来
System.out.println(file.isFile());
file.delete();//删除改文件
}
public static void main(String[] args) {
File file=new File("d:/123/giteedemo/util11/in");//创建1级目录
file.mkdir();
}
public static void main(String[] args) {
File file=new File("./in/aaa/bbb");
file.mkdirs();
}
三.针对文件内容:使用流对象进行操作
Java标准库的流对象从类型上分为两个大类:
字节流:
InputStream FileInputStream
OutputStream FileOutputStream
字符流
Reader FileReader
Writer FileWriter
这些类的使用方式非常固定,核心就是四个操作
打开文件(构造对象)
关闭文件(close)
读文件(read) 针对InputStream/Reader
写文件(write)针对OutputStream/Writer
public static void main(String[] args)throws IOException {
InputStream inputStream=new FileInputStream("c:/Intel/text2.txt");
while(true)
{
int a=inputStream.read();
if(a==-1)//表示读取到文件末尾
{
break;
}
System.out.println((byte)a);//实际上每次读一个字节我们再强转成byte
}
inputStream.close();
}
read 无参数版本,一次读一个字节
read 一个参数版本,把读到的内容填充到参数的这个字节数组里,只要字节数够每次读取的就是这个数组的大小,假设数组大小是1024,那么要读取的数据字节数够的情况下,一次就读124,如果不够124个字节的话,那么这次读取,剩下多少就读多少
public static void main(String[] args)throws IOException {
try(InputStream inputStream=new FileInputStream("c:/MyDrivers/pig.jpg"))
{
while(true)
{
byte[]buffer=new byte[1024];//每次尽力读1024
int length=inputStream.read(buffer);//返回每次读取的字节数
System.out.println(length);
if(length==-1)
{
break;
}
//for(int i=0;i<length;i++)//文件读取的内容
//{
// System.out.printf("%x\n",(byte)buffer[i]);
// }
}
}
}
最后读取的时候只有377个字节了,所以只读了377个。
像这种读取方式有什么好处呢?单次IO操作,是要访问硬盘/IO设备,单次操作就是比较消耗时间的,如果频繁进行这样的IO操作,肯定就更耗时了,单次IO时间是一定的,如果能缩短IO的次数,此时就可以提高程序的整体效率了,每次只读一个字节,循环次数很高,read次数也很高,但一次 读1024个字节,循环次数就降低很多了,read次数变少了。
read(三个参数版本类型)和一个参数版本类似,往数组里的一部分区间里填充
read的无参数版本,按理说每次读取的是一个字节,返回一个byte就行了,但是实际上返回的是int,因为除了要表示byte里的(-128-127)这样的情况之外,还需要表示一个特殊情况-1,这个情况表示读取文件结束了(读到文件末尾了)
我们来看一下文件里内容是什么
我们再来看一下打印结果
我们看到这些数字其实就是love的ascii码值
当文本内容是兄弟时,我们看一下读取结果
这里我们需要注意一件事情:
public static void main(String[] args)throws IOException {
InputStream inputStream=new FileInputStream("c:/Intel/text2.txt");
while(true)
{
int a=inputStream.read();
if(a==-1)//表示读取到文件末尾
{
break;
}
System.out.printf("%x"+" ",(byte)a);//实际上每次读一个字节我们再强转成byte
}
inputStream.close();
}
我们看到每次我们都需要close(),含义是关闭文件,那么这里的关闭文件又起到了什么作用呢?进程->在内核里,使用PCB这样的数据结构来表示进程,一个线程对应一个PCB,一个进程可以对应一个PCB,也可以对应多个,PCB有一个重要的属性,文件描述符表(相当于一个数组)记录了该进程打开了哪些文件(即使一个进程里有多个线程多个PCB,也没关系,共同用一个文件描述符表)
文件描述符表里的每个元素都是内核里的一个file_struct对象,这个对象就表示一个打开了的文件,每次打开文件操作,就会在文件描述符表中,把这个信息放进去,每次关闭文件,也就会把这个文件描述符表对应的表项给释放掉,如果没有close,对应的表项,没有及时释放,虽然java有GC,GC操作会在回收这个outputStream对象的时候去完成这个释放操作,但是这个GC不一定及时,如果不手动释放,意味着文件描述符表可能很快就被占满了,(这个数组,不能自动扩容,存在上限)如果占满了以后,后面再次打开文件,就会打开失败。
为了防止我们遗忘掉close,我们采取了一种更好的写法
try(InputStream inputStream=new FileInputStream("c:/Intel/text2.txt"))
{
while(true)
{
int a=inputStream.read();
if(a==-1)//表示读取到文件末尾
{
break;
}
System.out.printf("%x"+" ",(byte)a);//实际上每次读一个字节我们再强转成byte
}
}
我们将内容放到try语句块里,这个写法虽然没有显式的写close,实际上是会执行的,只要try语句快执行完毕,就可以自动执行到close,不是随便拿一个对象放到try()里就能自动释放,需要满足一定的要求。
实现了Closeable接口的类才可以放到try的()被自动关闭,这个接口提供的方法就是close方法。
使用了InputStream来读文件,还可以使用OutputStream来写文件。
public static void main12(String[] args)throws IOException {
try (OutputStream outputStream = new FileOutputStream("c:/Intel/text.txt")) {
outputStream.write('h');
outputStream.write('e');
}
}
我们可以看原本是“兄弟”现在是he.
我们接下来再来看一下Reader,Writer按字符读取和写的用法(适合读文本文件)
我们先来看一下我们text的文件里有啥
public static void main13(String[] args)throws IOException {
Reader reader=new FileReader("c:/Intel/text.txt");
while(true)
{
int a=reader.read();
if(a==-1)
{
break;
}
System.out.println(a);
}
}
我们看一下打印结果:
这是读文件,我们再来看一下写文件
public static void main17(String[] args)throws IOException {
try (Writer write = new FileWriter("c:/Intel/text.txt")) {
write.write("同志再见");
}
}
我们将文本里写入“同志再见”
新写入的内容会覆盖掉原来的内容。
另外像这种写操作,我们在最后最好是加上flush语句
write.flush();
也就是这样
public static void main999(String[] args)throws IOException {
try (Writer write = new FileWriter("c:/Intel/text.txt")) {
write.write("同志再见");
write.flush();
}
}
因为像这种写操作,其实是先写到缓冲区里,写操作执行完了,内容可能还在缓冲区里,还没有真的进入硬盘,close操作,就会触发缓冲区的刷新(刷新操作,就是把缓冲区里的内容写到硬盘里),除了close方法,还可以通过flush方法,也能起到刷新缓冲区的效果。
Scanner 是搭配流对象进行使用的
Scanner scaner=new Scanner(System.in);//System.in其实就是一个输入流对象
public static void main(String[] args)throws IOException {
try(InputStream inputStream=new FileInputStream("c:/Intel/text.txt"))
{
Scanner scanner=new Scanner(inputStream);//从文件里读
while(scanner.hasNext())
{
String s = scanner.next();
System.out.println(s);
}
}
}
文件内容:
打印结果:
四.两个小程序的实现
扫描指定目录,并找到名称中包含指定字符的所有普通文件(不包含目录),并且询问用户是否要删除该文件。
ublic static Scanner scan=new Scanner(System.in);
public static void main(String[] args) {
System.out.println("请输入路径");
String basepath=scan.next();
File root=new File(basepath);
if(!root.isDirectory())
{
System.out.println("输入有误");
return;
}
System.out.println("请输入你想删除的文件");
String target=scan.next();
find(root,target);
}
public static void find(File root,String target) {
//System.out.println(root.getAbsolutePath());
File[] files = root.listFiles();、//列举出当前目录所包含的东西
if (files == null) {//如果当前目录为空,就返回
return;
}
for (File file : files) {
if (file.isDirectory()) {//如果是目录,就继续递归
find(file, target);
} else {
if (file.getName().contains(target)) {//包含指定字符的文件
System.out.println("请确认是否要删除:删除请按yes,取消删除请按No");
String user = scan.next();
if (user.equals("yes")) {
file.delete();
System.out.println("删除成功");
} else {
System.out.println("取消删除");
}
}
}
}
}
}
进行普通文件的复制:
思路:把第一个文件按照字节依次读取,把结果写入到另一个 文件中
public static Scanner scan=new Scanner(System.in);
public static void main(String[] args) {
System.out.println("请输入想要想要复制的文件:");
String wen=scan.next();
File file=new File(wen);
if(!file.isFile())
{
System.out.println("文件不存在");
return;
}
System.out.println("请输入文件复制到的路径");
String lu=scan.next();
File filelu=new File(lu);
if(filelu.isFile())
{
System.out.println("您当前输入的目标路径有误");
return;
}
try(InputStream inputStream=new FileInputStream(wen); OutputStream outputStream=new FileOutputStream(lu))
{
while(true)
{
int a=inputStream.read();
if(a==-1)
{
break;
}
outputStream.write(a);
}
System.out.println("复制文件成功");
}catch(IOException e)
{
e.printStackTrace();
}
}
}