File类
目录列表器
查询一个文件夹中的文件数据,使用File类的list()
函数获得该目录下的文件数据,返回String数组。包括普通的文件名称和文件夹。list()
接收一个FilenameFilter
的实现类实例来过滤获得相应的文件。
1
2
3public interface FilenameFilter{
boolean accept(File dir, String name);
}
如何使用该过滤器?当调用list函数的时候会调用该接口的accept()函数来判断是否保留该文件数据返回。每个文件都会调用该函数一次,自己在accept()函数里面设置规则,就可以获得相应的文件数据。
目录的检查及创建
File代表的是一个文件或目录,文件的相关信息都可以使用File中的函数来获得。下面是常用的函数。
函数名 | 作用 |
---|---|
getAbsolutePath() | 获得绝对路径,不会处理.和.. |
getCanonicalPath() | 获得标准的绝对路径。将.和..解析之后返回路径 |
canRead() | |
canWrite() | |
getName() | |
getParent() | 解析File文件中路径的父路径。只是对构造文件对象时输入的path字符串 |
getPath() | 获得文件路径,构造时传进来的文件路径 |
length() | 返回文件的字节大小,long类型 |
lastModified() | 上一次修改时间戳,long类型 |
isFile() | |
isDirectory() | |
exists() | |
createNewFile | |
renameTo(File f) | 重命名 |
delete() | |
mkdirs() | 创建文件/目录(可以多级创建) |
mkdir() | 仅一级创建。路径的上一个文件夹必须存在才能创建。 |
getParent()
、getName()
、getPath()
、getAbsolutePath()
、getCanonicalPath()
的区别
1 | File f = new File(".\\pom.xml"); |
ouput:
1 | . |
getParent()
获得的只是路径中的上一层,构造对象f时传进去的是.\\pom.xml
。是对该字符串解析。如果直接使用pom.xml
构造f对象,该函数返回的是空值。
mkdirs()
和mkdir()
创建”E:\test\test1\test2\test3”嵌套目录,使用mkdirs()可以顺利创建。但是使用mkdir()如果文件夹组”E:\test\test1\test2”存在,成功创建。否则创建失败。
输入和输出
添加属性和有用的接口
Reader
和Writer
自我独立类:RandomAccessFile
I/O流的典型使用方式
- 缓冲输入文件。使用缓冲来提高文件的输入
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18public static String
read(String filename){
BufferReader in = new BufferReader(
new FileReader(filename)
);
String s;
StringBuilder sb = new StringBuilder();
while(s = in.readLine() != null){
sb.append(s+"\n");
// 使用如下代码是否更好?
/*
sb.append(s);
sb.append("\n");
*/
}
in.close();
return sb.toString();
} - 从内存读取文件。将文件加载进内存中,然后再从内存中读取数据。
1
2
3
4
5
6
7
8StringReader in = new StringReader(
BufferedInputFile.read(filename)
);
int c;
// 从内存缓冲区读取文件的字节数据。
while((c = in.read()) != -1){
System.out.print((char)c);
} - 格式化的内存输入。读取格式化的数据,不需要自己进行格式化处理。
1
2
3
4
5
6
7
8DataInputSteam in = new DataInputStream(
new ByteArrayInputStream(
BufferedInputFile.read(filename).getBytes()
)
);
while(in.available() != 0){
System.out.print((char)in.readBytes());
} - 文件输出
1
2
3
4
5PrintWriter out = new PrintWriter(
new BufferedWriter(new FileWriter(filename))
);
// 将内容输出到文件中
out.println(str); - 最简单的创建文件输出
1
2String filename = "Hello.java";
PrintWriter out = new PrintWriter(filename); - 存储和恢复数据。目标是写进去的数据能够正确的读出来。需要统一的写和读,编码统一才可以读出来。使用DataInputStream和DataOutputSteam来进行文件的读写。
文件数据的读写。
写 | 读 |
---|---|
writeUTF() | readUTF() |
writeDouble() | readDouble() |
更多的类型数据读写可以参考JDK.
- 读写随机文件。
使用RandomAccessFile 类来随机的读写文件。其它的输入输出流就只是从头开始读写,随机读写流可以使用seek()来读写文件。这是一个类似C中指针一样的东西,可以在特定的地方读写文件。但是这需要对文件的格式很熟悉才能定位到正确的地方。
标准I/O
标准的I/O中含有标准输入、标准输出、标准异常。在java中对应System.in
、System.out
、System.err
。out和err已经被包装成PrintStream对象。
从标准输入中读取
1 | BufferedReader stdin = new BufferedRead( |
可以使用Scanner来读出特定的类型。
System.out转换成PrintWriter
1 | // true 自动清空 |
IO重定向。
标准IO一般默认是从控制台输入和输出的,但是可以通过重定向来改变标准IO的方向。
1
2
3System.setIn(InputStream)
System.setOut(PrintStream)
System.setErr(PrintStream)
经过重定向之后就可以使用System.out、System.in、System.err。
进程控制
在java中运行其它的程序。使用Process类来运行程序。
1
2
3
4
5
6// comnands 是运行命令的String[]
Process process = new ProcessBuilder(commands).start();
// 获得运行的输出结果
BufferedRead result = new BufferedReader(new InputStreamReader(process.getInputStream()));
// 获得程序运行的错误
BufferedReader errors = new BufferedReader(new InputStreamReader(process.getErrorStream()))
新的I/O
nio不用太过在意,旧的IO已经用nio来实现过了。
通道和缓冲器
唯一和通道交互得缓冲器为ByteBuffer。原始得字节形式输出和读取,没有办法输出和读取对象。也就是存进去得是什么,拿出来得也是什么。不会包装成String或者其它得对象。
FileChannels 文件通道
1 | // 使用文件通道写入数据 |
通过通道来复制文件
1 | // 常量缓冲区的大小 |
flip()表示ByteBuffer已经写完数据,你从ByteBuffer可以读出来了。
clear()表示ByteBuffer已经清空完数据了,你现在已经可以往ByteBuffer写数据了。
rewind() 返回到数据开始部分。
两个通道相连除了通过上述中间变量之外,使用transferTo()和transFrom()可以将两个通道连起来。这两个不常用,知道就好。
1
2
3
4// in => out
in.transferTo(0,in.size(),out);
// or
// out.transferFrom(in,0,in.size());转换数据
要想ByteBuffer存储有意义的数据,比如说字符串数据。需要写入字节之前对字符串进行编码,或者在读出的时候进行解码。
1
2
3
4// 获得字节数据的时候编码
"hello world".getBytes("UTF-16BE");
// 读取数据的时候解码
buffer.asCharBuffer();使用java.nio.charset.Charset来实现编码和解码。
视图
由于通道存储的只是字节数据。不方便进行读取和管理,这时候需要一个视图缓冲器来作为中间人让使用者直接读写为整型、字符、长整型、浮点、双精度浮点型。
注意: 由于通道存储的是字节数据,转换成相应的数据类型会占用不同字节大小。类型 字节大小 shorts 2 chars 2 ints 4 floats 4 longs 8 double 8
转换成Chars类型。
1 | CharBuffer cb = ((ByteBuffer)bb.rewind()).asCharBuffer(); |
其中bb是一个ByteBuffer的对象。
同理转换成其它的类型的视图
1 | FloatBuffer fb = ((ByteBuffer)bb.rewind()).asFloatBuffer(); |
字节存放次序
有些基本的数据类型需要占用两个及两个以上的字节。计算机的基本存储单位是字节,对于像字符这种占用2个字节,存放有高位优先和低位优先两种方式。java默认的是高位优先。也就是说字符‘a’在内存中存放次序是[0,97]。如果是低位优先则是[97,0]。优先次序使用order来设置。
1
2
3
4// 高位优先
bb.order(ByteOrder.BIG_ENDIAN);
// 低位优先
bb.order(ByteOrder.LITTLE_ENDIAN);
字节存放次序并无优劣之分。重要的数据的一致性,存进去的和读出来的一致便可。
交换相邻两个字符
1 | public class UsingBuffers { |
输出结果:
1 | UsingBuffers |
性能
使用“映射文件访问”更好,更高效。下面是使用传统的io流和通道map的读写对比差距。使用映射速度更加显著。
1 | Stream Write:73.48 |
使用mapped写数据
1 | FileChannel fc = new RandomAccessFile("temp.tmp","rw").getChannel(); |
使用mapped读数据
1 | FileChannel fc = new FileInputStream(new File("temp.tmp")).getChannel(); |