Java进阶-IO(1)

进入java IO部分的学习,首先学习IO基础,内容如下。需要了解流的概念、分类还有其他一些如集合与文件的转换,字符编码问题等,这次先学到字节流的读写数据,剩余下次学完。
在这里插入图片描述

一、IO基础

1、背景

1.1 数据存储问题

变量、数组、对象和集合中存储的数据是暂时存在的,一旦程序结束它们就会丢失。

解决:为了永久保存程序创建的数据,需要将其保存到磁盘文件中。

1.2 流与IO

1)流
是一种抽象概念,是对数据传输的总称。即数据在设备间的传输称为流,流的本质是数据传输

  • Java 中所有数据都是用流读写的。
  • 流是一组有序的数据序列(以输入流的形式获取,输出流的形式输出),将数据从一个地方带到另一个地方。(可类比水管里水的流动)
    • 输入:将数据从各种输入设备(包括文件、键盘等)中读取到内存中
    • 输出:将数据写入到各种输出设备(比如文件、显示器、磁盘等)
  • 流相关的类都封装在 java.io 包中,且每个数据流都是一个对象。

可分为输入和输出两种模式

输入流:文件、网络、数据库及其他数据源—(输入流)—>目的地
输出流:源—(输出流)—>文件、网络、数据库及其他数据源

2)Java 的 I/O(输入/输出)技术
作用:处理设备间数据传输问题

将数据保存到文本文件和二进制文件中, 以达到永久保存数据的要求。常见应用:文件复制;文件上传;文件下载

2、流的分类

2.1 按流的方向

1)输入流(input)
用于读数据

所有输入流类都是 InputStream 抽象类(字节输入流)和 Reader 抽象类(字符输入流)的子类

InputStream(字节输入流)结构------>了解即可

FileInputStream  文件输入流
PipedInputStream  管道输入流
ObjectInputStream  对象输入流FilterInputStream  过滤器输入流
- PushBackInputStream  回压输入流
- BufferedInputStream  缓冲输入流
- DataInputStream  数据输入流SequenceInputStream  顺序输入流
ByteArrayInputStream  字节数组输入流
StringBufferInputStream  缓冲字符串输入流

InputStream 类常用方法------>掌握

read()方法(重载)3- int read():从输入流读入一个 8 字节的数据,将它转换成一个 0~ 255 的整数,返回一个整数,如果遇到输入流的结尾返回 -1
- int read(byte[] b):从输入流读取若干字节的数据保存到参数 b 指定的字节数组中,返回的字节数表示读取的字节数,如果遇到输入流的结尾返回 -1
- int read(byte[] b,int off,int len):从输入流读取若干字节的数据保存到参数 b 指定的字节数组中,其中 off 是指在数组中开始保存数据位置的起始下标,len 是指读取字节的位数。返回的是实际读取的字节数,如果遇到输入流的结尾则返回 -1close():关闭数据流,当完成对数据流的操作之后需要关闭数据流
available():返回可以从数据源读取的数据流的位数
skip(long n):从输入流跳过参数 n 指定的字节数目markSupported():判断输入流是否可以重复读取
mark(int readLimit):如果输入流可以被重复读取,从流的当前位置开始设置标记,readLimit 指定可以设置标记的字节数
reset():使输入流重新定位到刚才被标记的位置,这样可以重新读取标记过的数据

两点注意:

最后 3 个方法一般结合使用,先用 markSupported() 判断,如果可以重复读取,则用 mark(int readLimit) 方法进行标记,标记完成后可以用 read() 方法读取标记范围内的字节数,最后用 reset() 方法使输入流重新定位到标记的位置,继而完成重复读取操作。

Java 中的字符是 Unicode 编码(双字节),而 InputerStream 是用来处理单字节的,在处理字符文本时不是很方便。这时可以使用 Java 的文本输入流 Reader 类,该类是字符输入流的抽象类,即所有字符输入流的实现都是它的子类,该类的方法与 InputerSteam 类的方法类似,不再赘述。

2)输出流(Output)
用于写数据

所有输出流类都是 OutputStream 抽象类(字节输出流)和 Writer 抽象类(字符输出流)的子类

OutputStream(字节输出流)结构------>了解即可

