0%

Java——IO系统1

File类

  1. 含义:File指代的并不是文件,它既能代表一个特定文件的名称,又能代表一个目录下的一组文件的名称,它同时包含了许多操作真实文件的手柄。
  2. list()和listFiles():调用File对象的list方法可以返回当前文件(夹)下的所有文件的文件名,而listFiles方法则返回当前文件(夹)下的所有文件的文件对象
  3. FilenameFilter:当使用list或者是listFiles方法时,可以配合实现了FilenameFilter的类对象来对返回的文件进行过滤,这个接口拥有一个accept方法,可以将过滤的规则逻辑写在该方法中,下面是一个例子:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
package com.muchlab.io;

import java.io.File;
import java.io.FilenameFilter;
import java.util.regex.Pattern;

public class DirList {
public static void main(String[] args) {
//File代表的并不是文件,而是一个特定文件的名称,又能代表一个目录下的一组文件的名称
final File file = new File("D:/");
String[] list;
//使用list方法可以让文件对象返回一个字符数组,该字符数组里存放着File文件的名称列表
list = file.list();
//往list中传入过滤器可以使它回调filter中的accept方法(策略模式),即filter完善了list提供服务时所需要的算法
final String[] asps = file.list(new DirFilter("Java"));
for (String asp : asps) {
System.out.println("asp = " + asp);
}
// for (String s : list) {
// System.out.println("s = " + s);
// }
}
}
//DirFilter是一个过滤器,创建这个类的目的是把accept方法提供给list使用
class DirFilter implements FilenameFilter {
private Pattern pattern;
public DirFilter(String regex) {
pattern = Pattern.compile(regex);
}

/**
*
* @param dir 文件对象
* @param name 文件名
* @return
*/
@Override
public boolean accept(File dir, String name) {
//使用正则表达式过滤文件名
return pattern.matcher(name).matches();
}
}

使用匿名内部类过滤

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
package com.muchlab.io;

import java.io.File;
import java.io.FilenameFilter;
import java.util.regex.Pattern;

public class DirList2 {
public static void main(String[] args) {
final File file = new File("D:/");
final String[] list = file.list(new FilenameFilter() {
private Pattern pattern = Pattern.compile("Java");

@Override
public boolean accept(File dir, String name) {
return pattern.matcher(name).matches();
}
});
for (String s : list) {
System.out.println("s = " + s);
}
}
}

目录实用工具

  • 通过递归来输出目录下所有文件的文件名,这里的TreeInfo相当于Directory中的目录树信息,第一个TreeInfo将会存储所有文件的文件信息。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
package com.muchlab.io;

import net.mindview.util.PPrint;

import java.io.File;
import java.io.FilenameFilter;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Spliterator;
import java.util.function.Consumer;
import java.util.regex.Pattern;

public class Directory {
public static File[] local(File dir, final String regex){
return dir.listFiles(new FilenameFilter() {
private Pattern pattern = Pattern.compile(regex);
@Override
public boolean accept(File dir, String name) {
return pattern.matcher(new File(name).getName()).matches();
}
});
}
public static File[] local(String path, final String regex){
return local(new File(path), regex);
}
public static class TreeInfo implements Iterable<File>{
//文件列表
public List<File> files = new ArrayList<>();
//目录列表
public List<File> dirs = new ArrayList<>();

@Override
public Iterator<File> iterator() {
return files.iterator();
}

@Override
public void forEach(Consumer<? super File> action) {

}
void addAll(TreeInfo other){
files.addAll(other.files);
dirs.addAll(other.dirs);
}

@Override
public String toString() {
return "dirs: "+ PPrint.pformat(dirs)+"\n\nfiles: "+PPrint.pformat(files);
}

public static TreeInfo walk(String start, String regex){
return recurseDirs(new File(start), regex);
}
public static TreeInfo walk(File start, String regex){
return recurseDirs(start, regex);
}
public static TreeInfo walk(File start){
return recurseDirs(start, ".*");
}

public static TreeInfo walk(String start){
return recurseDirs(new File(start), ".*");
}

/**
* 判断当前文件是否是目录,若是目录,就将当前的目录列表添加到目录树中,然后递归创建新的目录树再进行迭代
* 若不是目录就是普通文件,直接将其添加到当前目录树的文件列表中
* @param startDir 开始的目录
* @param regex 正则表达式
* @return 返回目录树,递归后里面存储的是开始目录中所有的文件
*/
private static TreeInfo recurseDirs(File startDir, String regex) {
TreeInfo result = new TreeInfo();
for (File file : startDir.listFiles()) {
if (file.isDirectory()){
result.dirs.add(file);
result.addAll(recurseDirs(file, regex));
}
if (file.getName().matches(regex))
result.files.add(file);
}
return result;
}

public static void main(String[] args) {
// if (args.length == 0)
// System.out.println(walk("."));
// else
// for (String arg : args) {
// System.out.println(walk(arg));
// }
System.out.println(walk(new File("D://CloudMusic")));
}
}
}

