10.0 输入输出 I/O

IO操作主要是指使用Java程序完成输入(Input)、输出(Output)操作。所谓输入是指将文件内容以数据流的形式读取到内存中,输出是指通过Java程序将内存中的数据写入到文件中,输入、输出操作在实际开发中应用较为广泛。本章将针对IO的相关操作进行讲解。

1. File类

1.1. File类的构造方法

File类提供了多个构造方法用于创建File对象,具体如下表所示。

方法声明

功能描述

File(String pathname)

通过指定的一个字符串类型的文件路径来创建一个新的File对象

File(String parent,String child)

根据指定的一个字符串类型的父路径和一个字符串类型的子路径(包括文件名称)创建一个File对象

File(File parent,String child)

根据指定的File类的父路径和字符串类型的子路径(包括文件名称)创建一个File对象

下面通过一个案例演示如何使用File类的构造方法创建File对象。具体代码如下所示。

@Test
public void test1(){File f1 = new File("D:\\file\\hello.txt");     //使用绝对路径创建File对象File f2 = new File("src\\Hello.java");         //使用相对路径创建File对象System.out.println(f1);System.out.println(f2);
}

注意:案例在创建File对象时传入的路径使用了\\,这是因为Windows中的目录符号为反斜线\,但反斜线\在Java中是特殊字符,具有转义作用,所以使用反斜线\时,前面应该再添加一个反斜线,即为\\。此外,目录符号还可以用正斜线/表示,如“D:/file/hello.txt”。

1.2. File类的常用方法

File类提供了一系列方法,例如判断文件是否存在、获取文件的名称、文件的大小、文件的路径、删除文件等,用于操作File类对象内部封装的路径指向的文件或者目录。具体如下表所示。

方法声明

功能描述

boolean exists()

判断File对象对应的文件或目录是否存在,若存在则返回true,否则返回false

boolean delete()

删除File对象对应的文件或目录,若删除成功则返回true,否则返回false

boolean createNewFile()

当File对象对应的文件不存在时,该方法将新建一个文件,若创建成功则返回true,否则返回false

String getName()

返回File对象表示的文件或文件夹的名称

String getPath()

返回File对象对应的路径

String getAbsolutePath()

返回File对象对应的绝对路径(在Unix/Linux等系统上,如果路径是以正斜线/开始,则这个路径是绝对路径;在Windows等系统上,如果路径是从盘符开始,则这个路径是绝对路径)

String getParentFile()

返回File对象对应目录的父目录(即返回的目录不包含最后一级子目录)

boolean canRead()

判断File对象对应的文件或目录是否可读,若可读则返回true,反之返回false

boolean canWrite()

判断File对象对应的文件或目录是否可写,若可写则返回true,反之返回false

boolean isFile()

判断File对象对应的是否是文件(不是目录),若是文件则返回true,反之返回false

boolean isDirectory()

判断File对象对应的是否是目录(不是文件),若是目录则返回true,反之返回false

boolean isAbsolute()

判断File对象对应的文件或目录是否是绝对路径

long lastModified()

返回1970年1月1日0时0分0秒到文件最后修改时间的毫秒值

long length()

返回文件内容的长度(单位是字节)

String[] list()

递归列出指定目录的全部内容(包括子目录与文件),只是列出名称

File[] listFiles()

返回一个包含了File对象所有子文件和子目录的File数组

下面通过一个案例演示如何使用File类的常用方法。具体代码如下所示。

@Test
public void test2() {File file = new File("src/Hello.java");System.out.println("文件是否存在:" + file.exists());System.out.println("文件名:" + file.getName());System.out.println("文件大小:" + file.length() + "bytes");System.out.println("文件相对路径:" + file.getPath());System.out.println("文件绝对路径:" + file.getAbsolutePath());System.out.println("文件是否为文件:" + file.isFile());System.out.println("文件删除是否成功:" + file.delete());
}

1.3. 遍历目录下的文件

通过调用File类中的list()方法,可以遍历目录下的文件。按照调用方法的不同,目录下的文件遍历可分为以下3种方式。

  • 遍历指定目录下的所有文件。

@Test
public void test3(){// 创建File对象File file = new File("D:\\workspace-java\\case05");if (file.isDirectory()) {               // 判断File对象对应的目录是否存在String[] names = file.list ();      // 获得目录下的所有文件的文件名for (String name : names) {System.out.println(name);      // 输出文件名}}
}
  • 遍历指定目录下指定扩展名的文件。

