文件IO操作

news2025/1/17 3:39:52

日升时奋斗,日落时自省 

目录

1、文件基本认知

1.1、文件路径

1.2、相对路径

1.3、文件类型

2、Java的文件操作

2.1、文件操作类File

3、数据流读写

3.1字节流读文件

3.2、字节流写文件

3.3、字符流读操作

3.4、字符流写操作

4、文件操作案例

4.1、删除文件

4.2、普通文件复制(就是不能复制目录)

4.3、查找包含某字符的文件


1、文件基本认知

谈到文件无疑是从我们知道的开始说,狭义认知针对硬盘这种持久化存储的IO设备,当我们进行数据保存时,往往不是以以这个整体(一块内容)来保存的,而是单独保存的,这个独立的单位就在计算机中被抽象为文件的概念。

狭义:简单说就是硬盘上的文件 和  目录

文件除了有数据内容之外,还有一部分信息,例如文件名,文件类型,文件大小等并不作为文件数据类型而存在,这类信息就是文件的元信息。

广义:计算机中的软硬件资源

文件类似树结构一个文件下包含多个文件,当前文件有包含多个子文件,像树枝一样

文件的基本就是以上,没有什么特别的;

文件主要跟我们写代码有关系,之前我们写的代码,存储数据,主要是靠变量,变量是在内存中的。文件是在硬盘上的,本话题主要是如何实现代码与文件交接

1.1、文件路径

每个文件,在硬盘上都有一个具体的“路径”;

 test.txt文件的路径 ,叫做E:/Test/test.txt表示一个文件的具体位置路径,就可以使用/来分割不同的目录级别.

这里注意细节的友友们,会看到图片上使用的是反斜杠(\)但是我上面打出来是 斜杠(/)

问题为什么不一样?这样写的意义是什么?

(1)其实两种写法都可以,都是对的,可以尝试一下斜杠

(2)其次就是在java中尽量写 斜杠表示法 。为什么???

因为代码去写反斜杠的不方便

例如 :java中代码:String path="E:\Test\test.txt";     现在的\T就以个转义字符

斜杠与反斜杠肯定是有由来的 ,最早期的DOS系统使用过反斜杠 ,后来又改成了斜杠再后来Windows也继承了这样的设定。

刚刚事例中的文件路径开头都有一个大写字母C:、D:或者E:都叫做“盘符”,当然写小写也是可以的,都能起作用,盘符是通过“硬盘分区”来的,每个盘符可以单独的占一个硬盘,也可以若干个盘符占一个硬盘。

有个稀奇的问题,为啥没有a:,b:盘符?

有,但是出现的比较早,现在已经淘汰了,这样的盘符在之前叫做软盘,存储空间对于现在来说太小了只有几MB,所以喜欢了可以买个收藏

1.2、相对路径

因为绝对路径就是在“文件路径”标题下,以盘符开头的路径

相对路径:以当前所在的目录为基准,或者以..开头(也可以以.开头),找到指定路径

当前目录也称为工作目录,每个程序运行的时候,都有一个工作目录,这里来看一下相对路径是个啥,在控制台执行 打开控制台 win+r 然后输入cmd 

 这就是绝对路径因为它是默认的工作目录,那相对路径呢?

 这俩就是相对路径了,执行程序运行的目录。(这是两个简单Windows操作系统命令,想了解更多,可以百度稍微科普一下,记几个常用就行了)

相对路径有带点的表示方式 这个可能友友们也会有困惑

先解释大体的概念 : 点+斜杠(./)就是当前所在目录,点+点+斜杠(../)就是上级目录

现在理解相对路径就不难了,以idea为例

 当你打开idea的时候,这个路径就是你当前所在目录(这里是我的哈,这里用作解释,你肯定跟我是不一样的) 当前文件路径:D:/java使用/first/IO_Operations

以IO_Operations文件定位

所在目录 :D:/java使用/first

现在使用点+斜杠的方式表示一下当前所在目录 :

相对路径: ./IO_Operations     工作目录 :D:/java使用/first/

 相对路径: ./first/IO_Operations  工作目录:D:/java使用

 相对路径: ./java使用/first/IO_Operations  工作目录: D:/

多个例子之后就不难找到 点+ 斜杠表示的规律 不管在那个目录下都表示是当前所在目录。

