# 文件处理

文件编码

File类的使用

RandomAccessFile的使用

字节流的使用

字符流的使用

对象的序列化与反序列化

# 文件编码

String s = "您好世界ABC";
	
/**
 * 字符串转byte[]
 */
// 转换成字节码序列用的是项目默认的编码GBK,中文占两个字节
byte[] bytes1 = s.getBytes();
for (byte b : bytes1) {
    // 把字节(转换错了int)以16进制的方式显示
    System.out.print(Integer.toHexString(b & 0xff) + " ");
}
// 结果:c4 fa ba c3 ca c0 bd e7 41 42 43 
System.out.println("");

byte[] bytes2 = s.getBytes("GBK");
for (byte b : bytes2) {
    System.out.print(Integer.toHexString(b & 0xff) + " ");
}
// 结果:c4 fa ba c3 ca c0 bd e7 41 42 43 
// GBK编码中文占2个字节,英文占1个字节
System.out.println("");

// utf-8
byte[] bytes3 = s.getBytes("utf-8");
for (byte b : bytes3) {
    System.out.print(Integer.toHexString(b & 0xff) + " ");
}
// 结果:e6 82 a8 e5 a5 bd e4 b8 96 e7 95 8c 41 42 43 
// utf-8编码中文占3个字节,英文占1个字节
System.out.println("");

// Java是双字节编码utf-16be
byte[] bytes4 = s.getBytes("utf-16be");
for (byte b : bytes4) {
    System.out.print(Integer.toHexString(b & 0xff) + " ");
}
// 结果:60 a8 59 7d 4e 16 75 4c 0 41 0 42 0 43 
// utf-16be中文占2个字节,英文占用2个字节
System.out.println("");

/**
 * byte[]转字符串
 * 当字节序列为某种编码时,这个时候想把字节序列变成字符串,
 * 也需要用这种编码方式,否则会出现乱码
 */
// 用项目默认的编码
String str1 = new String(bytes1);
System.out.println(str1);
String str2 = new String(bytes2, "GBK");
System.out.println(str2);
String str3 = new String(bytes3, "utf-8");
System.out.println(str3);
String str4 = new String(bytes4, "utf-16be");
System.out.println(str4);

/**
 * 文本文件 就是字节序列,
 * 其可以使任意编码的字节序列
 * *在中文机器上直接创建文本文件,该文件只认识ansi编码
 */
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

# File类常用API

java.io.File类用于表示文件(或目录)

File类只用于表示文件(或目录)的信息(名称、大小等),不能用于文件内容的访问。

File file = new File("D:\\java\\test");
System.out.println("文件是否存在:" + (file.exists() ? "是" : "否"));
// 不存在文件(目录)创建
if (!file.exists()) {
    file.mkdir();
    // 多级目录创建
    //file.mkdirs();
} else {
    file.delete();
}
// 是目录或目录存在返回true,否则返回false
System.out.println("是否为目录:" + file.isDirectory());
System.out.println("是否为文件:" + file.isFile());
System.out.println();

//File file2 = new File("D:\\java\\test.txt");
File file2 = new File("D:\\java", "test.txt");
if (!file2.exists()) {
    try {
        file2.createNewFile();
    } catch (IOException e) {
        e.printStackTrace();
    }
} else {
    file2.delete();
}

System.out.println(file);//D:\java\test
System.out.println(file.getAbsolutePath());//D:\java\test
System.out.println(file.getParent());//D:\java
System.out.println(file.getName());//test
System.out.println();

System.out.println(file2.getParent());//D:\java
System.out.println(file2.getName());//test.txt
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

list() 与 listFiles()

/**
 * 列出指定目录下的所有文件及子文件夹(包含其子目录)
 * @param dir
 * @param deep
 */
public static void printListDirectory(File dir, boolean deep) throws IOException {
    if (!dir.exists()) {
        throw new IllegalArgumentException("目录不存在," + dir);
    }
    if (!dir.isDirectory()) {
        throw new IllegalArgumentException(dir + ", 不是目录");
    }
    //返回目录下的所有文件或子目录,及子目录的所有文件及目录
    if (deep) {
        File[] files = dir.listFiles();
        if (files != null && files.length > 0) {
            for (File f : files) {
                if (f.isDirectory()) {
                    printListDirectory(f, true);
                } else {
                    System.out.println(f);
                }
            }
        }
    }
    //返回目录下的文件或子目录名称,不包含子目录下的内容
    else {
        String[] fileNames = dir.list();
        for (String str : fileNames) {
            System.out.println(dir + "\\" + str);
        }
    }
}
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

# RandomAccessFile