有时程序需要获取指定类型的文件,如获取指定目录下所有的“.java”文件。针对这种需求,File类提供了一个重载的list()方法,该方法接收一个FilenameFilter类型的参数。FilenameFilter是一个接口,被称作文件过滤器,其中定义了一个抽象方法accept()用于依次对指定File的所有子目录或文件进行迭代。在调用list()方法时,需要实现文件过滤器FilenameFilter,并在accept()方法中进行筛选,从而获得指定类型的文件。

@Test
public void test4() {// 创建File对象File file = new File("D:\\workspace-java\\case05\\src\\com\\wfit\\test01");// 创建过滤器对象FilenameFilter filter = new FilenameFilter() {// 实现accept()方法public boolean accept(File dir, String name) {File currFile = new File(dir, name);// 如果文件名以.java结尾返回true,否则返回falseif (currFile.isFile() && name.endsWith(".java")) {return true;} else {return false;}}};if (file.exists()) { // 判断File对象对应的目录是否存在String[] lists = file.list(filter); // 获得过滤后的所有文件名数组for (String name : lists) {System.out.println(name);}}
}
  • 遍历包括子目录中的文件在内的所有文件。

有时候在一个目录下,除了文件还有子目录,如果想获取所有子目录下的文件,list()方法显然不能满足要求,这时可以使用File类提供的另一个方法listFiles()。listFiles()方法返回一个File对象数组,当对数组中的元素进行遍历时,如果元素中还有子目录需要遍历,则可以使用递归再次遍历子目录。

@Test
public void test5() {// 创建一个代表目录的File对象File file = new File("D:\\workspace-java\\case05\\src\\com\\wfit");fileDir(file);  // 调用FileDir方法
}
public void fileDir(File dir) {File[] files = dir.listFiles();   // 获得表示目录下所有文件的数组for (File file : files) {       // 遍历所有的子目录和文件if (file.isDirectory()) {fileDir(file);     // 如果是目录,递归调用fileDir()}System.out.println(file.getAbsolutePath()); // 输出文件的绝对路径}
}

1.4. 删除文件及目录

在操作文件时,可能会遇到需要删除一个目录下的某个文件或者删除整个目录的情况,这时可以调用File类的delete()方法。