使用策略模式来输出目录

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
package com.muchlab.io;

import java.io.File;
import java.io.IOException;

public class ProcessFiles {
public interface Strategy{
void process(File file);
}
private Strategy strategy;
private String ext;
public ProcessFiles(Strategy strategy, String ext){
this.strategy = strategy;
this.ext = ext;
}
public void start(String[] args){
try {
if (args.length == 0){
processDirectoryTree(new File("."));
}
else {
for (String arg : args) {
File fileArg = new File(arg);
if (fileArg.isDirectory())
processDirectoryTree(fileArg);
else {
if (!arg.endsWith("."+ext))
arg += "."+ext;
strategy.process(new File(arg).getCanonicalFile());
}
}
}
} catch (IOException e) {
e.printStackTrace();
}
}

private void processDirectoryTree(File root) throws IOException {
for (File file : Directory.TreeInfo.walk(root.getAbsolutePath(), ".*\\." + ext)) {
strategy.process(file.getCanonicalFile());
}
}

public static void main(String[] args) {
//这里的Strategy是实现策略模式的一个实例,ProcessFile通过Strategy某种策略来对某些对象执行策略规定的操作
new ProcessFiles(new ProcessFiles.Strategy(){
//搜寻特定的扩展名,然后对搜寻到的文件执行特定的操作
@Override
public void process(File file) {
System.out.println(file);
}
}, "class").start(args);
}
}

File的一些常用方法

  1. isDirectory():判断当前文件是否为目录

  2. isFile():判断当前文件是否为普通文件

  3. exists():判断当前文件是否存在

  4. mkdir():创建目录

  5. mkdirs():创建目录,它可以创建任意复杂的目录路径

  6. delete():删除目录

  7. canRead()/canWrite():判断当前文件是否可读可写

  8. length():文件的长度

    。。。

  • File有很多方法,可以去JDK文档中查看

输入和输出

  • 通常I/O类库经常使用流这个抽象概念,他代表了任何有能力产出数据的数据源对象或有能力接收数据的接收端对象。
  • 任何自Inputstream或Reader派生而来的类都含有名为read()的基本方法,用于读取单个字节或字节数组;而任何自Outputstream或Writer派生而来的类都含有名为writer()的基本方法,用于写单个字节或字节数组。而我们通常不会使用这些方法,它们一般都是其他JDK类去使用的。

InputStream类型

  • InputStream的作用是用来表示那些从不同数据源产生输入的类,这些数据源包括:
    1. 字节数组
    2. String对象
    3. 文件
    4. ”管道“
    5. 其他
  • 每一种数据源都有相对应的InputStream子类,下面是各种InputStream类型
类 功能 如何使用
ByteArrayInputStream 允许将内存的缓存区当作InputStream使用 缓存区,字节将从中取出
StringBufferInputStream 将String转换成InputStream 字符串,底层实际上是使用StringBuffer
FileInputStream 用于从文件中读取信息 字符串,表示文件名、文件或FileDescriptor对象
PipedInputStream 产生用于写入相关PipedOutputStream的数据,实现管道化 作为多线程中的数据源
SequenceInputStream 将多个InputStream对象转换成单一InputStream
FilterInputStream 抽象类,作为”装饰器“的接口。为其他的InputStream类提供有用的功能

