草庐IT

JavaEE & 文件操作和IO & 目录扫描全文检索小程序

s:103 2023-04-18 原文

不知道说啥了,看看吧

文章目录

JavaEE & 文件操作和IO

在之前的学习中,基本上都是围绕内存展开的~

  • MySQL 主要是操作硬盘的

  • 文件IO也是是操作硬盘的~

IOinput output

1. 文件系统操作

  • 创造文件,删除文件,重命名文件,创建目录······
  • 一些操作没有权限也做不了~

1.1 路径

  • 就是我们的文件系统上的一个文件/目录的具体位置
    • 目录:文件夹
  • 计算机的目录是有层级结果的,即N叉树

  • 我的代码库目录:

  • 那么这篇文章的源码所在的目录具体位置是什么呢?

  • 所以,路径就是:D:/马库/marathon-april-2023/文件IO
    • 这是个绝对路径
      1. 绝对路径,即从盘符开始到具体文件/目录
      2. 相对路径,从指定目录开始到具体文件/目录
        • 要确认**(基准)工作目录**是什么~

而里面的src目录下有java文件,out目录里面就有class文件,同样有对应的路径

  1. / 分割,推荐!
  2. \ 分割的话要加转义字符\ , 即 \\
    • 一般只能适用于Windows
  3. …/ 代表这一级的上一个目录
  4. . 代表这当前目录(与后续目录要以 / 分割,即 . / )通常可以省略
  5. . . 代表当前目录的上一级目录(与后续目录要以 / 分割,即 . . /
    • . . / . . / 代表上一级目录的上一级目录~
  6. 默认源头的源头是“此电脑”目录,可以不写

绝对路径可以认为是以“此电脑”为工作目录的相对路径

  • 并且任何一个文件/目录,对应的路径,肯定是唯一的

    • 在LInux可能出现两个不同路径找到同一文件的情况~
    • 但是在Windows上不存在~
  • 路径与文件一一对应~

  • 路径为文件的身份

1.2 文本文件 与 二进制文件

  • 这个就是字面意思了

文本文件:

  • 存储字符(不仅仅是char类型)

二进制文件:

  • 存储什么都OK,因为任何数据都是以二进制为根本的

判断:

  • 记事本打开,是文本就是文本,是二进制就是二进制~
  1. .txt文件:文本 / 二进制,看你怎么创造的~

  2. .java / .c 文件文本文件

    • 拖动到记事本里
  3. .class / .exe 文件二进制文件~

  1. .jpg / mp3 二进制文件
    • 乱码,即把这一个个字节转化为字符型,而原本不是字符型的~

  1. pdf xlsx doc … : 二进制文件

  • csv excel的文本格式:

1.3 文件系统操作

  • Java标准库提供了一个类:File
    • File对象代表着一个文件,是那个文件的抽象表示~
  • 硬盘上的文件 ==> 内存中的File对象 ==> 在内存上改变硬盘上的一些东西

1.3.1 构造File对象

  • 需要传一个文件路径为参数~
    • 这个文件可以存在也可以不存在

例如这张图片~

1.3.2 使用File对象

  • 不手动常见文件是不会自动创建的
    • 不会再new的时候创建
序号方法名方法说明
1String getParent()返回 File 对象的父目录文件路径
2String getName()返回 FIle 对象的纯文件名称
3String getPath()返回 File 对象的文件路径
4String getAbsolutePath()返回 File 对象的绝对路径
5String getCanonicalPath()返回 File 对象的修饰过的绝对路径
6boolean exists()判断 File 对象描述的文件是否真实存在
7boolean isDirectory()判断 File 对象代表的文件是否是一个目录
8boolean isFile()判断 File 对象代表的文件是否是一个普通文件
9boolean createNewFile()根据 File 对象,自动创建一个空文件。成功创建后返 回 true
10boolean delete()根据 File 对象,删除该文件。成功删除后返回 true
11void deleteOnExit()根据 File 对象,标注文件将被删除,删除动作会到 JVM 运行结束时才会进行
12String[] list()返回 File 对象代表的目录下的所有文件名
13File[] listFiles()返回 File 对象代表的目录下的所有文件,以 File 对象表示
14boolean mkdir()创建 File 对象代表的目录
15boolean mkdirs()创建 File 对象代表的目录,如果必要,会创建中间目录
16boolean renameTo(File dest)进行文件改名,也可以视为我们平时的剪切、粘贴操作
17boolean canRead()判断用户是否对文件有可读权限
18boolean canWrite()判断用户是否对文件有可写权限

小小演示:

  1. 绝对路径
public static void main(String[] args) throws IOException {
    File file = new File("d:/马图/瞪眼.jpg");
    System.out.println(file.getParent());
    System.out.println(file.getName());
    System.out.println(file.getPath());
    System.out.println(file.getAbsoluteFile());
    System.out.println(file.getCanonicalFile());
}
  • IOException是IO操作的会抛出的常见异常
    • 是首查异常,也叫编译时异常

  1. 相对路径
    • 默认是与src目录的上一级的为工作目录~
    • 就是项目所在目录
    • 而不是src这一级

  • 打印得出来这个文件不代表就存在这个文件~
  1. 是否存在?

  1. 性质,创造文件,删除文件
    • 是存在既不是目录又不是普通文件的文件的
    • 例如socket文件等等~
public static void main(String[] args) {
    File file = new File("./helloWorld.txt");
    System.out.println(file.exists());
    System.out.println(file.isDirectory());//是目录吗?(文件夹)
    System.out.println(file.isFile());//是文件吗?(普通文件)
}

  1. 创建与删除目录
public static void main(String[] args) {
    File file = new File("./helloWorld");
    if(!file.exists()) {
        file.mkdir();
    }
    System.out.println(file.exists());
    System.out.println(file.isFile());
    System.out.println(file.isDirectory());
}

make directory

  1. 路径转化为数组

获取目录里的所有文件/目录

  • list ==> 文件 /目录名数组
  • listFile ==> File对象 数组
public static void main(String[] args) {
    File file = new File("helloWorld");
    String[] results1 = file.list();
    File[] results2 = file.listFiles();
    System.out.println(Arrays.toString(results1));
    System.out.println(Arrays.toString(results2));
}

  1. 重命名
public static void main(String[] args) {
    File file = new File("helloWorld");
    file.renameTo(new File("HELLO_WORLD"));
}

2. 文件内容操作

  • 针对文件内容进行 读 与 写

  • 文件操作依赖于一些类,或者说是多组类

    1. 文本 ==> ”字符流“
    2. 二进制 ==> “字节流”

“流”:

  • 数据的运输像河流一样,流向哪,从哪流来~

2.1 获取文件输入流InputStream(字节流)

public static void main(String[] args) throws IOException {
    InputStream inputStream = new FileInputStream("HELLO_WORLD");
		
    //coding

    inputStream.close();

}

有了输入流,就相当于你有了“介质”

  • 相当于打开文件,文件的信息可以出来

关闭输入流

  • 相当于关闭文件
  • 如果不关闭,可能会导致,文件资源泄露 ===>
  • 进程里有个文件描述符表,一旦打开文件多了,这个表可能会爆了,导致机器出问题!
  • Java的对象,没用了会自动释放,但是这里的流对象并不会!!!

正确的写法:(利用finally保证关闭能够进行)

  • try括号内为打开文件操作,默认finally关闭文件~
    • try with resources操作
public static void main(String[] args) {
    try(InputStream inputStream = new FileInputStream("HELLO_WORLD/123.txt")) {

    } catch (FileNotFoundException e) {
        e.printStackTrace();
    } catch (IOException e) {
        e.printStackTrace();
    }
}
  • InputStream实现Closeable接口,那么就可以这样操作~
    • 这就是try with resource操作的要求
    • 注意:后续内容都是以这种方式打开与隐式关闭文件的

2.1.1 read方法

  • 只能说文件里有个指针指着读在哪了,并不是读了后原文件就删了~
方法名方法说明
int read()一次读一个字节并返回,返回-1代表读完了
int read(byte[] b)填满此数组为止,返回-1表示读完(可能填不满)
int read(byte[] b, int off, int len)填满此数组的[off, off + len)为止,返回-1表示读完(可能填不满)
  • 在java对此方法的描述中提到:返回的字节转化为int类型,范围是0 - 255

手写一些数据:

2.1.2 不带参数的read方法

public static void main(String[] args) {
    try(InputStream inputStream = new FileInputStream("HELLO_WORLD/123.txt")) {
        int b = 0;
        do {
            b = inputStream.read();
            System.out.println(b);
        }while(b != -1);

    } catch (FileNotFoundException e) {
        e.printStackTrace();
    } catch (IOException e) {
        e.printStackTrace();
    }
}
  • 测速结果:

  • 对于中文:

  • 测试结果:
    • 不是说一个中文 ==> 一个char字符 两个字节吗,为什么这里是三个字节一个汉字
    • 那是因为Unicode每个字符是两个字节
    • UTF-8汉字是三个字节,其他字符一个字节~
      • 我编译器无脑全设置UTF-8了
    • 而读取的内容可没有规定就是Java的char类型呀~
      • 但是我们可以通过一些手段翻译这个东西,后面讲~

E9 : 233 A9 : 169 AC : 172 ················

完美对应~

2.1.3 给定数组的read方法

public static void main(String[] args) {
    try(InputStream inputStream = new FileInputStream("HELLO_WORLD/123.txt")) {
        byte[] bytes = new byte[9];
        System.out.println(inputStream.read(bytes));
        System.out.println(Arrays.toString(bytes));


    } catch (FileNotFoundException e) {
        e.printStackTrace();
    } catch (IOException e) {
        e.printStackTrace();
    }
}
  • 测试结果:

  • 咋变负数了?

    • 因为读取到的字节仍然是 -128 - 127 的
    • 只不过刚才返回int类型的是无符号的~
  • 如何翻译呢?

    • 用String的构造方法~

2.2 获取文件输出流OutputStream(字节流)

public static void main(String[] args) {
    //每次打开输出流,都会清空文件内容~
    try(OutputStream outputStream = new FileOutputStream("HELLO_WORLD/123.txt")) {
        

    } catch (IOException e) {
        e.printStackTrace();
    }
}
  • 每次打开文件,会清空原内容!

2.2.1 write方法

方法名方法说明
void write(int b)传入一个int型,内部强行转化为byte型
void write(byte[] b)将整个字节数组写入文件中
int write(byte[] b, int off, int len)将字节数组的[off, off + len)部分写入文件中
void flush()重要:我们知道 I/O 的速度是很慢的,所以,大多的 OutputStream 为 了减少设备操作的次数,在写数据的时候都会将数据先暂时写入内存的 一个指定区域里,直到该区域满了或者其他指定条件时才真正将数据写 入设备中,这个区域一般称为缓冲区。但造成一个结果,就是我们写的 数据,很可能会遗留一部分在缓冲区中。需要在最后或者合适的位置, 调用 flush(刷新)操作,将数据刷到设备中。
  • flush很重要,在关闭之前没有flush,文件内容就无法得以更新

2.2.2 write 传入单个字节的构造方法

public static void main(String[] args) {
    //每次打开输出流,都会清空文件内容~
    try(OutputStream outputStream = new FileOutputStream("HELLO_WORLD/123.txt")) {

        outputStream.write(1);
        outputStream.write(2);
        outputStream.write(3);
        outputStream.write(4);
        outputStream.flush();

    } catch (FileNotFoundException e) {
        e.printStackTrace();
    } catch (IOException e) {
        e.printStackTrace();
    }
}

2.2.3 write 传入字节数组的构造方法

public static void main(String[] args) {
    //每次打开输出流,都会清空文件内容~
    try(OutputStream outputStream = new FileOutputStream("HELLO_WORLD/123.txt")) {

        outputStream.write(new byte[]{1, 2, 3, 4, 5, 6, 7});
        outputStream.flush();

    } catch (FileNotFoundException e) {
        e.printStackTrace();
    } catch (IOException e) {
        e.printStackTrace();
    }
}

2.3 字符流 Reader 与 Writer

2.3.1 Reader的读方法

  • 对比于字节流,这里读的是字符,读进字符数组~
    public static void main(String[] args) throws FileNotFoundException {
        try(Reader reader = new FileReader("HELLO_WORLD/123.txt")) {
            char ch = (char)reader.read();
            char[] chars = new char[7];
            reader.read(chars);
            System.out.println(ch);
            System.out.println(chars);
            int c = 0;
            do {
                c = reader.read();
                System.out.println((char)c);
            }while (c != -1);
        } catch (IOException e) {
            e.printStackTrace();
        }

  • 测试结果:
    • 同样read返回-1,代表读完了~

2.3.2 Writer的写操作

  • 对比于字节流,这里写入的是字符,字符数组,或者字符串~

    public static void main(String[] args) {
        try(Writer writer = new FileWriter("HELLO_WORLD/123.txt")) {
            writer.write('0');
            writer.write(new char[]{'1', '2', '3', '4', '5', '6'});
            writer.write("789");
            writer.flush();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
  • 写操作跟字节流一样,无此文件,自动创建~

  • 并且还会清空原内容

  • 测试结果:

3. 小程序练习:全文检索

  • 就是遍历目录,并在文件内容中查找信息

接下来以简单粗暴的方式去实现~

3.1 控制台输入根目录与关键字

public static void main(String[] args) throws IOException {
    Scanner scanner = new Scanner(System.in);
    System.out.print("请输入要扫描的根目录:");
    String root = scanner.next();
    File file = new File(root);
    if(!file.isDirectory()) { // 1. 目录不存在 2. 不是目录
        System.out.println("输入错误");
        return;
    }
    System.out.print("请输入要查询的词:>");
    String words = scanner.next();
    scan(file, words);//扫描
}
  1. 根据根目录构造File对象
  2. 如果这个file对象不是目录或者不存在的话,则说明输入错误,直接返回退出程序
  3. 如果是目录,输入要关键字
  4. 调用scan方法对目录进行扫描(自己实现)

3.2 scan递归方法

  • n叉树就得写循环来递归了
  • 如果是扫描到二进制文件,我们也不指望里面有我们要的文本,因为二进制一般存放一些后端数据信息,并不是给人看的,不是观赏性的,但是二进制文件还是可能会读到的~
  • 记得设立递归出口,死递归会导致栈溢出
public static void scan(File file, String words) throws IOException {
    File[] files = file.listFiles();
    if(files == null) { 
        // 这里空目录对应的并不是空数组!是null~
        return;
    }else {
        for (int i = 0; i < files.length; i++) {
            File f = files[i];
            if(f.isFile()) {
                String content = readAll(f);
                if(content.contains(words)) {
                    System.out.println(f.getCanonicalFile());
                }
            }
            if(f.isDirectory()) {
                scan(f, words);
            }
            //两种都不是的其他文件,就不能读~
        }
    }

}

3.3 readAll读取文件方法

  • 利用StringBuilder拼接字符串~
    • 用Reader字符流读取数据~
    • 对于Java,这些流对象只是读取方式,对文件是二进制还是文本没有要求
  • 最终返回
public static String readAll(File f) {
    StringBuilder stringBuilder = new StringBuilder();
    try (Reader reader = new FileReader(f)){
        while(true) {
            int c = reader.read();
            if(c == -1) {
                break;
            }
            stringBuilder.append((char)c);
        }
    } catch (FileNotFoundException e) {
        e.printStackTrace();
    } catch (IOException e) {
        e.printStackTrace();
    }
    return stringBuilder.toString();
}

  • 堆溢出~

3.4 测试

  • 测试用例:
    • 如果文件数量多,内容多,以此法会卡的半死
    • 到时候我们学习一下“倒排索引”这种数据结构,可能能够很好地优化!

  1. 根目录是:d:/马库/marathon-april-2023
  2. 关键字是:马大帅
  • 测试结果:

  • 测试结果正常!
    • 另外两个可能是其他项目里提到了这个关键字 ^ V ^

文章到此结束!谢谢观看
可以叫我 小马,我可能写的不好或者有错误,但是一起加油鸭🦆

文件操作的讲解告一段落,后面也会涉及到哦!

实践才是最好的学习!


有关JavaEE & 文件操作和IO & 目录扫描全文检索小程序的更多相关文章

  1. ruby - 使用 RubyZip 生成 ZIP 文件时设置压缩级别 - 2

    我有一个Ruby程序,它使用rubyzip压缩XML文件的目录树。gem。我的问题是文件开始变得很重,我想提高压缩级别,因为压缩时间不是问题。我在rubyzipdocumentation中找不到一种为创建的ZIP文件指定压缩级别的方法。有人知道如何更改此设置吗?是否有另一个允许指定压缩级别的Ruby库? 最佳答案 这是我通过查看ruby​​zip内部创建的代码。level=Zlib::BEST_COMPRESSIONZip::ZipOutputStream.open(zip_file)do|zip|Dir.glob("**/*")d

  2. ruby - 其他文件中的 Rake 任务 - 2

    我试图在一个项目中使用rake,如果我把所有东西都放到Rakefile中,它会很大并且很难读取/找到东西,所以我试着将每个命名空间放在lib/rake中它自己的文件中,我添加了这个到我的rake文件的顶部:Dir['#{File.dirname(__FILE__)}/lib/rake/*.rake'].map{|f|requiref}它加载文件没问题,但没有任务。我现在只有一个.rake文件作为测试,名为“servers.rake”,它看起来像这样:namespace:serverdotask:testdoputs"test"endend所以当我运行rakeserver:testid时

  3. ruby-on-rails - 在 Rails 中将文件大小字符串转换为等效千字节 - 2

    我的目标是转换表单输入,例如“100兆字节”或“1GB”,并将其转换为我可以存储在数据库中的文件大小(以千字节为单位)。目前,我有这个:defquota_convert@regex=/([0-9]+)(.*)s/@sizes=%w{kilobytemegabytegigabyte}m=self.quota.match(@regex)if@sizes.include?m[2]eval("self.quota=#{m[1]}.#{m[2]}")endend这有效,但前提是输入是倍数(“gigabytes”,而不是“gigabyte”)并且由于使用了eval看起来疯狂不安全。所以,功能正常,

  4. ruby-on-rails - rails : "missing partial" when calling 'render' in RSpec test - 2

    我正在尝试测试是否存在表单。我是Rails新手。我的new.html.erb_spec.rb文件的内容是:require'spec_helper'describe"messages/new.html.erb"doit"shouldrendertheform"dorender'/messages/new.html.erb'reponse.shouldhave_form_putting_to(@message)with_submit_buttonendendView本身,new.html.erb,有代码:当我运行rspec时,它失败了:1)messages/new.html.erbshou

  5. ruby-on-rails - 由于 "wkhtmltopdf",PDFKIT 显然无法正常工作 - 2

    我在从html页面生成PDF时遇到问题。我正在使用PDFkit。在安装它的过程中,我注意到我需要wkhtmltopdf。所以我也安装了它。我做了PDFkit的文档所说的一切......现在我在尝试加载PDF时遇到了这个错误。这里是错误:commandfailed:"/usr/local/bin/wkhtmltopdf""--margin-right""0.75in""--page-size""Letter""--margin-top""0.75in""--margin-bottom""0.75in""--encoding""UTF-8""--margin-left""0.75in""-

  6. ruby-on-rails - Rails 3 中的多个路由文件 - 2

    Rails2.3可以选择随时使用RouteSet#add_configuration_file添加更多路由。是否可以在Rails3项目中做同样的事情? 最佳答案 在config/application.rb中:config.paths.config.routes在Rails3.2(也可能是Rails3.1)中,使用:config.paths["config/routes"] 关于ruby-on-rails-Rails3中的多个路由文件,我们在StackOverflow上找到一个类似的问题

  7. ruby-on-rails - 'compass watch' 是如何工作的/它是如何与 rails 一起使用的 - 2

    我在我的项目目录中完成了compasscreate.和compassinitrails。几个问题:我已将我的.sass文件放在public/stylesheets中。这是放置它们的正确位置吗?当我运行compasswatch时,它不会自动编译这些.sass文件。我必须手动指定文件:compasswatchpublic/stylesheets/myfile.sass等。如何让它自动运行?文件ie.css、print.css和screen.css已放在stylesheets/compiled。如何在编译后不让它们重新出现的情况下删除它们?我自己编译的.sass文件编译成compiled/t

  8. ruby - 将差异补丁应用于字符串/文件 - 2

    对于具有离线功能的智能手机应用程序,我正在为Xml文件创建单向文本同步。我希望我的服务器将增量/差异(例如GNU差异补丁)发送到目标设备。这是计划:Time=0Server:hasversion_1ofXmlfile(~800kiB)Client:hasversion_1ofXmlfile(~800kiB)Time=1Server:hasversion_1andversion_2ofXmlfile(each~800kiB)computesdeltaoftheseversions(=patch)(~10kiB)sendspatchtoClient(~10kiBtransferred)Cl

  9. ruby - 如何将脚本文件的末尾读取为数据文件(Perl 或任何其他语言) - 2

    我正在寻找执行以下操作的正确语法(在Perl、Shell或Ruby中):#variabletoaccessthedatalinesappendedasafileEND_OF_SCRIPT_MARKERrawdatastartshereanditcontinues. 最佳答案 Perl用__DATA__做这个:#!/usr/bin/perlusestrict;usewarnings;while(){print;}__DATA__Texttoprintgoeshere 关于ruby-如何将脚

  10. ruby - 使用 Vim Rails,您可以创建一个新的迁移文件并一次性打开它吗? - 2

    使用带有Rails插件的vim,您可以创建一个迁移文件,然后一次性打开该文件吗?textmate也可以这样吗? 最佳答案 你可以使用rails.vim然后做类似的事情::Rgeneratemigratonadd_foo_to_bar插件将打开迁移生成的文件,这正是您想要的。我不能代表textmate。 关于ruby-使用VimRails,您可以创建一个新的迁移文件并一次性打开它吗?,我们在StackOverflow上找到一个类似的问题: https://sta

随机推荐