写在前面的话:
- 版权声明:本文为博主原创文章,转载请注明出处!
- 博主是一个小菜鸟,并且非常玻璃心!如果文中有什么问题,请友好地指出来,博主查证后会进行更正,啾咪~~
- 每篇文章都是博主现阶段的理解,如果理解的更深入的话,博主会不定时更新文章。
- 本文初次更新时间:2021.01.01,最后更新时间:2021.01.05
文章目录
- 正文开始
- 1. 文件操作 - File
- 1.1 基本操作
- 1.2 创建、删除文件
- 1.3 创建、删除目录
- 1.4 其他操作
- 1.5 文件过滤器
- 2. RandomAccessFile
- 2.1 创建 RandomAccessFile 实例
- 2.2 字节数据读写操作
- 2.2.1 写一个字节
- 2.2.2 读一个字节
- 2.2.3 利用读、写进行复制操作
- 2.2.4 写一组字节
- 2.2.5 读一组字节
- 2.2.6 改进复制文件
- 2.3 文件指针操作
- 2.3.1 getFilePointer() 方法
- 2.3.2 seek() 方法
- 2.4 基本类型读写
- 2.4.1 如何写入一个 int 值
- 2.4.2 各种基本类型读写
- 2.5 字符串读写
- 3. 基本 IO 操作
- 4. 文件数据 IO 操作
- 参考
- 相关文章
正文开始
1. 文件操作 - File
File
用来表示文件系统中的一个文件或目录。
使用 File 可以:
- 访问其表示的文件或目录的属性信息(名字,大小等信息)
- 创建或删除文件及目录
- 访问一个目录中的子项
重点: File 不能访问文件数据(就是说不能读写文件内容)。
1.1 基本操作
关于File的一些基本操作:
- String getName()
获取文件名称 - long length()
获取文件长度 - boolean canRead()
判断文件是否可读,可读返回true - boolean canWrite()
判断文件是否可写,可写返回true - boolean isHidden()
判断文件是否隐藏,隐藏返回true
示例如下:
package file;
import java.io.File;
public class FileDemo {
public static void main(String[] args) {
File file = new File("./demo.txt");
String name = file.getName();
System.out.println(name);
long len = file.length();
System.out.println(len);
boolean cr = file.canRead();
boolean cw = file.canWrite();
System.out.println("可读:" + cr);
System.out.println("可写:" + cw);
boolean ih = file.isHidden();
System.out.println("是否隐藏:" + ih);
}
}
运行结果:
demo.txt
18
可读:true
可写:true
是否隐藏:false
1.2 创建、删除文件
判断文件或目录是否存在:
- boolean exists()
判断当前 File 表示的文件或目录是否已经存在,存在返回 true
创建文件:
- boolean createNewFile() throws IOException
创建新文件,需要处理异常
创建文件示例:
package file;
import java.io.File;
import java.io.IOException;
public class CreateNewFileDemo {
public static void main(String[] args) throws IOException {
File file = new File("./test.txt");
if (!file.exists()) {
file.createNewFile();
System.out.println("文件已创建");
} else {
System.out.println("文件已存在");
}
}
}
删除文件、目录:
- boolean delete()
删除文件和目录。删除目录时只能删除空目录,非空目录需要挨个把所有子项都删掉才能删除。
删除文件:
package file;
import java.io.File;
public class DeleteFileDemo {
public static void main(String[] args) {
File file = new File("./test.txt");
if (file.exists()) {
file.delete();
System.out.println("文件已删除");
} else {
System.out.println("文件不存在");
}
}
}
1.3 创建、删除目录
创建目录:
- boolean mkdir()
创建目录,要求创建的目录所在的父目录必须存在 - boolean mkdirs()
创建多级目录,会将不存在的父目录一同创建出来
创建目录:
package file;
import java.io.File;
public class MkDirDemo {
public static void main(String[] args) {
File dir = new File("demo");
if (!dir.exists()) {
dir.mkdir();
System.out.println("目录已创建");
} else {
System.out.println("目录已存在");
}
}
}
创建多级目录:
package file;
import java.io.File;
public class MkDirsDemo {
public static void main(String[] args) {
File dir = new File("a/b/c/d/e/f");
if (!dir.exists()) {
dir.mkdirs();
System.out.println("创建完毕!");
} else {
System.out.println("目录已存在!");
}
}
}
删除目录:
package file;
import java.io.File;
public class DeleteDirDemo {
public static void main(String[] args) {
File dir = new File("a");
if (dir.exists()) {
dir.delete();
System.out.println("目录已删除");
} else {
System.out.println("目录不存在");
}
}
}
1.4 其他操作
- boolean isDirectory()
判断是否为目录 - boolean isFile()
判断是否为文件 - File[] listFiles()
列出所有文件
列出指定目录的所有文件:
package file;
import java.io.File;
public class ListFilesDemo {
public static void main(String[] args) {
File dir = new File(".");
if (dir.isDirectory()) {
File[] subs = dir.listFiles();
for (int i = 0; i < subs.length; i++) {
System.out.println(subs[i].getName());
}
}
}
}
运行结果:
6
.classpath
.project
.settings
bin
demo.txt
src
删除文件或目录(递归调用):
package file;
import java.io.File;
public class DeleteTestDemo {
public static void main(String[] args) {
File dir = new File("a");
delete(dir);
}
public static void delete(File f) {
if (f.isDirectory()) {
File[] subs = f.listFiles();
for (int i = 0; i < subs.length; i++) {
File sub = subs[i];
delete(sub);
}
}
f.delete();
}
}
1.5 文件过滤器
重载的ListFiles
方法允许我们传入一个文件过滤器(作为参数),然后将目录中满足过滤器要求的子项返回。
文件过滤器是一个接口,如果用的话要实现类:
class MyFilter implements FileFilter {
public boolean accept(File file) {
String name = file.getName();
return name.startsWith(".");
}
}
示例:
package file;
import java.io.File;
import java.io.FileFilter;
public class FileDemo {
public static void main(String[] args) {
File dir = new File(".");
MyFilter filter = new MyFilter();
File[] subs = dir.listFiles(filter);
System.out.println(subs.length);
for (int i = 0; i < subs.length; i++) {
System.out.println(subs[i].getName());
}
}
}
class MyFilter implements FileFilter {
public boolean accept(File file) {
String name = file.getName();
System.out.println("正在过滤:" + name);
return name.startsWith(".");
}
}
运行结果:
正在过滤:.classpath
正在过滤:.project
正在过滤:.settings
正在过滤:bin
正在过滤:demo.txt
正在过滤:src
3
.classpath
.project
.settings
可以用匿名内部类:
FileFilter filter = new FileFilter() {
public boolean accept(File file) {
String name = file.getName();
System.out.println("正在过滤:" + name);
return name.startsWith(".");
}
};
能少写就少写:
File[] subs = dir.listFiles(new FileFilter() {
public boolean accept(File file) {
String name = file.getName();
System.out.println("正在过滤:" + name);
return name.startsWith(".");
}
});
JDK8
后有lambda表达式
:
File[] subs = dir.listFiles((file)->{
String name = file.getName();
System.out.println("正在过滤:" + name);
return name.startsWith(".");
}
});
最终可以简化为:
File[] subs = dir.listFiles(
(file)->file.getName().startsWith(".")
);
2. RandomAccessFile
上面我们讲到 File 不能访问文件数据,接下来说一个可以读写文件数据的类RandomAccessFile
,这个类是基于指针
操作的。
这是一个专门用来读写文件数据的 API,RandomAccessFile
基于指针对文件数据读写,可以移动指针读写任意位置,所以可以灵活的对文件数据进行编辑工作,即可以对文件随机访问。
2.1 创建 RandomAccessFile 实例
创建 RandomAccessFile 时有两种常见模式:
- r:只读模式,只能读,如果文件不存在则会抛出异常。
- rw:读写模式,如果文件不存在,则会自动创建该文件。
关于创建实例,以下两种方法都可以:
RandomAccessFile raf = new RandomAccessFile("raf.dat", "rw");
File file = new File("raf.dat");
RandomAccessFile raf = new RandomAccessFile(file, "rw");
因为有这两种常用的构造方法(还有其他构造方法,不过没有下面两种常用):
RandomAccessFile(String name, String mode) throws FileNotFoundException
RandomAccessFile(File file, String mode) throws FileNotFoundException
2.2 字节数据读写操作
2.2.1 写一个字节
一个简单的示例:
package raf;
import java.io.File;
import java.io.IOException;
import java.io.RandomAccessFile;
public class RandomAccessFileDemo {
public static void main(String[] args) throws IOException {
RandomAccessFile raf = new RandomAccessFile("raf.dat", "rw");
raf.write(1);
System.out.println("写出完毕!");
raf.close();
}
}
其中:
void write(int b) throws IOException
根据当前指针所在位置处向文件中写入1个字节,写入的是给定的 int 值所对应的二进制的低8位
。
例如:int 是4字节,每8位二进制一个字节,这里只是写入了低8位,具体为,整数1
的二进制为 00000000 00000000 00000000 00000001
,低8位即为00000001
,所以 raf.write(1)
表示写入了 00000001
。
写操作完毕之后,需要close()
,会释放文件的写操作,如果不释放可能会出现“正在读写不能删除”这样子。
上面的示例程序运行完毕后,项目目录下会出现一个 raf.dat
文件,大小为1字节,不过内容没有定义对应的编解码,所以打开看该文件的内容可能是乱码:
值得注意的是,write()
每次创建后都是从头写的,这样就会覆盖之前的数据。并且一个字节无法表示int
型的-1
(int 型的 -1 为 11111111 11111111 11111111 11111111
),只存低 8 位,所以只能写 0-255 之间的数据。
2.2.2 读一个字节
从文件中读取 1 个字节,并以 int 形式返回:
int read() throws IOException
若返回值为-1
,则表示读取到了文件末尾。该方法会从文件中读取一个字节(8位),填充到 int 的低 8 位,剩下的高 24 位为 0,每次读取后自动移动文件指针,准备下次读取。
关于写的小示例:
package raf;
import java.io.File;
import java.io.IOException;
import java.io.RandomAccessFile;
public class ReadDemo {
public static void main(String[] args) throws IOException {
File file = new File("raf.dat");
RandomAccessFile raf = new RandomAccessFile(file, "r");
int d = raf.read();
System.out.println("first: " + d);
d = raf.read();
System.out.println("second: " + d);
raf.close();
}
}
运行结果:
first: 1
second: -1
可以看到,第一次读的时候,我们读取了最开始写进去的 1 的低 8 位,第二次读的时候,由于这时已经读到了文件末尾,所以返回值为 -1。
上面我们提到一个字节无法表示int
型的-1
(int 型的 -1 为 11111111 11111111 11111111 11111111
),只存低 8 位,所以只能写 0-255 之间的数据。做一个小测试,如果一定要使用 write()
写入 -1,那么再用 read()
读出来是多少呢?
简单测试一下就知道啦:
package raf;
import java.io.File;
import java.io.IOException;
import java.io.RandomAccessFile;
public class RandomAccessFileDemo {
public static void main(String[] args) throws IOException {
File file = new File("raf.dat");
RandomAccessFile raf = new RandomAccessFile(file, "rw");
raf.write(-1);
System.out.println("写出完毕!");
raf = new RandomAccessFile(file, "r");
int d = raf.read();
System.out.println("读出完毕:" + d);
raf.close();
}
}
运行结果:
写出完毕!
读出完毕:255
答案是 255。解释一下:int 型的 -1 为 11111111 11111111 11111111 11111111
,写的时候只写入了低8位,即写入了 11111111
,那么读的时候自然是读出了 00000000 00000000 00000000 11111111
,就是 255 啦。
需要注意的是,这里在写入完毕之后,需要重新打开使指针移动到文件开始的位置,如果写完直接读,写完成时指针指向文件末尾,接着读的话返回值就是 -1 了,当然还有其他方法可以移动指针,而不用重新打开,这个等下会讲。
2.2.3 利用读、写进行复制操作
复制很简单,就是一边读一遍写的操作,把读到的数据原封不动地写出来就好了:
package raf;
import java.io.IOException;
import java.io.RandomAccessFile;
public class RandomAccessFileDemo {
public static void main(String[] args) throws IOException {
RandomAccessFile src = new RandomAccessFile("test.pdf", "r");
RandomAccessFile desc = new RandomAccessFile("cp.pdf", "rw");
int d = -1;
while ( (d = src.read()) != -1 ) {
desc.write(d);
}
System.out.println("copy done!");
src.close();
desc.close();
}
}
2.2.4 写一组字节
根据当前指针所在位置处,连续写出给定数组中的所有字节:
void write(byte b[]) throws IOException
根据当前指针所在位置处,连续写出给定数组中的部分字节,从 off 处开始,连续写 len 个字节:
void write(byte b[], int off, int len) throws IOException
示例:
package raf;
import java.io.File;
import java.io.IOException;
import java.io.RandomAccessFile;
public class RandomAccessFileDemo {
public static void main(String[] args) throws IOException {
File file = new File("raf.dat");
RandomAccessFile raf = new RandomAccessFile(file, "rw");
byte[] data = new byte[100];
raf.write(data);
System.out.println("写出完毕!");
raf.close();
}
}
可以看到这次写入的文件大小为 100 字节:
2.2.5 读一组字节
一次性读取给定字节数组总长度的字节量,并存入到该数组中,返回值为实际读取到的字节量,若返回值为 -1,则表示文件末尾:
int read(byte b[]) throws IOException
将给定字节数组从下标 off 处开始的连续 len 个字节一次性写出,返回值为实际读取到的字节量:
int read(byte b[], int off, int len) throws IOException
示例:
package raf;
import java.io.File;
import java.io.IOException;
import java.io.RandomAccessFile;
public class RandomAccessFileDemo {
public static void main(String[] args) throws IOException {
RandomAccessFile raf = new RandomAccessFile("raf.dat", "rw");
int len = -1;
len = raf.read(data);
System.out.println("读出完毕: " + len);
raf.close();
}
}
运行结果:
读出完毕: 100
2.2.6 改进复制文件
上面我们写到利用读写来复制文件,不过当时读取和写入都是 1 字节,速度很慢:
package raf;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.RandomAccessFile;
public class CopyDemo {
public static void main(String[] args) throws IOException {
RandomAccessFile src = new RandomAccessFile("服务器.xmind", "r");
RandomAccessFile desc = new RandomAccessFile("服务器_cp.xmind", "rw");
long start = System.currentTimeMillis();
int d = -1;
while ((d = src.read()) != -1) {
desc.write(d);
}
long end = System.currentTimeMillis();
System.out.println("复制完毕!耗时:" + (end - start) + "ms");
src.close();
desc.close();
}
}
运行结果:
复制完毕!耗时:700ms
接下来我们改进一下,读、写都是一组字节:
package raf;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.RandomAccessFile;
public class CopyDemo2 {
public static void main(String[] args) throws IOException {
RandomAccessFile src = new RandomAccessFile("服务器.xmind", "r");
RandomAccessFile desc = new RandomAccessFile("服务器_cp.xmind", "rw");
byte[] data = new byte[1024];
int len = -1;
long start = System.currentTimeMillis();
while((len = src.read(data)) != -1) {
desc.write(data, 0, len);
}
long end = System.currentTimeMillis();
System.out.println("复制完毕!耗时:" + (end - start) + "ms");
src.close();
desc.close();
}
}
运行结果:
复制完毕!耗时:2ms
单字节读写的模式,一般称为随机读写
;一组字节读写的模式一般称为块读写
。可见使用块读写,复制的效率大大提高。不过需要注意的是,每次读写的大小需要考虑一下硬盘的读写上限哟。
2.3 文件指针操作
RandomAccessFile 的读写操作都是基于指针
的,也就是说总是在指针当前所指向的位置进行读写操作。
2.3.1 getFilePointer() 方法
该方法用于获取当前 RandomAccessFile 的指针位置:
long getFilePointer() throws IOException
小示例:
long pos = raf.getFilePointer();
System.out.println("pos: " + pos);
2.3.2 seek() 方法
该方法用于移动当前 RandomAccessFile 的指针位置:
void seek(long pos) throws IOException
小示例:
raf.seek(0);
2.4 基本类型读写
2.4.1 如何写入一个 int 值
上面我们讲到字节读写,那么如何利用单个字节读写来写入一个 int
值呢。
答案是利用位运算(>>>)
。
假设需要写入的值的二进制为 01111111 11111111 11111111 11111111
,那么我们可以先把高8位01111111
取出来写入,然后再写次高8位,一直到把低8位都写进去。如下:
假设:max = 01111111 11111111 11111111 11111111
执行:int d = max >>> 24;
此时:d = 00000000 00000000 00000000 01111111
测试一下:
package raf;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.RandomAccessFile;
public class RandomAccessFileDemo2 {
public static void main(String[] args) throws IOException {
RandomAccessFile raf = new RandomAccessFile("int.dat", "rw");
long pos = raf.getFilePointer();
System.out.println("pos: " + pos);
int max = Integer.MAX_VALUE;
raf.write(max >>> 24);
System.out.println("*pos: " + raf.getFilePointer());
raf.write(max >>> 16);
System.out.println("*pos: " + raf.getFilePointer());
raf.write(max >>> 8);
System.out.println("*pos: " + raf.getFilePointer());
raf.write(max);
System.out.println("*pos: " + raf.getFilePointer());
System.out.println("写出int值完毕!");
raf.close();
}
}
运行结果:
pos: 0
*pos: 1
*pos: 2
*pos: 3
*pos: 4
写出int值完毕!
可以看到生成的文件为 4 字节:
但是像上面那样写会很麻烦,而这里有一个提供给我们的方法:
void writeInt(int v) throws IOException
该方法可以一次性写4个字节,将给定的 int 值写出。
可以看一下该方法的源码,跟我们刚刚讲的方法是一样滴:
public final void writeInt(int v) throws IOException {
write((v >>> 24) & 0xFF);
write((v >>> 16) & 0xFF);
write((v >>> 8) & 0xFF);
write((v >>> 0) & 0xFF);
}
2.4.2 各种基本类型读写
看名字就懂啦:
void writeBoolean(boolean v)
void writeByte(int v)
void writeShort(int v)
void writeChar(int v)
void writeInt(int v)
void writeLong(long v)
void writeFloat(float v)
void writeDouble(double v)
小示例:
raf.writeInt(123);
raf.writeLong(123L);
raf.writeDouble(123.123);
2.5 字符串读写
byte[] getBytes()
byte[] getBytes(String charsetName) throws UnsupportedEncodingException
3. 基本 IO 操作
4. 文件数据 IO 操作
占个坑,不要急,正在写~~
参考
相关文章
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)