OutputStream类型

类 功能 如何使用
ByteArrayOutputStream 在内存中创建缓存区 缓冲区初始化,用于指定数据的目的地
FileOutputStream 用于将信息写入文件 字符串,指定数据的目的地
PipedOutputStream 任何写入其中的信息都会自动作为相关PipedOutputStream的输出 指定用于多线程数据的目的地
FilterOutputStream 抽象类,作为”装饰器“的接口。为其他的OutputStream类提供有用的功能

Reader和Writer

  • 设计Reader和Writer主要是为了国际化,老的I/O系统继承层次结果仅支持8位字节流,并不能很好地处理16位地Unicode字符;而添加Reader和Writer的目的就是让I/O可以处理16位的Unicode字符。所以,尽量地使用Reader和Writer。
  • 每个旧的I/O流都会有新的I/O流类型
旧的Java1.0 新的Java1.1
InputStream Reader
OutputStream Writer
FileInputStream FileInputStreamWriter
FileOutputStream FileOutputStreamWriter
StringBufferInputStream StringReader
StringBufferOutputStream StringWriter
ByteArrayInputStream CharArrayReader
ByteArrayOutputStream CharArrayWriter
PipedInputStream PipedReader
PipedOutputStream PipedWriter

独立的类:RandomAccessFile

  • RandomAccessFile是用来操作由大小已知的记录组成的文件,它实现了DataInput和DataOutput接口,它跟InputStream和OutputStream没有任何关系,这是因为该类有别于其他I/O类本质不同的行为,使用它可以中一个文件内向前或向后移动。
  • getFilePointer()用于查找当前所处的文件位置,seek()用于在文件内移至新的位置,length()用于判断文件的最大尺寸,另外,构造器还需要第二个参数,它表示的是”随机读“(r)还是”既读又写“(rw),它并不支持只写文件。

输入示例

缓冲输入文件

  • 使用BufferedReader可以对文件进行缓冲,它需要一个Reader对象,这里使用一个FileReader,用来输出文件的内容。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
package com.muchlab.io;

import java.io.*;

public class BufferedInputFile {
public static String read(String fileName) throws IOException {
//将文件进行缓存,可以提高速度
BufferedReader in = new BufferedReader(new FileReader(fileName));

String s;
StringBuilder sb = new StringBuilder();
//该对象每读取文件中的一行,就将行数据添加到StringBuilder对象中
while((s = in.readLine())!=null){
sb.append(s + "\n");
}
in.close();
return sb.toString();
}

public static void main(String[] args) throws IOException {
System.out.println(read("D:\\Java\\JavaBasicCode\\IO\\src\\com\\muchlab\\io\\BufferedInputFile.java"));
}
}

从内存中输入

  • read()是以int形式返回下一个字节,所以必须把它转换成char才能正确打印
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
package com.muchlab.io;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.StringReader;

public class MemoryInput {
public static void main(String[] args) throws IOException {
//从BufferedInputFile读取出来的字符串被用来创建一个StringReader对象
StringReader reader = new StringReader(
BufferedInputFile.read("D:\\Java\\JavaBasicCode\\IO\\src\\com\\muchlab\\io\\MemoryInput.java"));
int c;
//该对象每读取一个字符,就把它输出到控制台
while((c = reader.read())!=-1){
System.out.print((char)c);
}
}
}

格式化内存输入

  • 必须把字符串转换成ByteArrayInputStream可接受的字节数组,然后使用一个InputStream初始化DataInputStream
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
package com.muchlab.io;

import java.io.ByteArrayInputStream;
import java.io.DataInputStream;
import java.io.IOException;

public class FormattedMemoryInput {
public static void main(String[] args) {
try {
//如果是面向字节的I/O类。则我们必须使用InputStream,而不是Reader,在这里可以使用DataInputStream
final DataInputStream reader = new DataInputStream(
//ByteArrayInputStream需要一个字节数组
new ByteArrayInputStream(
BufferedInputFile.read("D:\\Java\\JavaBasicCode\\IO\\src\\com\\muchlab\\io\\FormattedMemoryInput.java").getBytes()));
//返回的字节并不能来检测输入是否结束
while(true)
System.out.print((char)reader.readByte());
} catch (IOException e) {
System.err.println("End of stream");
}
}
}
  • 若想判断输入是否结束,可以使用DataInputSream的available方法来进行检测,这里使用FileInputStream来指定文件。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