现在使用点+点+斜杠的方式表示一下 :../IO_Operations

IO_Operations文件的当前层目录是first所以只需要表示一个上层目录就行了,这里的java使用就是根目录 所以表示如上。

再举一个事例:D:/java使用/first/IO文件/IO_Operations (稍作修改)

同样以IO_Operations文件定位

现在使用点+点+斜杠的方式表示一下 :../../IO_Operations

这里只需要到到达根目录就行,当前层目录是IO文件 根目录是Java使用,相当于连跳两级,两个../就可以解决问题

1.3、文件类型

文件类型我们看见过很多种,word文件 ,exe程序,图片,视频等,虽然很多种但是文件还是分为两个大类;

(1)文本文件 (存的是文本,字符串)

字符串是由字符构成,字符是怎么写上去的,通过数字表示,能存储的一定都是合法的字符,因为会按照你指定的字符编码表进行查找对应的数字,然后进行存储

(2)二进制文件(存的是二进制数据,不一定是字符串了) 

没有限制是不是字符都可以存,(光脚的不怕穿鞋的)

区分两种文件:能看的懂的就是文本文件(主要特征不乱),看不懂的就是二进制文件(乱)

2、Java的文件操作

2.1、文件操作类File

java标准库提供的一个File类,用于创建文件对象,这里叙述只三个构造方法(当然还有很多,有兴趣的友友,idea中查看或者API官方文档)

该构造方法带有两个参数 第一个参数是父目录 +孩子文件路径 两者都是用路径表示

 该构造方法带有两个参数,第一个参数是父目录 +孩子文件路径,父目录就不需要路径了,直接放就行。

该构造方法就一个参数==>路径  

说到路径这里解释一个问题,就是写路径的时候写斜杠还是反斜杠,由于的话就写separator针对Windows(是File里的一个静态变量)斜杠还是反斜杠就可以不用担心了,它会跟随系统

File file=new File("e:"+File.separator+"test.txt");

 如上写法就行,把斜杠或者反斜杠换成该静态方法

(1)再提一些File类中简单方法 以下方法相关 文件名 ,父目录,,文件路径,绝对路径,相对路径

 public static void main(String[] args) throws IOException {
        File file=new File("e:"+File.separator+"test.txt");
        System.out.println(file.getName());  //获取文件名字
        System.out.println(file.getParent());  //获取文件父目录
        System.out.println(file.getPath());  //获取文件路径
        System.out.println(file.getAbsoluteFile());  //获取绝对路径
        System.out.println(file.getCanonicalFile());   //获取相对路径 此时 要抛出异常 快捷键 alt+enter(也就是回车)
    }

代码中只有相对路径比较特殊需要抛出异常 快捷键 alt+enter(enter也就是回车)。

提示:文件路径不用敲出来,直接复制粘贴就行,在同一个系统上的话不会错,如果不同系统也不放心的情况下使用 刚刚的静态方法就可以了(它会自己对应不同系统)

(2)以下代码解释方法 :文件存在,是否是文件 ,是否是目录 ,创建我呢间

public static void main(String[] args) throws IOException {
        File file=new File("./test.txt");
        System.out.println(file.exists());   //当前文件是否存在
        System.out.println(file.isFile());   //是不是文件
        System.out.println(file.isDirectory());   //是不是目录
        file.createNewFile();                //创建文件
        System.out.println(file.exists());   //当前文件是否存在
        System.out.println(file.isFile());   //是不是文件
        System.out.println(file.isDirectory());   //是不是目录
    }

File类创建对象的时候,本身是不会创建文件,这里有专门的方法创建文件,这里是以相对路径创建文件的所以会在idea的目录上显示(以下是过程及问题解决)

 (3)以下是 删除文件,创建目录,还有替换文件名称

删除方法:delete 这个就没有过多的解释了

删除临时文件:deleteOnExit 方法 程序退出的时候,自动删除,临时文件程序有时候也会用到,啥是临时文件,咋没有见过???

public static void main(String[] args) {
        //创建文件目录
        File dir=new File("./test/aaa/bbb");
        /*
        * 创建多级目录的时候就需要  mkdirs
        * */
        dir.mkdirs();  //如果是创建一个目录的话 就使用 mkdir
    }