FileOutputStream  文件输出流
PipedOutputStream  管道输出流
ObjectOutputStream  对象输出流
FilterOutputStream  过滤器输出流
PrintStream  打印输出流
BufferedOutputStream  缓冲输出流
DataOutputStream  数据输出流
ByteArrayOutputStream  字节数组输出流

OutputStream 类常用方法------>掌握

write()方法(重载)3- int write(b):将指定字节的数据写入到输出流
- int write(byte[] b):将指定字节数组的内容写入输出流
- int write(byte[] b,int off,int len):将指定字节数组从 off 位置开始的 len 字节的内容写入输出流
close():关闭数据流
flush():刷新数据流
2.2 按数据单位
一般IO流是按数据单位(即数据类型)来分的
流的使用:
- 如果数据通过window自带的记事本软件打开,还可以读懂里面的内容,就用字符流,否则用字节流
- 如果不知道使用哪种类型的流,就用字节流(字节流是万能的流)

1)字节流
写数据
字节流抽象基类(父类)

InputStream:是字节输入流的所有类的超类(父类)
OutputStream:是字节输出流的所有类的超类
子类名特点:子类名称都是以其父类名作为子类名的后缀 (可参考上一节的输入流和输出流内容)

FileOutputStream:文件输出流用于将数据写入File

FileOutputStream(String name):创建文件输出流以指定的名称写入文件

使用字节输出流写数据的步骤:(创建对象–>写数据–>释放资源)

1、创建字节输出流对象(做了3件事情)
- 调用系统功能创建了文件
- 创建字节输出流对象
- 让字节输出流对象指向文件2、调用字节输出流对象的写数据方法
3、释放资源(关闭此文件输出流并释放与此流相关联的任何系统资源)

示例1