package com.muchlab.io;

import java.io.*;

public class TestEOF {
public static void main(String[] args) throws IOException {
DataInputStream in = new DataInputStream(
new BufferedInputStream(
new FileInputStream("D:\\Java\\JavaBasicCode\\IO\\src\\com\\muchlab\\io\\TestEOF.java")));
//使用available来查看还有多少可供存取的字符
while(in.available()!=0){
System.out.print((char) in.readByte());
}
}
}

输出示例

基本的文件输出

  • FileWriter对象可以向文件写入数据,先创建一个FileWriter对象指定文件,然后使用BufferedWriter将其包装用以缓存输出,为了提供格式化机制,它继续被包装成了PrintWriter对象,使用该对象就可以进行格式化的输出。也可以使用快捷的方式避免重复的包装PrintWriter out = new PrintWriter(file);。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
package com.muchlab.io;

import java.io.*;

public class BasicFileOutput {
static String file = "BasicFileOutput.out";
public static void main(String[] args) throws IOException {
BufferedReader in = new BufferedReader(
new StringReader(BufferedInputFile.read("D:\\Java\\JavaBasicCode\\IO\\src\\com\\muchlab\\io\\BasicFileOutput.java")));
PrintWriter out = new PrintWriter(new BufferedWriter(new FileWriter(file)));
int lineCount = 1;
String s;
//这里仍需使用Reader来判断是否读取完毕
while((s = in.readLine())!=null){
out.println(lineCount++ +": "+s);
}
out.close();
System.out.println(BufferedInputFile.read(file));
}
}

存储和恢复数据

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
package com.muchlab.io;

import javax.xml.crypto.Data;
import java.io.*;

public class StoringAndRecoveringData {
public static void main(String[] args) throws IOException {
//使用OutputStream来写入数据
DataOutputStream out = new DataOutputStream(
new BufferedOutputStream(
new FileOutputStream("Data.txt")));
//进行写入
out.writeDouble(3.14);
out.writeUTF("That is pi");
out.close();
//使用InputStream来恢复数据
DataInputStream in = new DataInputStream(
new BufferedInputStream(
new FileInputStream("Data.txt")));
//进行读取
System.out.println(in.readDouble());
System.out.println(in.readUTF());
}
}

RandomAccessFile示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
package com.muchlab.io;

import java.io.IOException;
import java.io.RandomAccessFile;

public class UsingRandomAccessFile {
static String file = "rtest.dat";
static void display() throws IOException {
RandomAccessFile rf = new RandomAccessFile(file, "r");
for (int i = 0; i < 7; i++) {
System.out.println(rf.readDouble());
}
System.out.println(rf.readUTF());
rf.close();
}

public static void main(String[] args) throws IOException {
RandomAccessFile rf = new RandomAccessFile(file, "rw");
for (int i = 0; i < 7; i++) {
rf.writeDouble(i*1.414);
}
//读写字符串使用UTF的格式更为可靠,因为UTF是一种多字节格式,其编码长度会根据实际使用的字符集会有所变化
rf.writeUTF("The end of the file");
rf.close();
display();
rf = new RandomAccessFile(file, "rw");
//这里的double类型是8字节长,使用seek(5*8)可以把光标移动到第五个double值下方,然后覆盖重写
rf.seek(5*8);
rf.writeDouble(47.0001);
rf.close();
display();
}
}
  • 管道流:PipedInputStream、PipedOutputStream、PipedReader、PipedWriter用于任务之间的通信,到后面讲到多线程的时候再提及。

自定义文件读写实用工具

  • 为了简化每一次读写文件的编码,我们可以自定义一个工具类来进行文件读写。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
package com.muchlab.io;

import javax.xml.soap.Text;
import java.io.*;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.TreeSet;