public static void main(String[] args) {
        //修改文件名称
        File file=new File("./test");
        File file1=new File("./testnow");
        file.renameTo(file1);   //修改昵称
    }

3、数据流读写

使用“流对象”进行操作 如何理解流???

“流”字联想起来跟水有关系,水流 连绵不断,没有固定形状,那就可以划分多次,也可以不划分。以接水为例: 一家人需要水是固定量的,但是接水的次数是不定的

(1)可以一次接完

(2)也可以先接一半

(3)还可以将需要水分5次来接

这就是水流的提点,为什么会以流来解释到水,因为读字节 与 写字节也是如此

从文件中读取字节情况很多,可能一次读完,可能一次读不完,像水一样

(1)可以直接一次性读(写)完

(2)分两次读(写)完

(3)分多次读(写)完

流类型上分为两类 :字节流 、字符流

(1)字节流 相关类   FileInputStream ->(继承) InputStream 、FileOutputStream->OutputStream

(2)字符流 相关类   Reader ->(继承)FileReader  、  Writer->(继承)FileWriter

 为什么会有继承这一说?

 是因为这里InputStream/OutputStream/Reader/Writer都是抽象类是不能创建对象的。

3.1字节流读文件

读文件操作使用InputStream和FileInputStream两个类进行。

 InputStream inputStream=new FileInputStream("e:/test.txt");

创建对象,赋予文件路径即可

//进行读操作
        while(true){
            //获取字节对应的数字
            int n=inputStream.read();
            if(n==-1){  //文件读到末尾了也就是结束 此时read返回-1
                break;
            }
            //因为获取的是数字,所以这里需要进行转换byte类型 
            System.out.println("字节流打印 "+(byte)n);
            //如果是文字的话 就该以十六进制来打印了 可以去查字符表就能知道是什么了
            System.out.printf("%x \n",(byte)n);
        }

以上两个写在一个main方法中就可以尝试了,这里说一下read方法在写的时候会抛出异常;有友友肯定疑惑这里的read读的返回值为啥是int,首先是方法这么定的,然后就是手动转化成byte类型就行;InputStream提供了三个重载方法read;

(1)read无参数版本一次读一个字节

(2)read有一个参数的版本 把内容一次填充提到byte数组中(这里相当于“输入型参数”)

(3)read 三个参数版本 其实就比(2)多了个范围从第几个开始多长

注:字节对于打印汉字可以使用16进制,对照编码表查找针对不同的(例如utf8,gbk,BIG5)根据自己需求对照即可(所以说字节流也是可读文本文件的)

这里再写一下read两个参数的代码(三个参数与两个参数大体相同就不在展示),前面创建对象是一样的,以下部分不同

 //read带有一个参数时进行读取
        while(true){
            //首先需要准备一个数组 这里的字节数 可以随心所欲设置(有点夸张)意思明白即可。
            byte[] buffer=new byte[1024];
            //这里的传参操作就是把刚才的数组传进去, 计算的当前多少个字节
            int len = inputStream.read(buffer);
            System.out.println("len "+len);
            if(len==-1){
                break;
            }
            //读取就结果同样时转换成byte 
            for(int i=0;i<len;i++){
                System.out.printf("%x \n",buffer[i]);
            }
        }

上面给出的数组长度是1024,read就会尽可能的读取1024个字节,填到数组里,但是实际上,文件剩余长度是有限的,如果文件剩余长度超过了1024此时就只能接收到1024个字节,同时并进行打印,然后再次执行while循环,如果当前剩余的长度不足1024,此时有多少就返回多少。

注:读文件的时候,文件在磁盘上内容可能比较多,甚至超出内存容量,一次读完是不显示的,对此类文件操作都是一边读一边处理,处理好了一部分,再处理下一个部分

有一个问题,这里的数组名为什么刚好叫做buffer???

答案:buffer翻译为缓冲区,存在的意义就是提高IO操作的效率,单次IO操作,是要访问硬盘IO设备,单次操作是比较消耗时间的,如果频繁进行这样的IO操作,同样要耗费不少时间。这里很像mysql的插入操作,插入一组传给服务器也是消耗时间的,不如一次插入多组。

单次IO时间是一定的,缩短IO次数就会提高整个程序效率,第一个版本的代代码一个一个read次数多时间长;第二个版本是一次read1024个字节,循环降低了很多,也就是IO次数下降很多。