@Test
public void test6(){File file = new File("D:\\test\\testdel");deleteDir(file);         // 调用deleteDir删除方法System.out.println("删除成功!");
}
public void deleteDir(File dir) {if (dir.exists()) {                 // 判断传入的File对象是否存在File[] files = dir.listFiles();    // 得到File数组for (File file : files) {        // 遍历所有的子目录和文件if (file.isDirectory()) {deleteDir(file);    // 如果是目录,递归调用deleteDir()} else {file.delete();      // 如果是文件,直接删除}}// 删除完一个目录里的所有文件后,就删除这个目录dir.delete();}
}

2. 字节流

在程序的开发中,经常需要处理设备之间的数据传输,而计算机中,无论是文本、图片、音频还是视频,所有文件都是以二进制(字节)形式存在的。对于字节的输入输出,I/O流提供了一系列的流,统称为字节流,字节流是程序中最常用的流,根据数据的传输方向可将其分为字节输入流和字节输出流。

JDK提供了两个抽象类InputStream和OutputStream,它们是字节流的顶级父类,所有的字节输入流都继承自InputStream,所有的字节输出流都继承自OutputStream。为了方便理解,可以把InputStream和OutputStream比作两根“水管”,具体如下:

在上图中,InputStream被看成一个输入管道,OutputStream被看成一个输出管道,数据通过InputStream从源设备输入到程序,通过OutputStream从程序输出到目标设备,从而实现数据的传输。由此可见,I/O流中的输入输出都是相对于程序而言的。

2.1. InputStream类

  • InputStream类的常用方法

InputStream类提供了一系列与读数据相关的方法。具体如下表所示。

方法声明

功能描述

int read()

从输入流读取一个8位的字节,把它转换为0~255之间的整数,并返回这一整数

int read(byte[] b)

从输入流读取若干字节,把它们保存到参数b指定的字节数组中,返回的整数表示读取字节的数目

int read(byte[] b,int off,int len)

从输入流读取若干字节,把它们保存到参数b指定的字节数组中,off指定字节数组开始保存数据的起始索引,len表示读取的字节数目

void close()

关闭此输入流并释放与该流关联的所有系统资源

  • InputStream体系结构

InputStream类虽然提供了一系列和读数据有关的方法,但是InputStream类是抽象类,不能被实例化,因此针对不同的功能,InputStream类提供了不同的子类,这些子类形成了一个体系结构。如下图所示。

  • 字节流FileInputStream

InputStream就是JDK提供的基本输入流,它是所有输入流的父类,FileInputStream是InputStream的子类,它是操作文件的字节输入流,专门用于读取文件中的数据。

@Test
public void test1(){File file = new File("d:\\filetest\\hello.txt");//从文件中读取数据try{FileInputStream in = new FileInputStream(file);int len = in.read();while(len != -1){System.out.print((char)len);len = in.read();}in.close();}catch (Exception e){System.out.println(e.toString());}
}

2.2. OutputStream类

  • OutputStream类的常用方法

OutputStream类提供了一系列与写数据相关的方法。具体如下表所示。

方法声明

功能描述

void write(int b)

向输出流写入一个字节

void write(byte[] b)

把参数b指定的字节数组的所有字节写到输出流

void write(byte[] b,int off,int len)

将指定byte数组中从偏移量off开始的len个字节写入输出流

void flush()

刷新此输出流并强制写出所有缓冲的输出字节

void close()

关闭此输出流并释放与此流相关的所有系统资源

  • OutputStream体系结构

OutputStream类虽然提供了一系列和写数据有关的方法,但是OutputStream类是抽象类,不能被实例化,因此针对不同的功能,OutputStream类提供了不同的子类,这些子类形成了一个体系结构。如下图所示。

  • 字节流FileOutputStream

OutputStream是JDK提供的最基本的输出流,与InputStream类似,OutputStream也是抽象类,它是所有输出流的父类。OutputStream是一个抽象类,如果使用此类,则必须先通过子类实例化对象。OutputStream类有多个子类,其中FileOutputStream子类是操作文件的字节输出流,专门用于把数据写入文件。

@Test
public void test2(){File file = new File("d:\\filetest\\hello.txt");//向文件中写入数据FileOutputStream out = null;try{out = new FileOutputStream(file);String str = "hello wfit";out.write(str.getBytes());out.close();}catch (Exception e){System.out.println(e.toString());}
}

2.3. 文件的复制

在应用程序中,I/O流通常都是成对出现的,即输入流和输出流一起使用。例如,文件的复制就需要通过输入流读取文件中的数据,再通过输出流将数据写入文件。

编写程序,使用FileInputStream和FileOutputStream将文件helloA.txt 的内容复制到helloB.txt文件中。

@Test
public void test3(){File helloA = new File("d:\\filetest\\helloA.txt");File helloB = new File("d:\\filetest\\helloB.txt");try{FileInputStream in = new FileInputStream(helloA);FileOutputStream out = new FileOutputStream(helloB);int len = in.read();while(len != -1){out.write(len);len = in.read();}in.close();out.close();}catch (Exception e){System.out.println(e.toString());}
}

3. 字符流

前面讲解内容都是通过字节流直接对文件进行读写,如果读写的文件内容是字符,考虑到使用字节流读写字符可能存在传输效率以及数据编码问题,此时建议使用字符流。同字节流一样,字符流也有两个抽象的顶级父类,分别是Reader类和Writer类。其中Reader类是字符输入流,用于从某个源设备读取字符。Writer类是字符输出流,用于向某个目标设备写入字符。

3.1. Reader类

  • Reader类的常用方法

Reader类提供了一系列与读数据相关的方法。具体如下表所示。

方法声明

功能描述

int read()

以字符为单位读数据

int read(char cbuf[])

将数据读入char类型数组,并返回数据长度

int read(char cbuf[],int off,int len)

将数据读入char类型数组的指定区间,并返回数据长度

void close()

关闭数据流

long transferTo(Writer out)

将数据直接读入字符输出流

  • Reader体系结构

Reader类作为字符流的顶级父类,也有许多子类,下面通过一张继承关系图列举Reader类的常用子类。如下图所示。

  • 字符流FileReader

在程序开发中,经常需要对文本文件的内容进行读取,如果想从文件中直接读取字符便可以使用字符输入流FileReader,通过此流可以从关联的文件中读取一个或一组字符。

编写程序,使用FileReader对象从hello.txt文件中读出字符并输出。

@Test
public void test1(){File file = new File("d:\\filetest\\hello.txt");try{FileReader in = new FileReader(file);BufferedReader ins = new BufferedReader(in);String line;while ((line = ins.readLine()) != null){System.out.println(line);}ins.close();in.close();}catch (Exception e){System.out.println(e.toString());}
}

3.2. Writer类

  • Writer类的常用方法

Writer类提供了一系列与写数据相关的方法。具体如下表所示。

方法声明

功能描述

void write(int c)

以字符为单位写数据

void write(char cbuf[])

将char类型数组中的数据写出

void write(char cbuf[],int off,int len)

将char类型数组中指定区间的数据写出

void write(String str)

将String类型的数据写出

void wirte(String str,int off,int len)

将String类型指定区间的数据写出

void flush()

可以强制将缓冲区的数据同步到输出流中

void close()

关闭数据流

  • Writer体系结构

Writer类作为字符流的顶级父类,也有许多子类,下面通过一张继承关系图列举Writer类的常用子类。如下图所示。

  • 字符流FileWriter

在程序开发中,有时需要向文本文件写入内容,通过字符流向文本文件写入内容需要使用FileWriter类,FileWriter类可以一次向文件中写入一个或一组字符。

下面通过一个案例学习如何使用FileWriter字符流将字符写入文件。具体代码如下所示。

@Test
public void test2(){File file = new File("d:\\filetest\\hello.txt");try{FileWriter out = new FileWriter(file);String str = "hello wfit";out.write(str);out.close();}catch (Exception e){System.out.println(e.toString());}
}

4. 转换流

I/O流分为字节流和字符流,字节流和字符流之间可以进行转换。JDK提供了两个类用于将字节流转换为字符流,它们分别是InputStreamReader和OutputStreamWriter,也称为转换流,其作用如下所示。

(1)InputStreamReader是Reader的子类,它可以将一个字节输入流转换成字符输入流,方便直接读取字符。

(2)OutputStreamWriter是Writer的子类,它可以将一个字节输出流转换成字符输出流,方便直接写入字符。

@Test
public void test(){File file = new File("d:\\filetest\\hello.txt");try{// 创建一个字节输入流inFileInputStream in = new FileInputStream(file);// 将字节输入流in转换成字符输入流isrInputStreamReader isr = new InputStreamReader(in);int len = isr.read();while(len != -1){System.out.print((char)len);len = isr.read();}isr.close();in.close();}catch (Exception e){System.out.println(e.getMessage());}
}

5. 序列化和反序列化

程序在运行过程中,数据都保存在Java中的对象中(内存),但很多情况下我们都需要将一些数据永久保存到磁盘上。为此,Java提供了对象序列化,对象序列化可以将对象中的数据保存到磁盘。

5.1. 序列化和反序列化

对象序列化(Serializable)是指将一个Java对象转换成一个I/O流的字节序列的过程。

反序列化(Deserialize)是指将I/O流中的字节序列恢复为Java对象的过程。

5.2. Serializable接口和Externalizable接口

对象实现支持序列化机制,这个对象所属的类必须是可序列化的。在Java中可序列化的类可通过实现Serializable或Externalizable两个接口之一。

Serializable接口

Externalizable接口

系统自动存储必要的信息

由程序员决定所存储的信息

Java内部支持,易于实现,只需实现该接口即可,不需要其他代码支持

接口中只提供了两个抽象方法,实现该接口必须要实现这两个抽象方法

性能较差

性能较好

5.3. 实现序列化方法

与实现Serializable接口相比,虽然实现Externalizable接口可以带来一定性能上的提升,但由于实现Externalizable接口,需要实现两个抽象方法,所以实现Externalizable接口也将导致编程的复杂度增加。在实际开发时,大部分都采用实现Serializable接口的方式来实现对象序列化。使用Serializable接口实现对象序列化非常简单,只需要让目标类实现Serializable接口即可,无需实现任何方法。

  • Student类

public class Student implements Serializable {private static final long serialVersionUID = 1L;private String name;public String getName() {return name;}public void setName(String name) {this.name = name;}@Overridepublic String toString() {return "Student{" +"name='" + name + '\'' +'}';}
}
  • 测试类

public class TestDemo3 {@Testpublic void test1(){Student student = new Student();student.setName("张三");File file = new File("d:\\filetest\\hello.txt");//向文件中写入数据try{ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream(file));out.writeObject(student);out.close();}catch (Exception e){System.out.println(e.toString());}}@Testpublic void test2(){File file = new File("d:\\filetest\\hello.txt");//向文件中写入数据try{ObjectInputStream in = new ObjectInputStream(new FileInputStream(file));Student student = (Student) in.readObject();System.out.println(student);in.close();}catch (Exception e){System.out.println(e.toString());}}
}

serialVersionUID适用于Java的对象序列化机制。简单来说,Java的对象序列化机制是通过判断类的serialVersionUID来验证版本一致性。在进行反序列化时,JVM会把字节流中的serialVersionUID与本地相应实体类的serialVersionUID进行比较,如果相同就认为是一致的,可以进行反序列化,否则就会抛出序列化版本不一致的异常。因此,为了在反序列化时确保序列化版本的兼容性,最好在每一个要序列化的类中加入private static final long serialVersionUID的变量值,具体数值可自定义,默认是1L。

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

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

相关文章

TiDB专题---2、TiDB整体架构和应用场景

上个章节我们讲解了TiDB的发展和特性,这节我们讲下TiDB具体的架构和应用场景。首先我们回顾下TiDB的优势。 TiDB的优势 与传统的单机数据库相比,TiDB 具有以下优势: 纯分布式架构,拥有良好的扩展性,支持弹性的扩缩容…

一、Linux系统概述和安装

目录 1、Linux系统概述 2、Linux发行版介绍 3、虚拟机软件介绍 4、VMware安装 5、Linux系统(CentOS)系统安装 6、登录并查看IP地址 7、Linux连接工具CRT使用 7.1 概述 7.2 CRT安装 7.3 使用步骤 7.4 文件上传 8、Linux的快照 8.1 作用 8.2…

Go 从编译到执行

一、Go运行编译简介 Go语言(也称为Golang)自从2009年由Google发布以来,已成为现代软件开发中不可或缺的一部分。设计者Rob Pike, Ken Thompson和Robert Griesemer致力于解决多核处理器、网络系统和大型代码库所引发的现实世界编程问题。我们…

kubeadm快速搭建k8s高可用集群

1.安装及优化 1.1基本环境配置 1.环境介绍 (1).高可用集群规划 主机名ip地址说明k8s-master01192.168.2.96master节点k8s-master02192.168.2.97master节点k8s-master03192.168.2.98master节点k8s-node01192.168.2.99node节点k8s-node02192.168.2.100n…

【10张图带你搞清楚生成树协议】

STP协议分类 BPDU,网桥协议数据单元 STP路径开销,以链路带宽为准,两个标准,现在主要以NEW为准 在网络刚开始运行的阶段,所有交换机都会从所有端口发送BPDU,大家都认为自己是root,随着B…

基于YOLOv8深度学习的火焰烟雾检测系统【python源码+Pyqt5界面+数据集+训练代码】目标检测、深度学习实战

《博主简介》 小伙伴们好,我是阿旭。专注于人工智能、AIGC、python、计算机视觉相关分享研究。 ✌更多学习资源,可关注公-仲-hao:【阿旭算法与机器学习】,共同学习交流~ 👍感谢小伙伴们点赞、关注! 《------往期经典推…

增强静态数据的安全性

静态数据是数字数据的三种状态之一,它是指任何静止并包含在永久存储设备(如硬盘驱动器和磁带)或信息库(如异地备份、数据库、档案等)中的数字信息。 静态数据是指被动存储在数据库、文件服务器、端点、可移动存储设备…

多线程05

前言 前面我们说到了死锁以及线程可见性的问题 我们将线程可见性主要归结于是JVM自身的一个bug 一个线程写一个线程读 会将一直不变的变量优化到直接从寄存器中读取,而不是缓存等读取,因为这样我们就设置了使用volatile关键字使得用到这个变量的时候必须从内存中读取数据 死锁主…

项目终验的exce表格缩放,排版等经常使用

xxx个项目的验收资料 1.申请表等等很多信息 需求:放在一页内等办法 上述文档,在excel表格打印预览中都是在两页中,很难调节,这个时候采用wps专业版本即可。 wps排版经常使用的功能如下: 经常使用的是 1.把所有列打印…

const 和 constexpr 深入学习

在 C 中,const 和 constexpr 都可以用来修饰对象和函数。修饰对象时,const 表示它是常量,而 constexpr 表示它是一个常量表达式。常量表达式必须在编译时期被计算1。修饰函数时,const 只能用于非静态成员的函数,而 con…

WPF窗口样式的比较

WPF窗口样式的比较 1.WPF默认Window窗口 带有图标 标题栏 最小最大化推出按钮 <Window x:Class"GlowWindowDemo.MainWindow"xmlns"http://schemas.microsoft.com/winfx/2006/xaml/presentation"xmlns:x"http://schemas.microsoft.com/winfx/2006…

nginx: [alert] could not open error log file

先把cmd的报错信息粘出来 nginx: [alert] could not open error log file: CreateFile() “logs/error.log” failed (3: The system cannot find the path specified) 2023/11/29 11:27:37 [emerg] 5040#18772: CreateDirectory() “D:\enviroment\nginx-1.24.0\conf/temp/cli…

Linux学习笔记09、Shell命令之历史命令和自动补全

上一篇&#xff1a;Linux学习笔记08、Shell命令之常用命令缩写及全称 目录 1、历史命令&#xff1a; 1.1、查看所有历史命令列表&#xff1a; 1.2、查看指定历史命令&#xff1a; 1.3、清除历史命令&#xff1a; 2、自动补全 2.1、当字符串唯一时&#xff1a; 2.2、当有多个…

力扣题:字符的统计-11.25

力扣题-11.25 [力扣刷题攻略] Re&#xff1a;从零开始的力扣刷题生活 力扣题1&#xff1a;387. 字符串中的第一个唯一字符 解题思想&#xff1a;直接遍历即可 class Solution(object):def firstUniqChar(self, s):""":type s: str:rtype: int""&qu…

机器学习——决策树

1.决策树 2.熵&#xff08;不确定程度&#xff09; 3.信息增益 & 信息增益比 3.1 信息增益 & 信息增益比 的 概念 3.2 案例解释说明 &#xff13;.&#xff12;.&#xff11;数据集说明 &#xff13;.&#xff12;.&#xff12;计算 &#xff14;&#xff0e;&#x…

智能井盖传感器怎么监测井盖出现倾斜?

智能井盖传感器是一种先进的智能设备&#xff0c;能够二十四小时连续监测井盖是否出现倾斜。其工作原理主要是依靠内置的传感器&#xff0c;以及搭载的MEMS“芯”技术。便于智能井盖传感器实时感知到井盖的姿态变化&#xff0c;一旦发现有倾斜的现象&#xff0c;就会立即向运维…

Jmeter之压力测试总结!

一、基本概念 1.线程组N&#xff1a;代表一定数量的并发用户&#xff0c;所谓并发就是指同一时刻访问发送请求的用户。线程组就是模拟并发用户访问。 2.Ramp-Up Period(in seconds)&#xff1a;建立所有线程的周期&#xff0c;就是告诉jmeter要在多久没启动所有线程&#xff…

python+pytest接口自动化(5)-requests发送post请求

简介 在HTTP协议中&#xff0c;与get请求把请求参数直接放在url中不同&#xff0c;post请求的请求数据需通过消息主体(request body)中传递。 且协议中并没有规定post请求的请求数据必须使用什么样的编码方式&#xff0c;所以其请求数据可以有不同的编码方式&#xff0c;服务…

elasticsearch安装分词器插件

查看插件安装情况 elasticsearch-plugin list 插件在线安装 bin/elasticsearch-plugin install analysis-icu 离线安装ik分词 cd plugins wget https://github.com/medcl/elasticsearch-analysis-ik/releases/download/v7.17.7/elasticsearch-analysis-ik-7.17.7.zip unzi…

Wireshark之Intro, HTTP, DNS

源码地址&#x1f447; moranzcw/Computer-Networking-A-Top-Down-Approach-NOTES: 《计算机网络&#xff0d;自顶向下方法(原书第6版)》编程作业&#xff0c;Wireshark实验文档的翻译和解答。 (github.com) 目录 &#x1f33c;Introduce &#x1f3a7;前置 &#x1f3a7;过…