public class TextFile extends ArrayList<String> {
/**
* 读
*/
public static String read(String fileName) {
final StringBuilder builder = new StringBuilder();
try {
BufferedReader in = new BufferedReader(new FileReader(fileName));
try{
String s;
while ((s = in.readLine())!=null){
builder.append(s + "\n");
}
}catch (IOException e) {
e.printStackTrace();
}finally {
in.close();
}
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e){
e.printStackTrace();
}
return builder.toString();
}
/**
* 写
*/
public static void write(String fileName, String text){
try {
// PrintWriter out = new PrintWriter(new File(fileName).getAbsoluteFile());
PrintWriter out = new PrintWriter(new FileOutputStream(fileName));
try{
out.println(text);
}finally {
out.close();
}
} catch (FileNotFoundException e) {
e.printStackTrace();
}
}
public TextFile(String fileName, String splitter){
super(Arrays.asList(read(fileName).split(splitter)));
if (get(0).equals(""))
remove(0);
}
public TextFile(String fileName){
this(fileName, "\n");
}
public void write(String fileName){
try {
PrintWriter out = new PrintWriter(new FileOutputStream(fileName));
try{
for (String s : this) {
out.println(s);
}
}finally {
out.close();
}
} catch (FileNotFoundException e) {
e.printStackTrace();
}
}

public static void main(String[] args) {
String file = read("D:\\Java\\JavaBasicCode\\IO\\src\\com\\muchlab\\io\\TextFile.java");
write("text.txt", file);
//在创建对象的时候,会把文件中的内容填充到this的字符串列表上,使用"\n"分割
final TextFile textFile = new TextFile("text.txt");
//把列表中的字符串写到text2.txt中
textFile.write("text2.txt");
TreeSet<String> words = new TreeSet<>(new TextFile("D:\\Java\\JavaBasicCode\\IO\\src\\com\\muchlab\\io\\TextFile.java", "\\W+"));
System.out.println(words.headSet("a"));
}
}

读二进制文件

  • 与TextFile类似,它也是简化读取二进制文件的工具类
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
package com.muchlab.io;

import java.io.*;

public class BinaryFile {
/**
* 读
*/
public static byte[] read(File bFile) {
byte[] data = null;
try {
BufferedInputStream bf = new BufferedInputStream(new FileInputStream(bFile));
try{
data = new byte[bf.available()];
bf.read(data);
}finally {
bf.close();
}
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
return data;
}
public static byte[] read(String fileName){
return read(new File(fileName).getAbsoluteFile());
}

public static void main(String[] args) throws IOException {
//对二进制图片进行读取
final byte[] bytes = BinaryFile.read("C:\\Users\\14276\\OneDrive\\图片\\本机照片\\1.jpg");
final ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(bytes);
final DataInputStream in = new DataInputStream(byteArrayInputStream);
while(in.available()!=0){
System.out.print((char) in.readByte());
}
}
}

标准I/O

  • 程序所有的输入都可以来自于标准输入,它的所有输出也可以发送到标准输出,以及所有的错误信息都可以发送到标准错误,它的意义在于可以让我们很容易地将程序串联起来,即一个程序的标准输出可以成为另一个程序的标准输入。

从标准输入中读取

  • Java提供了System.in、System.out、System.err三个标准IO,其中System.out和System.err的底层是PrintStream对象,而System.in的底层是InputStream对象,所以,我们可以立即地使用out和err,但是在读取System.in之前必须对其进行包装。通常我们使用的是一行一行的读取,所以可以使用BufferedReader的readLine方法进行读取输入,这时就可以使用InputStreamReader把System.in转换成Reader。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
package com.muchlab.io;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;

public class Echo {
public static void main(String[] args) throws IOException {
BufferedReader stdin = new BufferedReader(new InputStreamReader(System.in));
String s;
while ((s = stdin.readLine())!=null && s.length()!=0)
System.out.println(s);
}
}

将System.out转换成PrintWriter

1
2
3
4
5
6
public class ChangeSystemOut {
public static void main(String[] args) {
final PrintWriter out = new PrintWriter(System.out, true);
out.println("Hello World");
}
}
-------------本文结束感谢您的阅读-------------