3.2、字节流写文件

OutputStream类来进行字节流写文件

 public static void main(String[] args) throws IOException {
        OutputStream outputStream = new FileOutputStream("e:/test.txt");   //字节流写 也是会报一个找不到文件的操作的
        outputStream.write(97);
        outputStream.write(98);
        outputStream.write(99);
        outputStream.write(100);
        //记得关闭文件
        outputStream.close();
    }

下面的操作用图解释

 确实我们一般也都会续着写,当然不想清空,流对象还提供了一个“追加写”对象,通过这个就可以实现不清空文件把新内容写在后面。

解释一下:input(输入),output(输出)为什么在代码中input是写入程序,output是写文件??

字节流写入涉及到了关闭文件的操作 close操作(尽量不要忘记)

为什么要写文件结束的时候要close?

进程->在内核里,使用PCB这样的数据结构来表示进程 (这里也有介绍PCB)

一个线程对应一个PCB

一个进程可以对应一个PCB也可以对应多个

PCB中有一个重要的属性叫做文件描述符表(相当于一个数组)记录了该进程打开那些文件,(即使一个进程里有多个线程也会有多个PCB)

 如果真忘了写close操作会有什么问题?

针对java来说还好,但是其他语言不能保证,例如C语言自身就没有GC(Garbage collection)功能,在java中GC操作会回收这个OutputStream对象的时候去完成释放操作,但是GC不一定能准时回收,可能再我们下次一使用的时候GC还没有回收

所以,如果不入手动释放,意味着文件描述符表可以能会被占满(文件描述符表不能自动扩容言外之意就是会满,满了就会卡主,不能用了),文件描述符表不同系统长度不同,但是肯定不大,长度撑死就是几千,对于计算机对于几千几百还是太小了。

特殊情况:close一般情况是执行的,如果一个程序对这个文件一直都需要使用,那文件直到进程结束,PCB销毁,文件描述符表也就销毁了,文件资源会被自动回收(所以文件close之后,程序立即结束了,你忘了关闭,java会自动回收的)

注:虽然java有自动回收功能,但是尽可能手动回收

那出来手动结束,程序能不能自己来结束??答案: 能   所以有了更优雅的代码

try(OutputStream outputStream=new FileOutputStream("e:/test.txt")){
            outputStream.write(97);
            outputStream.write(98);
            outputStream.write(99);
            outputStream.write(100);
        }

使用try()这个写法虽然没有显示close,实际上是会执行的,try语句块执行结束就可以自动执行close操作,该语法被称为try with resources,但是不是说try中谁都可以放置,来看看为啥OutputStream可以?

 实现了Closeable接口就可以该接口提供了close方法

3.3、字符流读操作

与字节流差不多使用,就是读的时候是一个字符一个字符读的,读文本的时候方便(因为读了能看懂)以下代码简单的读取文件内容

//字符流读操作
    public static void main(String[] args) throws IOException {
        try(Reader reader=new FileReader("e:/test.txt")){
            while(true){
                int ch=reader.read();
                if(ch==-1){
                    break;
                }
                System.out.print(""+(char)ch);
            }
        }
    }
