JAVA基础学习day20--IO流二-缓冲流、字节流
一、缓冲流
1.1、字符流的缓冲区
缓冲区的出现是为了提高IO的读写效率
对应类
BufferedReader
BufferedWriter
缓冲区要结合流才可以使用
在流的基础上对流的功能进行了增强
1.2、BufferedReader、BufferedWriter
public class BufferedWriterextends Writer
| 构造方法摘要 | |
|---|---|
BufferedWriter(Writer out) 创建一个使用默认大小输出缓冲区的缓冲字符输出流。 |
|
BufferedWriter(Writer out,创建一个使用给定大小输出缓冲区的新缓冲字符输出流。 |
|
将文本写入字符输出流,缓冲各个字符,从而提供单个字符、数组和字符串的高效写入。
可以指定缓冲区的大小,或者接受默认的大小。在大多数情况下,默认值就足够大了。
该类提供了 newLine() 方法,它使用平台自己的行分隔符概念,此概念由系统属性 line.separator 定义。并非所有平台都使用新行符 ('\n') 来终止各行。因此调用此方法来终止每个输出行要优于直接写入新行符。
通常 Writer 将其输出立即发送到底层字符或字节流。除非要求提示输出,否则建议用 BufferedWriter 包装所有其 write() 操作可能开销很高的 Writer(如 FileWriters 和 OutputStreamWriters)。例如,
PrintWriter out = new PrintWriter(new BufferedWriter(new FileWriter("foo.out")));
将缓冲 PrintWriter 对文件的输出。如果没有缓冲,则每次调用 print() 方法会导致将字符转换为字节,然后立即写入到文件,而这是极其低效的。
public class BufferedReaderextends Reader
| 构造方法摘要 | |
|---|---|
BufferedReader(Reader in) 创建一个使用默认大小输入缓冲区的缓冲字符输入流。 |
|
BufferedReader(Reader in, 创建一个使用指定大小输入缓冲区的缓冲字符输入流。 |
|
从字符输入流中读取文本,缓冲各个字符,从而实现字符、数组和行的高效读取。
可以指定缓冲区的大小,或者可使用默认的大小。大多数情况下,默认值就足够大了。
通常,Reader 所作的每个读取请求都会导致对底层字符或字节流进行相应的读取请求。因此,建议用 BufferedReader 包装所有其 read() 操作可能开销很高的 Reader(如 FileReader 和 InputStreamReader)。例如,
BufferedReader in
= new BufferedReader(new FileReader("foo.in"));
将缓冲指定文件的输入。如果没有缓冲,则每次调用 read() 或 readLine() 都会导致从文件中读取字节,并将其转换为字符后返回,而这是极其低效的。
通过用合适的 BufferedReader 替代每个 DataInputStream,可以对将 DataInputStream 用于文字输入的程序进行本地化
1.3、示例
在创建缓冲区之前,必须先有流对象
package com.pb.io.demo2; /**
创建缓冲区之间必须,先创建流
使用缓冲区,要flush,刷新
*/
/**
创建缓冲区之间必须,先创建流
使用缓冲区,要flush,刷新
*/
import java.io.*;
class BufferedReaderAndWriterDemo1
{
public static void main(String[] args)
{
File source=new File("d:\\demo.txt");
File objFile=new File("d:\\a.txt");
useBuffered(source,objFile);
}
public static void useBuffered(File source,File objFile){ BufferedReader br=null;
BufferedWriter bw=null; try{
//声明缓冲区对象,并将字符流做为参数传入
br=new BufferedReader(new FileReader(source));
bw=new BufferedWriter(new FileWriter(objFile));
//接收读取的长度
String line=null; //读取内容并放入buf中,判断是不是最后
while((line=br.readLine())!=null){
//写入,有多长写多长
bw.write(line,0,line.length());
bw.newLine(); }
System.out.println("读写完成!");
}catch(IOException e){
System.out.println(e.toString()); }finally{
try{
if(bw!=null)
bw.flush();
bw.close();
}catch(IOException e){
System.out.println(e.toString());
}
try{
if(br!=null)
br.close(); }catch(IOException e){
System.out.println(e.toString());
} }
}
}
二、MyBuffered
2.1、示例
package com.pb.io.demo2; import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException; public class MyBuffered { private FileReader fr; public MyBuffered(FileReader fr) {
this.fr = fr;
} /*
* 可以一次读一行的方法
*/
public String myReadLine() throws IOException {
// 定义一个临时 容器,原BufferedReader封装的是字符数组
// 为了方便,定义一个StringBuilder
StringBuilder sb = new StringBuilder();
int ch = 0; while ((ch = fr.read()) != -1) {
if (ch == '\r') {
continue;
}
if (ch == '\n') {
return sb.toString();
}
// 读一个存一个
sb.append((char)ch);
}
if(sb.length()!=0){
return sb.toString();
}
return null;
}
public void myClose() throws IOException{
if (fr != null)
fr.close();
}
public static void main(String[] args) throws IOException { File file=new File("d:\\demo.txt");
MyBuffered mb=new MyBuffered(new FileReader(file));
String line=null;
while((line=mb.myReadLine())!=null){
System.out.println(line);
}
mb.myClose();
}
}
三、装饰设计模式
3.1、装饰设计模式
装饰设计模式:
当想要对已经有的对象进行功能增强时,
可以定义类,将已经对象传入,基于已经有的功能,并提供加强功能。
那么自定义的该类就称为装饰类
package com.pb.io.demo3;
class Person{
public void eat(){
System.out.println("吃饭");
}
}
class SuperPerson{
private Person p;
public SuperPerson(Person p) {
super();
this.p = p;
}
public void superEat(){
System.out.println("开胃酒");
p.eat();
System.out.println("甜点");
System.out.println("来一根");
}
}
public class PersonTest {
public static void main(String[] args) {
Person p=new Person();
p.eat();
SuperPerson sp=new SuperPerson(p);
sp.superEat();
}
}
3.2、与继承之间的关系
装饰类通常会通过构造方法接收被被装饰的对象,并基于被装饰的对象的功能,提供更强的功能
装饰模式比继承灵活,避免了继承体系臃肿
而且降低了类于类之间的关系
装饰类因为增强已有对象,具备的功能和已经的是相同的,只不过提供了更强的功能。
所以装饰类和被装饰类通常是都属于一个体系中。
四、LineNumberReader
4.1、
| 构造方法摘要 | |
|---|---|
LineNumberReader(Reader in) 使用默认输入缓冲区的大小创建新的行编号 reader。 |
|
LineNumberReader(Reader in, 创建新的行编号 reader,将字符读入给定大小的缓冲区。 |
|
| 方法摘要 | |
|---|---|
int |
getLineNumber()获得当前行号。 |
void |
mark(int readAheadLimit)标记该流中的当前位置。 |
int |
read()读取单个字符。 |
int |
read(char[] cbuf, 将字符读入数组中的某一部分。 |
String |
readLine()读取文本行。 |
void |
reset()将该流重新设置为最新的标记。 |
void |
setLineNumber(int lineNumber)设置当前行号。 |
long |
skip(long n)跳过字符。 |
4.2、示例
package com.pb.io.demo4; import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import java.io.LineNumberReader; public class LineNumberReaderDemo { public static void main(String[] args) {
LineNumberReader lnr=null;
try {
lnr=new LineNumberReader(new FileReader("d:\\demo.txt"));
//设置开始行叼
lnr.setLineNumber(100);
String line=null;
while((line=lnr.readLine())!=null){
//getLineNumber默认从1开始,设置后从设置的行号开始
System.out.println(lnr.getLineNumber()+":"+line);
}
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}finally{
try {
if(lnr!=null)
lnr.close();
} catch (IOException e) {
e.printStackTrace();
}
} } }
4.3、自定义
package com.pb.io.demo4; import java.io.File;
import java.io.FileReader;
import java.io.IOException;
import java.io.Reader; public class MyLineNumberReader { private Reader reader;
private int lineNumber; public MyLineNumberReader(Reader reader) {
this.reader = reader;
} /*
* 读一行
*/
public String myReadLine() throws IOException {
lineNumber++;
StringBuilder sb = new StringBuilder();
int ch=0;
while ((ch = reader.read()) != -1) {
if (ch == '\r')
continue;
if (ch == '\n')
return sb.toString();
sb.append((char) ch);
}
if (sb.length() != 0)
return sb.toString();
return null; } public void myClose() throws IOException{
reader.close();
} public Reader getReader() {
return reader;
} public void setReader(Reader reader) {
this.reader = reader;
} public int getLineNumber() {
return lineNumber;
} public void setLineNumber(int lineNumber) {
this.lineNumber = lineNumber;
} public static void main(String[] args) throws IOException {
File file=new File("d:\\demo.txt");
MyLineNumberReader mln=new MyLineNumberReader(new FileReader(file));
String line=null;
//mln.setLineNumber(100);
while((line=mln.myReadLine())!=null){
System.out.println(mln.getLineNumber()+":"+line);
}
mln.myClose();
} }
五、字节流
5.1、字节流
OutputStream写、InputStream读
FileOutputStream、FileInputStream 字节流输出、输入流
| 构造方法摘要 | |
|---|---|
OutputStream() |
|
| 方法摘要 | |
|---|---|
void |
close()关闭此输出流并释放与此流有关的所有系统资源。 |
void |
flush()刷新此输出流并强制写出所有缓冲的输出字节。 |
void |
write(byte[] b)将 b.length 个字节从指定的 byte 数组写入此输出流。 |
void |
write(byte[] b, 将指定 byte 数组中从偏移量 off 开始的len 个字节写入此输出流。 |
abstract |
write(int b)将指定的字节写入此输出流。 |
| 构造方法摘要 | |
|---|---|
InputStream() |
|
| 方法摘要 | |
|---|---|
int |
available()返回此输入流下一个方法调用可以不受阻塞地从此输入流读取(或跳过)的估计字节数。 |
void |
close()关闭此输入流并释放与该流关联的所有系统资源。 |
void |
mark(int readlimit)在此输入流中标记当前的位置。 |
boolean |
markSupported()测试此输入流是否支持 mark 和 reset 方法。 |
abstract |
read()从输入流中读取数据的下一个字节。 |
int |
read(byte[] b)从输入流中读取一定数量的字节,并将其存储在缓冲区数组 b 中。 |
int |
read(byte[] b, 将输入流中最多 len 个数据字节读入 byte数组。 |
void |
reset()将此流重新定位到最后一次对此输入流调用 mark 方法时的位置。 |
long |
skip(long n)跳过和丢弃此输入流中数据的 n 个字节。 |
5.2、FileInputStream与FileOutputStream
package com.pb.io.demo5; import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException; /**
* 使用字节流复制文件
*
*/
public class FileOutputAndFileInputStreamDemo { public static void main(String[] args) {
File source=new File("d:\\1.jpg");
File objFile=new File("d:\\4.jpg");
InputAndOutput2(source,objFile);
}
public static void InputAndOutput(File source,File objFile){
//声明字节流输入,输出对象
FileInputStream fis=null;
FileOutputStream fos=null;
try {
fis=new FileInputStream(source);
fos=new FileOutputStream(objFile);
//声明缓冲区
byte [] buf=new byte[1024];
int len=0;
while((len=fis.read(buf))!=-1){
fos.write(buf, 0, len);
}
System.out.println("=======读写完成========");
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}finally{
try {
if(fos!=null)
fos.close();
} catch (IOException e) {
e.printStackTrace();
}
try {
if (fis!=null)
fis.close();
} catch (IOException e) {
e.printStackTrace();
}
} }
/*
* 不建议使用
*/
public static void InputAndOutput2(File source,File objFile){
//声明字节流输入,输出对象
FileInputStream fis=null;
FileOutputStream fos=null;
try {
fis=new FileInputStream(source);
fos=new FileOutputStream(objFile);
//声明大小刚刚缓冲区
byte [] buf=new byte[fis.available()]; //不建议使用available fis.read(buf);
fos.write(buf,0,buf.length); System.out.println("=======读写完成========");
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}finally{
try {
if(fos!=null)
fos.close();
} catch (IOException e) {
e.printStackTrace();
}
try {
if (fis!=null)
fis.close();
} catch (IOException e) {
e.printStackTrace();
}
} }
}
六、字节流缓冲区
6.1、示例复制.mp3
package com.pb.io.demo5; import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException; public class BufferedStreamDemo { public static void main(String[] args) {
File file1=new File("d:\\gm.mp3");
File file2=new File("d:\\mg.mp3");
copy1(file1,file2);
}
public static void copy1(File file1,File file2){
//声明字节流缓冲区对象
BufferedOutputStream bos=null;
BufferedInputStream bis=null;
try {
bis=new BufferedInputStream(new FileInputStream(file1));
bos=new BufferedOutputStream(new FileOutputStream(file2)); //声明缓冲区大小
int by=0;
while((by=bis.read())!=-1){
bos.write(by);
} } catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}finally{
try {
if(bos!=null)
bos.close();
} catch (IOException e) {
e.printStackTrace();
}
try {
if(bis!=null)
bis.close();
} catch (IOException e) {
e.printStackTrace();
}
} } }
6.2、自定义
package com.pb.io.demo5; import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream; public class MyBufferedStream { private InputStream is;
private byte[] buf = new byte[1024]; private int pos = 0;
private int count = 0; public MyBufferedStream(InputStream is) {
this.is = is;
} /*
* 一次读一个字节,从缓冲区的(字节数组)获取
*/
public int myRead() throws IOException {
// 通过in对象读取硬盘上的数据,并存在buf中
if (count == 0) {
count = is.read(buf);
if (count < 0) {
return -1;
}
pos = 0;
byte b = buf[pos];
count--;
pos++;
return b & 0xff;
} else if (count > 0) {
byte b = buf[pos];
count--;
pos++;
return b & 0xff;
}
return -1; } public void myClose() throws IOException {
is.close();
} public static void main(String[] args) {
File file1 = new File("d:\\gm.mp3");
File file2 = new File("d:\\kk.mp3");
copy1(file1, file2);
} public static void copy1(File file1, File file2) {
// 声明字节流缓冲区对象
BufferedOutputStream bos = null;
MyBufferedStream mbs = null;
try {
mbs = new MyBufferedStream(new FileInputStream(file1));
bos = new BufferedOutputStream(new FileOutputStream(file2)); // 声明缓冲区大小
int by = 0;
while ((by = mbs.myRead()) != -1) {
bos.write(by);
} } catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
if (bos != null)
bos.close();
} catch (IOException e) {
e.printStackTrace();
}
try {
mbs.myClose();
} catch (IOException e) {
e.printStackTrace();
}
} } }
七、转换
7.1、字节流转换字符流
InputStreamReader 是字节流通向字符流的桥梁:它使用指定的 charset 读取字节并将其解码为字符。它使用的字符集可以由名称指定或显式给定,或者可以接受平台默认的字符集。
BufferedReader in
= new BufferedReader(new InputStreamReader(System.in));
OutputStreamWriter 是字符流通向字节流的桥梁:可使用指定的 charset 将要写入流中的字符编码成字节。它使用的字符集可以由名称指定或显式给定,否则将接受平台默认的字符集。
Writer out
= new BufferedWriter(new OutputStreamWriter(System.out));
7.2、示例
接收键盘输入,使用转换流实现
package com.pb.io.demo6; import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
/**
* 接收键盘输入,并将内容写入文件
*
*/
public class Demo { public static void main(String[] args) { BufferedReader br=null;
BufferedWriter bw=null; try {
//声明输入流,使用转换流实现
br=new BufferedReader(new InputStreamReader(System.in)); //声明输出流,使用转换流实现
bw=new BufferedWriter(new OutputStreamWriter(new FileOutputStream("d:\\t.txt"))); //接收键盘录入
String str=null;
System.out.println("请输入要写入文件的内容,输入over结束!");
//输入over结束
while(!(str=br.readLine()).equals("over")){
bw.write(str);
bw.newLine();
} } catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}finally{
try {
if(bw!=null)
bw.close();
} catch (IOException e) {
e.printStackTrace();
} try {
if(br!=null)
br.close();
} catch (IOException e) {
e.printStackTrace();
}
} } }
八、流操作的规律
1、明确源和目的
源:输入流。InputStream,Reader
目的:输出流 .OutputStream,Writer
2、操作的数据是否为纯文本
是:字符流。Reader、Writer
不是:字节流。InputStream、OutputStream
3、当体系明确后,在明确要使用哪个具体对象。
通过设备来进行区别。
源设备:内存、硬盘、键盘
目的设备:内存、硬盘、控制台
九、重定向IO
9.1、示例
//设置输入流的
System.setIn(new FileInputStream("d:\\demo.txt"));
//设置的输出流的方向
System.setOut(new PrintStream("d:\\zz.txt"));
十、系统信息
10.1、示例
package com.pb.io.demo6;
import java.util.Properties;
public class Demo1 {
public static void main(String[] args) {
Properties prop=System.getProperties();
prop.list(System.out); //可以设置为输出流对象new PrintWriter(文件)
}
}
-- listing properties --
java.runtime.name=Java(TM) SE Runtime Environment
sun.boot.library.path=C:\Java\jre1.8.0_60\bin
java.vm.version=25.60-b23
java.vm.vendor=Oracle Corporation
java.vendor.url=http://java.oracle.com/
path.separator=;
java.vm.name=Java HotSpot(TM) 64-Bit Server VM
file.encoding.pkg=sun.io
user.script=
user.country=CN
sun.java.launcher=SUN_STANDARD
sun.os.patch.level=
java.vm.specification.name=Java Virtual Machine Specification
user.dir=F:\work\IODemo
java.runtime.version=1.8.0_60-b27
java.awt.graphicsenv=sun.awt.Win32GraphicsEnvironment
java.endorsed.dirs=C:\Java\jre1.8.0_60\lib\endorsed
os.arch=amd64
java.io.tmpdir=C:\Users\ADMINI~1\AppData\Local\Temp\
line.separator= java.vm.specification.vendor=Oracle Corporation
user.variant=
os.name=Windows 7
sun.jnu.encoding=GBK
java.library.path=C:\Java\jre1.8.0_60\bin;C:\Windows\Su...
java.specification.name=Java Platform API Specification
java.class.version=52.0
sun.management.compiler=HotSpot 64-Bit Tiered Compilers
os.version=6.1
user.home=C:\Users\Administrator
user.timezone=
java.awt.printerjob=sun.awt.windows.WPrinterJob
file.encoding=UTF-8
java.specification.version=1.8
user.name=Administrator
java.class.path=F:\work\IODemo\bin
java.vm.specification.version=1.8
sun.arch.data.model=64
java.home=C:\Java\jre1.8.0_60
sun.java.command=com.pb.io.demo6.Demo1
java.specification.vendor=Oracle Corporation
user.language=zh
awt.toolkit=sun.awt.windows.WToolkit
java.vm.info=mixed mode
java.version=1.8.0_60
java.ext.dirs=C:\Java\jre1.8.0_60\lib\ext;C:\Window...
sun.boot.class.path=C:\Java\jre1.8.0_60\lib\resources.jar...
java.vendor=Oracle Corporation
file.separator=\
java.vendor.url.bug=http://bugreport.sun.com/bugreport/
sun.cpu.endian=little
sun.io.unicode.encoding=UnicodeLittle
sun.desktop=windows
sun.cpu.isalist=amd64
JAVA基础学习day20--IO流二-缓冲流、字节流的更多相关文章
- 【java基础学习】IO流
IO流 字节流InputStream和OutputStream 字符流Writer和Reader 装饰模式
- java基础学习总结——GUI编程(二)
一.事件监听
- java基础学习总结——GUI编程(二) 未学习
一.事件监听
- JAVA基础学习之路(二)方法定义,重载,递归
一,方法的定义: package test; public class test1 { public static void main(String args[]) { int result = ad ...
- 转载-java基础学习汇总
共2页: 1 2 下一页 Java制作证书的工具keytool用法总结 孤傲苍狼 2014-06-24 11:03 阅读:25751 评论:3 Java基础学习总结——Java对象的序列化和 ...
- java基础(24):转换流、缓冲流
1. 转换流 在学习字符流(FileReader.FileWriter)的时候,其中说如果需要指定编码和缓冲区大小时,可以在字节流的基础上,构造一个InputStreamReader或者OutputS ...
- java的 IO流之缓冲流(转载)
java缓冲流本身不具IO功能,只是在别的流上加上缓冲提高效率,像是为别的流装上一种包装.当对文件或其他目标频繁读写或操作效率低,效能差.这时使用缓冲流能够更高效的读写信息.因为缓冲流先将数据缓存起来 ...
- java:IO流(处理流(缓冲流,转换流,数据流),对象的序列化,Properties)
字节缓冲流:(BufferedInputStream,BufferedOutStream) *按照流的功能来分:节点流和处理流 *节点流可以直接操作数据源: *InputStream *--FileI ...
- java - >IO流_缓冲流(高效流)
缓冲流(高效流) 在我们学习字节流与字符流的时候,大家都进行过读取文件中数据的操作,读取数据量大的文件时,读取的速度会很慢,很影响我们程序的效率,那么,我想提高速度,怎么办? Java中提高了一套缓冲 ...
随机推荐
- 关于 iOS 的一些学习资料
iOS.Book.Effective Objective-C 2.0 1. 中文翻译版 (更新中) https://github.com/HagerHu/effective-objective-c-2 ...
- Java知多少(完结)
系列文章: Java知多少(上) Java知多少(中) Java知多少(下)
- Unity 摄像机Clear Flags和Culling Mask属性用途详解
原文地址:http://blog.csdn.net/tanmengwen/article/details/8798231 1.简述两个属性 1.1 Clear Flags 清除标记 每个相机在渲染时会 ...
- ruby -- 基础学习(五)empty、nil、blank三者之间的区别
这三个方法在ROR中经常用到,都是用来判断是否为空的. 区别是: ruby的方法:.nil?..empty? rails的方法 :.blank? 用法的区别: .nil? : 判断对象是否存 ...
- ruby -- 进阶学习(十二)fragment cache
基于rails4.0环境 Rails 页面缓存的方法很多,最近弱弱地尝试了fragment cache,用法还算简单~@_@|| 首先,查看config/environment/production. ...
- 理解并使用.NET 4.5中的HttpClient
HttpClient介绍 HttpClient是.NET4.5引入的一个HTTP客户端库,其命名空间为System.Net.Http..NET 4.5之前我们可能使用WebClient和HttpWeb ...
- UBUNTU上的GIT SERVER
Git是一个开源的版本控制系统,由Linus Torvalds主导,用于支持Linux内核开发.每一个Git工作目录,都是一个完整的代码库,包含所有的提交历史.有能力跟踪所有的代码版本,而不会去依赖于 ...
- Tracert 转
路由跟踪在线Tracert Tracert(跟踪路由)是路由跟踪实用程序,用于确定 IP 数据报访问目标所采取的路径.Tracert 命令用 IP 生存时间 (TTL) 字段和 ICMP 错误消息来确 ...
- SharePoint 2013 搜索爬网功能
最近在政府部门介绍SharePoint 2013 新功能,我也准备了很多,比如SharePoint 2013的Search.以后有机会谈谈Office Web App,Workflow等. Share ...
- [linux]scp指令
实例1:从远处复制文件到本地目录 $scp root@10.6.159.147:/opt/soft/demo.tar /opt/soft/ 说明: 从10.6.159.147机器上的/opt/soft ...