java提供的对文件内容的访问,既可以读文件,也可以写文件。

支持随机访问文件,可以访问文件的任意位置。

(1)java文件模型:在硬盘上的文件是byte byte byte存储的,是数据的集合

(2)打开文件:有两种模式rw(读写),r(只读)。

RandomAccessFile raf = new RandomAccessFile(file, "rw");
1

文件指针,打开文件时指针在开头 pointer = 0;

(3)写方法

raf.write(int);
1

只写一个字节(后8位),同时指针指向下一个位置,准备再次写入。

(4)读方法

raf.read();
1

从指针位置读一个字节。

(5)文件读写完成以后,一定要关闭,否则可能会抛出意想不到的异常(Oracle官方说明)

File demo = new File("D:\\java\\demo");
if (!demo.exists()) {
    demo.mkdir();
}
File file = new File(demo, "raf.dat");
if (!file.exists()) {
    file.createNewFile();
}
RandomAccessFile raf = new RandomAccessFile(file, "rw");
// 指针的位置
System.out.println(raf.getFilePointer());
// write方法每次只能写一个字节
raf.write('A');
System.out.println(raf.getFilePointer());
raf.write('B');

int num = 0x7fffffff;
// write方法每次只能写一个字节,如果把num写进去就的写4次
raf.write(num >>> 24);
raf.write(num >>> 16);
raf.write(num >>> 8);
raf.write(num);

//可以直接写入一个int
raf.writeInt(num);

String s = "中";
byte[] gbk = s.getBytes("GBK");
raf.write(gbk);
System.out.println(raf.length());

// 读文件,把指针移到头部
raf.seek(0);
//一次性读取,把文件中的内容,读取到字节数组中
byte[] buf = new byte[(int)raf.length()];
raf.read(buf);

System.out.println(Arrays.toString(buf));
for (byte b : buf) {
    System.out.print(Integer.toHexString(b & 0xff) + " ");
}
raf.close();
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

# 字节流 FileInputStream

IO流(输入流、输出流)

字节流、字符流

1.字节流

InputStream、OutputStream InputStream抽象了应用程序读取数据的方式 OutputStream抽象了应用程序写出数据的方式 EOF = End 读到-1,即读到结尾

输入流基本方法:

// 读取一个字节无符号填充到int低8位,-1是EOF
int b = in.read();
// 读取数据到字节数组buf
in.read(byte[] buf);
// 读取数据到字节数组buf,从buf的start位置开始,存放size长度的数据
in.read(byte[] buf, int start, int size);
1
2
3
4
5
6

输出流基本方法:

// 写出一个byte到流,b的低8位(一个整型32位)
out.write(int b);
// 将buf字节数组都写入到流
out.write(buf);
// 字节数组buf从start位置开始写size长度的字节到流
out.write(byte[] buf, int start, int size);
1
2
3
4
5
6

FileInputStream

FileInputStream(InputStream的子类,及继承了InputStream),具体实现了在文件上读取数据

/**
 * 读取指定文件内容,按照16进制输出到控制台
 * 并且每输出10个byte换行
 * @param fileName
 * @throws IOException 
 */