public static void main(String[] args) {
        // Scanner scanner=new Scanner(System.in);  该种输入方式 返回的是字节流 只是读取的键盘信息
        try(InputStream inputStream=new FileInputStream("e:/test.txt")){
            Scanner scanner=new Scanner(inputStream);

            //此时读取就是从文件中读取
            scanner.next();  //此处不建议使用nextLine
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

 借读操作来介绍Scanner ,其实该类是搭配流对象进行使用的,这就是为什么我们每次写Scanner创建对象的时候都会在参数里写 System.in ,这是一个输入流与键盘相连能接受键盘传来的信息。

同样Scanner本身就是字节流所以可以放字节流对象。

3.4、字符流写操作

//字符流写操作
    public static void main(String[] args) {
        try(Writer writer=new FileWriter("e:/test.txt")){
            writer.write("hello world");
            writer.flush();   //每次写入都需要耍新缓冲区 , 为缓冲区不能总发着会进行覆盖
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

writer写操作解释:写操作是先写在缓冲区里的,(缓冲区存在很多种形态,咱们自己的代码里也会有缓冲区;标准库里也可以有缓冲区,操作系统内核里也可以有缓冲区)

写操作执行完了,内容仍然可能在缓冲区里,还没有真的进入硬盘。

close操作,就会触发缓冲区的刷新,刷新后缓冲区将内容写到硬盘中,出来close之外还有别的方法,flush方法进行刷新,close方法一旦关闭文件后就不能在对文件操作了,flush方法进行刷新后仍然可以对文件操作。

4、文件操作案例

4.1、删除文件

扫描指定目录,并找到要删除的文件名称(不包含目录),并且后续访问到会问用户是否删除该文件(以下代码均有注释)

思路:

(1)第一次需要输入根目录   并且判断当前输入的是否是目录

(2)第二次需要输入你要删除的文件名称

(3)输入目录后不一定就是当前我们需要的目录,里面可能还包含了很多子目录,此时就需要递归了,文件是成树形结构的,所以次数采用深度优先进行遍历

(4)递归条件如果文件为空则返回,如果不为空 判断是否是目录如果是目录则继续递归,如果不是目录if判断当前文件中有是否包含删除文件名,包含再次判定是否需要删除即可

private static Scanner scanner=new Scanner(System.in);
    public static void main(String[] args) {
        //用户输入一个指定文件路径
        System.out.println("请输入要搜索的文件路径");
        String basepath=scanner.next(); // 提醒使用next就可以
        //针对用户输入路径简单判断
        File root=new File(basepath);
        if(!root.isDirectory()){
            //当前路径不存在或者不是一个文件 提醒一下
            System.out.println("用户输入文件有误!!");
            return;
        }
        //如果没有问题就进入下一步
        //输入要删除的文件
        System.out.println("请输入要删除的文件名称");
        String deletename=scanner.next();
        /*
        * 前面是输入一个目录但是不一定当前目录下没有子目录
        * 先从根本目录出发
        * 先判定一下,当前目录里,看看是否包含要删除的文件, 如果是就删除 否则就跳过下一个
        * 如果当前这里包含一些目录,针对目录进行递归
         * */
        scanDir(root,deletename);
    }
 private static void scanDir(File root, String deletename) {
        //列出root下的文件和目录
        File[] files=root.listFiles();  //数组接收了当前文件目录
        if(files==null){
            //当前文件是空 ,就没有判定的需要了直接返回
            //按递归的思想就是  这里不行这条路径就结束了
            return ;
        }
        //开始进行遍历
        for (File f:files) {
            if(f.isDirectory()){
                //如果是目录的话 就继续递归
                scanDir(f,deletename);
            }else{
                //另一种 不是目录的话,只要包含就可以删了
                if(f.getName().contains(deletename)){
                    System.out.println("已经找到要删除的文件是否要删除"+f.getAbsolutePath());
                    //找到之后 让用户确定是否要删除  如果删除 就输入 确认删除
                    String choic=scanner.next();
                    //用户输入后 进行判定
                    if(choic.equals("确认删除")){
                        f.delete();
                        System.out.println("删除成功");
                    }else{
                        System.out.println("删除取消");
                    }

                }
            }
        }
    }

 运行结果和执行过程

 

4.2、普通文件复制(就是不能复制目录)

思路:
(1)输入你要拷贝的文件路径包括当前你要拷贝的文件名(这先叫做被拷贝文件)

(2)输入你要拷贝在哪里这里还需要你写相同的文件名,因为是拷贝  (这里就叫做拷贝文件)

(3)两次输入完成之后 判定你要被拷贝的文件必须存在并且判定拷贝文件不能存在(要不这不就覆盖了)

(4)判定结束说明没有问题,字节流输入 输出创建 对象开始进行被拷贝文件输入拷贝文件输出(FileOutputStream会自动创建文件)

//拷贝文件
    public static void main(String[] args) {
        //输入两个路
        // 源文件 和  目标文件
        /*
        * 解释 一个 思路就 拷贝出来的文件肯定是不存在的
        * */
        Scanner scanner=new Scanner(System.in);
        System.out.println("请输入你要拷贝的文件路径");
        String srcPath=scanner.next();
        System.out.println("请输入你要拷贝到那个位置");
        String destPath=scanner.next();
        File srcfile=new File(srcPath);
        //首先就是你要拷贝的地方要有东西 ,所以这个文件他一定是要存在的
        if(!srcfile.isFile()){
            //如果文件不存在  ,也就不存在打印这一说了 可以结束了
            System.out.println("原文件路径有问题");
            return ;
        }
        File desfile=new File(destPath);
        //这个拷贝文件也需要判定,为什么呢,因为如果有重复就会产生覆盖
       if(desfile.isFile()){
           System.out.println("拷贝文件名重复");
           return ;
       }
       //进行拷贝  try()  里面是可以放多个 字节流对象的 分号隔开
        try(InputStream inputStream =new FileInputStream(srcfile);
            OutputStream outputStream=new FileOutputStream(desfile)){
            //进行文件读操作
            while(true){
                int b=inputStream.read();
                if(b==-1){
                    break;
                }
                outputStream.write(b);
            }
        } catch (FileNotFoundException e) {
            throw new RuntimeException(e);  //这个异常其实可以删除了,删除了也不会影响
        } catch (IOException e) {
            throw new RuntimeException(e);  //前面的异常继承该异常 所以都是同一个在起作用
        }
    }

 运行结果及执行过程

4.3、查找包含某字符的文件

扫描指定目录,并找到名称或者内容中包含指定字符的所有普通文件(不包含目录)

思路:

(1)第一步输入需扫描的文件路径  判定路径是否是目录

(2)创建一个List集合用来装包含这些字符的文件路径

(3)写一个深度优先遍历  因为给的当前路径也可能包含多个子目录,这里需要文件数组来接收当前文件列表该方法是 (listFiles),然后进行判定如果数组为空或者数组长度为0就递归返回,然后操作将文件分别进行判定是目录的就继续递归,如果不是就进行扫描是否有我们需要的字符出现(这里需要写一个方法来判定是否有这些字符)

(4)判定是否有这些字符的方法 采用思路:使用StringBuilder 来接收文件中的所有字符,然后判定如果该字符在StringBuilder对象中就返回true

注:以下三个代码是写在一个类中的,后面里两个是第一个代码中调用的自定义方法

public static void main(String[] args) throws IOException {
        Scanner scanner=new Scanner(System.in);
        System.out.println("请输入要扫描的的根目录");
        String rootDirPath=scanner.next();
        //输入路径直接使用文件创建对象
        File rootdir=new File(rootDirPath);
        //判定当前文件是否是目录
        if(!rootdir.isDirectory()){
            System.out.println("您输入的根目录不存在或者不是根目录");
            return ;
        }
        System.out.println("请输入要找出的文件的字符");
        //输入字符即可 
        String token=scanner.next();
        //创建一个List集合是为了  接收包含字符的路径
        List<File> result=new ArrayList<>();
        //文件成树形结构  深度优先遍历
        scanDirWithContent(rootdir,token,result);
        //以下就是基础的打印方法,就不多介绍了
        System.out.println("共找到了符合条件的文件" + result.size());
        for(File f:result){
            System.out.println(f.getCanonicalPath());
        }
    }
private static void scanDirWithContent(File rootdir, String token, List<File> result) {
        //这里是用了一个文件列表的方法  listFile 返回的是一个数组 用文件数组接收
        File[] files=rootdir.listFiles();
        //判定文件是 为空返回  文件长度为0返回也就是啥都没有写
        if(files==null||files.length==0){
            return ;
        }
        //如果有文件 将文件分开分别判断
        for (File file: files){
            //如果是目录的话就再递归
            if(file.isDirectory()){
                scanDirWithContent(file,token,result);
            }else{
                //如果是文件 就要进行字符查找了, 写一个方法进行字符查找
                if(isContentContains(file,token)){
                    //如果查找到了就添加路径
                    result.add(file.getAbsoluteFile());
                }
            }
        }
    }
private static boolean isContentContains(File file, String token) {
        //这里采用StringBuilder是因为可变性
        StringBuilder sb=new StringBuilder();
        //字节流输入中放入文件路径    Scanner可以放入字节流 使用更多方法
        try(InputStream inputStream=new FileInputStream(file);
           Scanner scanner=new Scanner(inputStream,"UTF-8")){
            //这下就完事了,正常将每个字符接收到 StringBuilder对象中
            while(scanner.hasNextLine()){
                sb.append(scanner.nextLine());
                sb.append("\r\n");
            }
        } catch (FileNotFoundException e) {
            throw new RuntimeException(e);
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
        //返回这里不要感觉难 其实不难,查找找的字符串如果存在的话sb返回值就是一个正数数值 !=-1也就是true
        return sb.indexOf(token)!=-1;
    }

运行结果及执行过程:

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/155810.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【自学Python】Python类型转换

Python类型转换 Python类型转换教程 虽然 Python 是弱类型编程语言&#xff0c;不需要像 Golang 或 C/C 语言那样还要在使用 变量 前声明变量的类型&#xff0c;但在一些特定场景中&#xff0c;仍然需要用到类型转换。 对 Python 内置的数据类型进行转换时&#xff0c;可以使…

valgrind callgrind使用

valgrind --toolcallgrind --dump-instryes ./foo # ,这里会运行很久,执行完毕后会生成一个callgrind.out.1266511如果你调试的程序是多线程&#xff0c;你也可以在命令行中加一个参数 -separate-threadsyes。这样就会为每个线程单独生成一个性能分析文件。如下&#xff1a;val…

好玩的小游戏系列 (一)基于html+js 原生贪吃蛇

一朵花如果只被用来观赏那只呈现出它的外在意义只是它生命的一部分若是不能够将其内在更实质的美发挥出来充其量也不过就是一朵死的花而已。 目录 一、前言 二、代码介绍 三、效果显示 四、编码实现 index.html jquery-1.10.2.js 五、获取源码 获取源码&#xff1f;私信…

【vue2】组件进阶与插槽(匿名·具名·作用域插槽详解)

&#x1f973;博 主&#xff1a;初映CY的前说(前端领域) &#x1f31e;个人信条&#xff1a;想要变成得到&#xff0c;中间还有做到&#xff01; &#x1f918;本文核心&#xff1a;v-modedl表单双向绑定、ref|$ref操作dom、dynamic动态组件、$nextTick同步、匿名插槽、具…

Java常用开发工具有哪些

本节介绍一下 Java 常用的几个开发工具。下面这些工具或许功能和作用不同&#xff0c;但是有着一个共同的主旨&#xff0c;那就是——它们都是为了给 Java 编码和开发提供卓越的支持。 常用源码编辑工具介绍 Java 源代码本质上其实就是普通的文本文件&#xff0c;所以理论上来…

【阶段三】Python机器学习16篇:机器学习项目实战:集成模型介绍、随机森林模型的基本原理与随机森林分类模型

本篇的思维导图: 集成模型介绍 集成学习模型使用一系列弱学习器(也称为基础模型或基模型)进行学习,并将各个弱学习器的结果进行整合,从而获得比单个学习器更好的学习效果。集成学习模型的常见算法有Bagging算法和Boosting算法两种。 算法类型 模型名称

lua 入门

安装 linux 下安装方式 curl -R -O http://www.lua.org/ftp/lua-5.4.4.tar.gz tar zxf lua-5.4.4.tar.gz cd lua-5.4.4 # 编译并测试没有问题 make all test make install卸载 cd lua-5.4.4 # 删除相关配置,之后可以删除 lua-5.4.4 make uninstall执行 文件以 .lua 结尾 方…

Flume第一章:环境安装

系列文章目录 Flume第一章&#xff1a;环境安装 文章目录系列文章目录前言一、Flume是什么&#xff1f;二、环境安装1.文件下载2.环境安装3.官方案例三、几个案例1.实时监控 Hive 日志&#xff0c;并上传到 HDFS 中2.使用 Flume 监听整个目录的文件&#xff0c;并上传至 HDFS3…

【构造】Codeforces Round #843 (Div. 2) B Gardener and the Array

Problem - B - Codeforces题意&#xff1a;给定一个序列&#xff0c;让你判断是否存在两个子序列使得这两个子序列或起来相等思路&#xff1a;设两个子序列是a和b两个子序列凭空出现&#xff0c;那肯定考虑构造满足的条件是&#xff1a;a!bf(a)f(b)如果只考虑第二个条件&#x…

java系列文章之反射

文章目录一、动态语言二、反射机制概念三、反射的应用场合1. 编译时类型和运行时类型2. 编译时类型无法获取具体方法四、 反射 API五、反射使用步骤六、获取 Class 对象的 3 种方法七、创建对象的两种方法总结一、动态语言 动态语言&#xff0c;是指程序在运行时可以改变其结构…

读书:《5%的改变》

《5%的改变》 我们并不需要100%的改变&#xff0c;彻底推翻以前的旧习惯&#xff0c;对于绝大多数人来说&#xff0c;并不太现实&#xff0c;不如考虑一下只改变5%。 一天结束&#xff0c;22:00&#xff0c;开始为睡觉做准备&#xff0c;反思一下&#xff0c;发现今天好像什…

Pytorch LSTM实现中文单词预测(附完整训练代码)

Pytorch LSTM实现中文单词预测(附完整训练代码) 目录 Pytorch LSTM实现中文单词预测(词语预测 附完整训练代码) 1、项目介绍 2、中文单词预测方法&#xff08;N-Gram 模型&#xff09; 3、训练词嵌入word2vec&#xff08;可选&#xff09; 4、文本预处理 &#xff08;1&…

Java面向对象之继承

目录继承概述、使用继承的好处总结继承的设计规范、内存运行原理总结继承的特点总结继承后&#xff1a;成员变量、成员方法的访问特点总结继承后&#xff1a;方法重写继承后&#xff1a;子类构造器的特点总结继承后&#xff1a;子类构造器访问父类有参构造器总结this、super使用…

k8s之DaemonSet

写在前面 假定现在有一个这样的需求&#xff0c;需要收集每个Node的运行状态信息&#xff0c;并进行上报&#xff0c;假设有4个节点&#xff0c;我们可以使用Deployment 来实现吗&#xff1f;好像是可以的&#xff0c;我们只需要将repliacas设置为4不就行了&#xff0c;但是de…

怎样让公司全员贡献结构化内容?

- 1 - 问题 一个朋友在一个生产型企业的文档团队负责产品文档&#xff0c;他们使用DITA来编写各类文档&#xff0c;比如&#xff1a;公司管理文档、产品介绍、产品使用说明、产品安装手册等。 DITA 是基于XML的体系结构&#xff0c;用于编写、制作、交付面向主题的信息类型…

【NI Multisim 14.0 操作实例——音量控制电路】

目录 序言 一、音量控制电路 &#x1f34a;1.设置工作环境 &#x1f34a; 2.设置原理图图纸 &#x1f34a; 3.设置图纸的标题栏 &#x1f34a; 4.放置元器件 &#x1f34a; 5.编辑元器件属性 &#x1f34a; 6. 布局元器件 序言 NI Multisim最突出的特点之一就是用户界面…

数字IC设计、验证、FPGA笔试必会 - Verilog经典习题 (一)四选一多路器

数字IC设计、验证、FPGA笔试必会 - Verilog经典习题 &#xff08;一&#xff09;四选一多路器 &#x1f508;声明&#xff1a; &#x1f603;博主主页&#xff1a;王_嘻嘻的CSDN博客 &#x1f9e8;未经作者允许&#xff0c;禁止转载 &#x1f511;系列专栏&#xff1a; &#x…

Mercurius <11.5.0 存在拒绝服务漏洞(CVE-2023-22477)

漏洞描述 Mercurius 是NPM仓库中的开源组件&#xff0c;用作于 Fastify Web 框架的 GraphQL 适配器。 11.5.0 之前版本的 Mercurius 开启“订阅”功能时&#xff0c;任何 Mercurius 用户都可以通过 WebSocket 向 /graphql 端点&#xff08;如&#xff1a;ws://127.0.0.1:1337…

【屏幕驱动移植】点亮OLED屏幕并播放视频

写在前面 硬件软件准备: 名称备注屏幕SSD1106本文章所使用的的屏幕型号&#xff0c;仅仅作为驱动移植示例&#xff0c;其他型号的都可以按照本文的方法推广树莓派3B用于驱动屏幕&#xff0c;树莓派2B3B4B等型号都可以ESP32开发板用于驱动屏幕&#xff0c;具体是ESP32还是ESP32…

都2023年啦~用python来玩一次股票.....

人生苦短&#xff0c;我用python 这不是2023年已经来了吗&#xff1f; 总不能空着手回去吧&#xff1f; 这次简单用python来玩一下股票~ 本章源码更多电子书点击文末名片~ 准备工作 我们需要使用这些模块&#xff0c;通过pip安装即可。 后续使用的其它的模块都是Python自…