import java.io.FileOutputStream;
import java.io.IOException;public class FileOutputStreamDemo1 {public static void main(String[] args) throws IOException { // 抛出异常// 创建字节输出流对象// FileOutputStream(String name):创建文件输出流以指定的名称写入文件FileOutputStream fos=new FileOutputStream("C:\\Users\\ASUS\\Desktop\\project1\\src\\fos.txt");/*做了三件事情:1:调用系统功能创建了文件2:创建字节输出流对象3:让字节输出流对象指向创建好的文件*///void write(int b):将指定的字节写入此文件输出流fos.write(97);  // afos.write(57);  // 9 (是字符不是数字)fos.write(55);  // 7 (是字符不是数字)// 最后都要释放资源// void close():关闭此文件输出流并释放与此流相关联的任何系统资源fos.close();}
}

运行结果(会生成一个fos.txt文件,打开查看内容如下)

a97

字节流写数据的三种方式(write()方法的重载–3个)

int write(b):将指定字节的数据写入到输出流
int write(byte[] b):将指定字节数组的内容写入输出流
int write(byte[] b,int off,int len):将指定字节数组从 off 位置开始的 len 字节的内容写入输出流

示例2

import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.nio.charset.StandardCharsets;public class FileOutputStreamDemo2 {public static void main(String[] args) throws IOException { // 抛出异常/*** 1.创建对象*/// 创建字节输出流对象// FileOutputStream(String name):创建文件输出流以指定的名称写入文件FileOutputStream fos1=new FileOutputStream("C:\\Users\\ASUS\\Desktop\\project1\\src\\fos.txt");/*** 2.写数据(3种方式)*/// void write(int b):将指定的字节写入此文件输出流
//        fos1.write(97);
//        fos1.write(98);
//        fos1.write(99);
//        fos1.write(100);
//        fos1.write(101);  // 结果:abcde// void write(byte[] b):将指定字节数组的内容写入输出流
//        byte[] bys={97,98,99,100,101};  //方法1:创建数组byte[] bys="abcde".getBytes();    //方法2:getBytes()方法
//        fos1.write(bys);      // 结果:abcde// void write(byte[] b,int off,int len):将指定字节数组从 off 位置开始的 len 字节的内容写入输出流
//        fos1.write(bys,0,bys.length);  // 结果:abcdefos1.write(bys,1,3);     // 结果:bcd/*** 3.释放资源*/fos1.close();}
}

getBytes()方法使用注意:

getBytes(String charsetName): 使用指定的字符集将字符串编码为 byte 序列,并将结果存储到一个新的 byte 数组中。getBytes(): 使用平台的默认字符集将字符串编码为 byte 序列,并将结果存储到一个新的 byte 数组中。

字节流写数据的两个小问题
a、实现换行(添加换行符)

windows:\r\n
linux:\n
mac:\r

b、实现追加写入

public FileOutputStream(String name,boolean append)创建文件输出流以指定名称写入文件。若第二个参数为true,则字节将写入文件的末尾而不是开头(开头覆盖,末尾追加)
- 默认是false,即写在开头,这样每运行一次都会覆盖原内容,追加写入时要改为true

示例3

import java.io.FileOutputStream;
import java.io.IOException;public class FileOutputStreamDemo3 {public static void main(String[] args) throws IOException {// 创建字节输出流对象//boolean append为true实现追加(将字节写入末尾而不是开头)FileOutputStream fos3=new FileOutputStream("C:\\Users\\ASUS\\Desktop\\project1\\src\\fos.txt",true);// 写数据for(int i=0;i<10;i++){fos3.write("hello".getBytes()); // getBytes()方法fos3.write("\r\n".getBytes()); // 每写完一次做一个换行}// 释放资源fos3.close();}
}

补充:

try-catch异常处理(一般文件相关的异常直接用throws抛出即可,但try-catch异常捕获方式也要了解,不懂的可以回去看异常处理部分)

finally:在异常处理时提供finally块来执行所有清除操作。如IO流中的释放资源。特点:被finally控制的语句一定会执行,除非JVM退出

示例4

import java.io.FileOutputStream;
import java.io.IOException;public class FileOutputStreamDemo4 {public static void main(String[] args) {FileOutputStream fos4 = null; // 初始化文件对象为null(全局+初始化)try {// 创建对象
//            fos4 = new FileOutputStream("Z:\\Ultimate JavaCode\\src\\test6\\fos4.txt"); //文件不存在会抛出FileNotFoundException异常fos4 = new FileOutputStream("C:\\Users\\ASUS\\Desktop\\project1\\src\\fos.txt");// 写入数据fos4.write("javaEE".getBytes());// 关闭资源fos4.close();// 若写入操作存在异常,则直接跳到catch捕获,不会执行关闭资源操作,这时需要引入finally} catch (IOException e) { // 捕获异常e.printStackTrace(); //存在异常时,追踪异常信息} finally { // 加入finally实现释放资源if(fos4!=null){ // 文件不为null时,才需要释放资源(防止文件为null时关闭资源出现空指针异常NullPointerException)try{fos4.close(); // 释放资源}catch (IOException e){e.printStackTrace();}}}}
}

读数据
字节流读数据(一次读一个字节数据)

FileInputStream:从文件系统中的文件获取输入字节
- FileInputStream(String name):通过打开与实际文件的连接来创建一个FileInputStream,该文件由文件系统中的路径名name命名使用字节输入流读数据的步骤:(创建对象-->读数据-->释放资源)
1、创建字节输入流对象(做了3件事情)
- 调用系统功能创建了文件
- 创建字节输出流对象
- 让字节输出流对象指向文件
2、调用字节输入流对象的读数据方法
3、释放资源(关闭此文件输出流并释放与此流相关联的任何系统资源)

字节流读数据的3种方式(read()方法重载–3个)

int read():从输入流读入一个 8 字节的数据,将它转换成一个 0~ 255 的整数,返回一个整数,如果遇到输入流的结尾返回 -1
int read(byte[] b):从输入流读取若干字节的数据保存到参数 b 指定的字节数组中,返回的字节数表示读取的字节数,如果遇到输入流的结尾返回 -1
int read(byte[] b,int off,int len):从输入流读取若干字节的数据保存到参数 b 指定的字节数组中,其中 off 是指在数组中开始保存数据位置的起始下标,len 是指读取字节的位数。返回的是实际读取的字节数,如果遇到输入流的结尾则返回 -1

案例1

import java.io.FileInputStream;
import java.io.IOException;public class FileInputStreamDemo1 {public static void main(String[] args) throws IOException {// 创建对象FileInputStream fis=new FileInputStream("C:\\Users\\ASUS\\Desktop\\project1\\src\\fos.txt");// 读数据
//        int bys= fis.read();
//        System.out.println(bys); // 结果:98(文件的字符是'b')
//        System.out.println((char)bys); // 大转小,强制转换,显示原字符(结果:b)/*字节流读数据标准代码如下1.fis.read():读数据2.by=fis.read():将读取的数据赋值给by3.by!=-1:判断读取到的数据是否是-1(到达文件末尾)*/int by; // 类型声明while((by=fis.read())!=-1){System.out.print((char)by); // 强转显示原字符(结果:bcd),注意用print而不是println}// 释放资源fis.close();}
}

案例2:字节流复制文本文件
需求:把文本文件的内容从一个文件中读取出来(数据源),然后写入到另一个文件中(目的地)

import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;public class FileInputStreamDemo2{public static void main(String[] args) throws IOException {// 创建对象FileInputStream fis=new FileInputStream("D:\\文本复制案例.txt"); // 从指定路径下读取要复制的文本文件(数据源)FileOutputStream fos=new FileOutputStream("C:\\Users\\ASUS\\Desktop\\project1\\src\\fos.txt");//目的地// 先从数据源(fis)读取,后写入目的地(fos)int by;while((by=fis.read())!=-1){ // 一次读取一个字节,直到读完(到文件末尾)fos.write(by); // 一次写入一个字节}// 释放资源fos.close();fis.close(); // 先创建的后释放}
}

案例3
需求:字节流读数据(一次读一个字符数组),用到int read(byte[] b)构造方法

import java.io.FileInputStream;
import java.io.IOException;public class FileInputStreamDemo3 {public static void main(String[] args) throws IOException {// 创建对象FileInputStream fis=new FileInputStream("C:\\Users\\ASUS\\Desktop\\project1\\src\\fos.txt");byte[] bys=new byte[1024]; // 1024及其整数倍int len;// 读取数据while((len=fis.read(bys))!=-1){ // 一次读一个字符数组,用到int read(byte[] b)构造方法System.out.println(new String(bys,0,len)); // new String方法转换为字符串输出到控制台}// 释放资源fis.close();}
}

字节缓冲流

BufferedOutputStream:该类实现缓冲输出流。 通过设置这样的输出流,应用程序可以将字节写入基础输出流,而不必为写入的每个字节调用底层系统。
BufferedInputStream:创建BufferedInputStream将创建一个内部缓冲区数组。当从流中读取或跳过字节时内部缓冲区将根据需要从所包含的输入流中重新填充,一次很多字节。

构造方法

字节缓冲输出流:BufferedOutputStream(OutputStream out)
字节缓冲输入流:BufferedInputStream(InputStream in)问题:构造方法里需要的是字节流而不是具体的文件或路径?
原因:字节缓冲流仅仅提供缓冲区,而真正的读写数据还得依靠基本的字节流对象进行操作。

示例

import java.io.*;public class BufferStreamDemo1 {public static void main(String[] args) throws IOException {
//        FileOutputStream fos=new FileOutputStream("C:\\Users\\ASUS\\Desktop\\project1\\src\\fos.txt");
//        BufferedOutputStream bos=new BufferedOutputStream(fos);// 创建字节缓冲输出流对象BufferedOutputStream bos=new BufferedOutputStream(new FileOutputStream("C:\\Users\\ASUS\\Desktop\\project1\\src\\fos.txt"));// 操作1:写数据bos.write("hello\n".getBytes());bos.write("javaEE\n".getBytes());// 创建字节缓冲输入流对象(用于读数据)BufferedInputStream bis=new BufferedInputStream(new FileInputStream("C:\\Users\\ASUS\\Desktop\\project1\\src\\fos.txt"));// 操作2:读数据// 方法1:一次读取一个字节数据
//        int by;
//        while((by=bis.read())!=-1){
//            System.out.println((char)by);
//        }// 方法2:一次读取一个字节数组byte[] bys=new byte[1024]; // 1024及其整数倍int len;while((len=bis.read(bys))!=-1){System.out.println(new String(bys,0,len)); // 转换为字符串输出到控制台}// 释放资源bos.close();}
}

运行结果(打开fos.txt文件,显示如下)

hello
javaEE

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

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

相关文章

代码随想录day11(1)字符串:反转字符串中的单词 (leetcode151)

题目要求&#xff1a;给定一个字符串&#xff0c;将其中单词顺序反转&#xff0c;且每个单词之间有且仅有一个空格。 思路&#xff1a;因为本题没有限制空间复杂度&#xff0c;所以首先想到的是用split直接分割单词&#xff0c;然后将单词倒叙相加。 但如果想让空间复杂度为O…

芯来科技发布最新NI系列内核,NI900矢量宽度可达512/1024位

参考&#xff1a;芯来科技发布最新NI系列内核&#xff0c;NI900矢量宽度可达512/1024位 (qq.com) 本土RISC-V CPU IP领军企业——芯来科技正式发布首款针对人工智能应用的专用处理器产品线Nuclei Intelligence(NI)系列&#xff0c;以及NI系列的第一款AI专用RISC-V处理器CPU IP…

网络爬虫部分应掌握的重要知识点

目录 一、预备知识1、Web基本工作原理2、网络爬虫的Robots协议 二、爬取网页1、请求服务器并获取网页2、查看服务器端响应的状态码3、输出网页内容 三、使用BeautifulSoup定位网页元素1、首先需要导入BeautifulSoup库2、使用find/find_all函数查找所需的标签元素 四、获取元素的…

基于springboot+vue的健身房管理系统

博主主页&#xff1a;猫头鹰源码 博主简介&#xff1a;Java领域优质创作者、CSDN博客专家、阿里云专家博主、公司架构师、全网粉丝5万、专注Java技术领域和毕业设计项目实战&#xff0c;欢迎高校老师\讲师\同行交流合作 ​主要内容&#xff1a;毕业设计(Javaweb项目|小程序|Pyt…

●139.单词拆分 ● 关于多重背包,你该了解这些! ●背包问题总结篇!

●139.单词拆分 物品&#xff1a;wordDict里面的单词&#xff1b;背包容量&#xff1a;s.size()。 1.dp[j]含义。dp[j]true表示字符串前j个可以拆分成字典中的单词。dp[s.size()] 就是最后的结果&#xff0c;整个字符串能&#xff08;true&#xff09;不能&#xff08;false…

Docker 创建容器并指定时区

目录 1. 通过环境变量设置时区&#xff08;推荐&#xff09;2. 挂载宿主机的时区文件到容器中3. 总结 要在 Docker 容器中指定时区&#xff0c;可以通过两种方式来实现&#xff1a; 1. 通过环境变量设置时区&#xff08;推荐&#xff09; 在 Docker 运行时&#xff0c;可以通…

CentOS安装Docker(黑马学习笔记)

Docker 分为 CE 和 EE 两大版本。CE 即社区版&#xff08;免费&#xff0c;支持周期 7 个月&#xff09;&#xff0c;EE 即企业版&#xff0c;强调安全&#xff0c;付费使用&#xff0c;支持周期 24 个月。 Docker CE 分为 stable test 和 nightly 三个更新频道。 官方网站上…

文件底层的理解之缓冲区

目录 一、缓冲区的初步认识 二、向文件中写数据的具体过程 三、缓冲区刷新的时机 一、缓冲区的初步认识 缓冲区其实就是一块内存区域&#xff0c;采用空间来换时间&#xff0c;可以提高使用者的效率。我们一直说的缓冲区其实是语言层面上的缓冲区&#xff0c;其实操作系统内部…

JVM 第一部分 JVM两种解释器 类加载过程和类加载器

JVM是跨平台跨语言的虚拟机&#xff0c;不直接接触硬件&#xff0c;位于操作系统的上一层 跟字节码文件直接关联&#xff0c;和语言没有关系 一次编译成字节码文件&#xff0c;多次执行 虚拟机可以分成三部分&#xff1a;类加载器&#xff0c;运行时数据区&#xff0c;执行引…

TDengine 在 DISTRIBUTECH 分享输配电数据管理实践

2 月 27-29 日&#xff0c;2024 美国国际输配电电网及公共事业展&#xff08;DISTRIBUTECH International 2024&#xff09;在美国-佛罗里达州-奥兰多国家会展中心举办。作为全球领先的年度输配电行业盛会&#xff0c;也是美洲地区首屈一指的专业展览会&#xff0c;该展会的举办…

【和鲸冬令营】通过数据打造爆款社交APP用户行为分析报告

【&#x1f40b;和鲸冬令营】通过数据打造爆款社交APP用户行为分析报告 文章目录 【&#x1f40b;和鲸冬令营】通过数据打造爆款社交APP用户行为分析报告1 业务背景2 数据说明3 数据探索性分析4 用户行为分析4.1 用户属性与行为关系分析4.2 转化行为在不同用户属性群体中的分布…

值类型和引用类型详解(C#)

可能你对值类型和引用类型还不太了解。 值类型和引用类型&#xff0c;是c#比较基础&#xff0c;也必须掌握的知识点&#xff0c;但是也不是那么轻易就能掌握&#xff0c;今天跟着我一起来看看吧。 典型类型 首先我们看看这两种不同的类型有哪些比较典型的代表。 典型值类型…

在Windows 10系统中启用快速启动功能

在Windows 10系统中启用快速启动功能&#xff0c;可以按照以下步骤进行&#xff1a; 方法一&#xff08;通过设置应用&#xff09;&#xff1a; 点击任务栏左下角的“开始”按钮或者按键盘上的Win键打开“开始”菜单。在“开始”菜单中选择“设置”图标&#xff08;齿轮形状&…

3.3日学习打卡----初学Redis(一)

3.3日学习打卡 目录&#xff1a; 3.3日学习打卡NoSQL为什么要用NoSQL什么是NoSQL?NoSQL的四大分类关系型数据库和非关系型数据及其区别NoSQL经典应用 RedisRedis是什么?Linux下安装RedisDocker下安装Redis基本知识 NoSQL 为什么要用NoSQL 单机Mysql的美好年代 在90年代&…

Sqlmap进行http头注入及流量分析

环境准备:构建完善的安全渗透测试环境:推荐工具、资源和下载链接_渗透测试靶机下载-CSDN博客 利用 SQLMap 进行 HTTP 头注入的方式对于 Less-19 注入点的注入 SQLMap 工具我使用kali中自带的 注入准备 先使用bp将Less-19靶场的包抓下来保存到 txt 文件中,输入账号 admin…

Ubuntu23.10禁用Wayland

禁用前 编辑custom.conf文件 sudo vim /etc/gdm3/custom.conf 去掉WaylandEnablefalse前的#号 保存退出 重启系统 生效: 成功转换为X11

【LeetCode题解】2809. 使数组和小于等于 x 的最少时间+2788. 按分隔符拆分字符串+410. 分割数组的最大值

文章目录 [2809. 使数组和小于等于 x 的最少时间](https://leetcode.cn/problems/minimum-time-to-make-array-sum-at-most-x/)思路&#xff1a; [2788. 按分隔符拆分字符串](https://leetcode.cn/problems/split-strings-by-separator/)思路&#xff1a; [410. 分割数组的最大…

Leetcoder Day36| 动态规划part03

343. 整数拆分 给定一个正整数 n&#xff0c;将其拆分为至少两个正整数的和&#xff0c;并使这些整数的乘积最大化。 返回你可以获得的最大乘积。 示例 1: 输入: 2输出: 1解释: 2 1 1, 1 1 1。 示例 2: 输入: 10输出: 36解释: 10 3 3 4, 3 3 4 36。说明: 你可以假设 …

如何提取图片中某个位置颜色的RGB值,RGB十进制值与十六进制的转换

打开本地的画图工具&#xff0c;把图片复制或截图粘进去&#xff0c;用颜色提取器点对应的位置就可以提取了。 获取到的 RGB 值为 (66,133,244) 转化后的值为 #4285F4。 【内容拓展一】&#xff1a;RGB 十进制值与十六进制的转换 当我们从 RGB 十进制值转换为十六进制值时&a…

Yapi部署

【GO开发工程师】Yapi部署 推荐个人主页&#xff1a;席万里的个人空间 文章目录 【GO开发工程师】Yapi部署1、Yapi部署 1、Yapi部署 初始化yapi&#xff1a; git clone https://github.com/Ryan-Miao/docker-yapi.git cd docker-yapi docker-compose upyapi启动失败 1.cd进入…