RandomAccessFile类的使用

该类默认的写操作是覆盖操作。例如原来的文件内容是“abcde”,我们将文件指针移向2位置(seek(2),即覆盖的位置在b字符之后),我们写入3个字符:raf.write("xxx".getBytes());文件就变成了"abxxe"。如果我们要将xxx插入到b和c字符之间,默认的RandomAccessFile是无法实现的

/**
 * 向文本文件的特定位置插入字符串
 */
public static void insertStringToTxtFile(File f,String content,int pos) throws IOException{
    
    RandomAccessFile raf = new RandomAccessFile(f, "rw");
    raf.seek(pos);
    
    // 暂存插入点后的字符串
    byte[] buf = new byte[1024];
    int len = 0;
    StringBuilder sb = new StringBuilder();
    while ((len = raf.read(buf)) != -1) {
        sb.append(new String(buf, 0, len));
    }
    
    // 此时FP已经移动到了EOF,需要重新回到插入点写入字符串
    raf.seek(pos);
    raf.write(content.getBytes());
    raf.write(sb.toString().getBytes());
    raf.close();
    
}

以上的代码中最关键的代码是将文件指针之后的内容暂存起来,使用到了StringBuilder容器,读取的时候采用的是读取到字节数组(因为一次读取一行可能会产生换行的问题)。

该类用于随机读取文件,利用该类我们可以实现多线程写文件的操作。具体思路是将目标文件分块,每个线程负责写入一个数据块。例如:

public class WriteFile implements Runnable{

    private int block;                                // 数据块标号
    private File descFile;                            // 目标文件
    private static final int SIZE_PER_BLOCK = 20;    // 每个数据块大小
    
    public WriteFile(int block, File descFile) {
        super();
        this.block = block;
        this.descFile = descFile;
    }

    @Override
    public void run() {
        try {
            RandomAccessFile raf = new RandomAccessFile(descFile, "rw");
            raf.seek((block - 1) * SIZE_PER_BLOCK);
            raf.writeBytes("This is block " + block + "\n");
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

}
public class MultipleThreadWrite {
    
    static File file = new File("data.txt");
    
    public static void main(String[] args) {
        
        for (int i = 1; i < 6; i++) {
            new Thread(new WriteFile(i, file)).start();
        }
    }

}

以上开启了5个线程向data.txt文件中写入数据,每个线程负责写入一个数据块的数据,我们要写入的字符串的长度是16个字节,没有完全占用1个数据块,但是第二个线程写入数据的时候将文件指针移动到了20,从20开始写入,前面的4个字节是没有数据的。因此,全部线程执行完毕之后,文件的大小是 4 * 20 + 16 = 96bytes。

clipboard.png

apache commons IO库

maven依赖如下:

<dependency>
    <groupId>commons-io</groupId>
    <artifactId>commons-io</artifactId>
    <version>2.4</version>
</dependency>

常用方法如下:

@Test
public void testFileUtils() throws Exception{
    
    File file = new File("test.txt");

    // 将文本文件读取为字符串
    String str = FileUtils.readFileToString(file, "utf-8");
    System.out.println("文本文件内容:" + str);
    
    // 文件大小
    System.out.println(FileUtils.byteCountToDisplaySize(FileUtils.sizeOf(new File("F:/BaiduYunDownload/OS X Mavericks 10.9 (GM).iso"))));
    
    // 向文件中写入字符串
    FileUtils.writeStringToFile(new File("data.txt"), "你好hellodfd#$", "utf-8");
    
    // 遍历目录及其子目录下的所有文件
    Iterator<File> iterator = FileUtils.iterateFilesAndDirs(new File("E:/tmp"), FileFileFilter.FILE, DirectoryFileFilter.DIRECTORY);
    while (iterator.hasNext()) {
        System.out.println(iterator.next());
        
    }
    
    // 文件拷贝
    File destFile = new File("test_copy.txt");
    FileUtils.copyFile(file, destFile);
    
    // 从URL(必须是file协议,即本地计算机)中得到文件资源
    file = FileUtils.toFile(new URL("file:///E:/tmp/%E7%84%A6%E7%82%B9%E5%9B%BE.html"));
    System.out.println("本地文件:" + file);
    
    // 将文件拷贝到指定目录(目录不存在,创建之)
    FileUtils.copyFileToDirectory(file, new File("E:/tmp/demo"));
    
    // 拷贝一个目录(及其文件和子目录)到另一个目录
    FileUtils.copyDirectoryToDirectory(new File("E:/tmp"), new File("E:/demo"));
    
    // 下载远程资源到本地
    FileUtils.copyURLToFile(new URL("http://www.knowsky.com/384261.html"), new File("tmp.html"), 3000, 3000);
    
    // 删除目录中的内容
    FileUtils.deleteDirectory(new File("E:/demo"));
    
    // 移动文件(rename)
    FileUtils.moveFile(new File("test.txt"), new File("ok.txt"));
    
}

commons io参考文档


雨碎江南
1.8k 声望32 粉丝

风风雨雨寒寒暖暖处处寻寻觅觅,