public static void printHex(String fileName) throws IOException {
    // 把文件作为字节流进行读取操作
    FileInputStream in = new FileInputStream(fileName);
    int b;
    int count = 1;
    while((b = in.read()) != -1) {
        // 单位数前补0,及小于10的数
        if (b <= 0xf) {
            System.out.print("0");
        }
        System.out.print(Integer.toHexString(b) + " ");
        if (count++ % 10 == 0) {
            System.out.println();
        }
    }
    in.close();
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
/**
 * printHexByByteArray
 * @param fileName
 * @throws IOException
 */
public static void printHexByByteArray(String fileName) throws IOException {
    File file = new File(fileName);
    FileInputStream in = new FileInputStream(file);
    byte[] buf = new byte[(int)file.length()];
    // 从in中排量读取字节,放入到buf这个字节数组中
    // 从第0个位置开始放,最多放buf.length个
    // 返回的是读到的字节的个数
    int bytes = in.read(buf, 0, buf.length);
    int count = 1;
    for (int i = 0; i < bytes; i++) {
        print(buf[i], count++);
    }
    in.close();
}

/**
 * printHexByByteArray2
 * @param fileName
 * @throws IOException
 */
public static void printHexByByteArray2(String fileName) throws IOException {
    File file = new File(fileName);
    FileInputStream in = new FileInputStream(file);
    byte[] buf = new byte[(int)file.length()];
    int count = 1;
    int bytes = 0;
    while((bytes = in.read(buf, 0, buf.length)) != -1) {
        for (int i = 0; i < bytes; i++) {
            print(buf[i], count++);
        }
    }
    in.close();
}

/**
 * print
 * @param b
 * @param count
 */
private static void print(int b, int count) {
    // 单位数前补0,及小于10的数
    if (b >= 0x0 && b <= 0xf) {
        System.out.print("0");
    }
    // & 0xff:byte类型8位,int类型32位,
    // 为了避免数据转换错误,通过&0xff将高24位清零
    System.out.print(Integer.toHexString(b & 0xff) + " ");
    if (count % LINE_NUM == 0) {
        System.out.println();
    }
}
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

Test

String filePath = "D:\\java\\test.txt";
IOUtils.printHex(filePath);
System.out.println("\n");
IOUtils.printHexByByteArray(filePath);
System.out.println("\n");
IOUtils.printHexByByteArray2(filePath);
1
2
3
4
5
6

FileOutputStream

FileOutputStream(OutputStream的子类,及继承了OutputStream),实现了向文件中写入byte数据的方法

String filePath = "D:\\java\\temp\\out.dat";
// 如果文件不存在,则直接创建;如果存在,删除后再创建
// #若有第二个参数:true往文件里追加内容
FileOutputStream out = new FileOutputStream(filePath);
// 写出了A的低8位
out.write('A');
// 写出了B的低8位
out.write('B');
// write只能写8位,即写一个int需要写4次
int a = 10;
out.write(a >>> 24);
out.write(a >>> 16);
out.write(a >>> 8);
out.write(a);
byte[] gbk = "中国".getBytes("GBK");
out.write(gbk);
out.close();

IOUtils.printHex(filePath);
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19

IOUtils.java

/**
 * file copy
 * @param srcFile
 * @param destFile
 * @throws IOException
 */
public static void fileCopy(File srcFile, File destFile) throws IOException {
    if (!srcFile.exists()) {
        throw new IllegalArgumentException("文件不存在。" + srcFile);
    }
    if (!srcFile.isFile()) {
        throw new IllegalArgumentException("非文件对象。" + srcFile);
    }
    FileInputStream in = new FileInputStream(srcFile);
    FileOutputStream out = new FileOutputStream(destFile);
    byte[] buffers = new byte[(int)srcFile.length()];
    int n;
    while((n = in.read(buffers, 0, buffers.length)) != -1) {
        out.write(buffers, 0, n);
        out.flush();
    }
    in.close();
    out.close();
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24

FileOutPutDemo.java

File srcFile = new File("D:\\java\\temp\\out.dat");
File destFile = new File("D:\\java\\temp\\copy.txt");
IOUtils.fileCopy(srcFile, destFile);
1
2
3

# DataInputStream/DataOutputStream

对“流”功能的扩展,可以更加方便的读取int、long、字符等数据类型的数据。

# DataOutputStream
writeInt()/writeDouble()/writeUTF()...
1
2

DataOutputStreamDemo.java

package file.io;

import java.io.DataOutputStream;
import java.io.FileOutputStream;
import java.io.IOException;

public class DataOutputStreamDemo {

	public static void main(String[] args) throws IOException {
		String filePath = "D:\\java\\temp\\dos.dat";
		FileOutputStream fos = new FileOutputStream(filePath);
		DataOutputStream dos = new DataOutputStream(fos);
		// write
		dos.writeInt(10);
		dos.writeInt(-10);
		dos.writeLong(10l);
		dos.writeDouble(10.5);
		// 采用utf-8编码输出
		dos.writeUTF("中国");
		// 采用utf-16be编码输出
		dos.writeChars("中国");
		dos.close();
		
		IOUtils.printHex(filePath);
		System.out.println("\n");
		IOUtils.printHexByByteArray(filePath);
		System.out.println("\n");
		IOUtils.printHexByByteArray2(filePath);
	}

}
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

DataInputStreamDemo.java

package file.io;

import java.io.DataInputStream;
import java.io.FileInputStream;
import java.io.IOException;

public class DataInputStreamDemo {

	public static void main(String[] args) throws IOException {
		String filePath = "D:\\java\\temp\\dos.dat";
		IOUtils.printHex(filePath);
		System.out.println("\n");
		
		FileInputStream inputFile = new FileInputStream(filePath);
		DataInputStream dis = new DataInputStream(inputFile);
		
		// read
		int i = dis.readInt();
		System.out.println(i);
		i = dis.readInt();
		System.out.println(i);
		
		long l = dis.readLong();
		System.out.println(l);
		
		double d = dis.readDouble();
		System.out.println(d);
		
		String s = dis.readUTF();
		System.out.println(s);
		
		dis.close();
	}

}
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