中级05 - File与IO

IO就是输入输出。IO 的本质就是字节流。

一切文件的本质就是一段字节流,不管是文本文件(txt/代码/HTML等)。由每个程序负责解释文件中的字节流,赋予具体含义。

一、IO

1. InputStream/OutputStream

是一种抽象的输入/输出操作,无论是文件写入字节流;从网络读取字节流,或其他什么地方读取字节流。
如果对文件系统不是十分熟悉的话,永远使用绝对路径,防止踩坑。

从输入流中读取字节:

FileInputStream("C:\\Users\\Administrator\\Projects\\tmp\\read-write-files\\src\\main\\java\\com\\github\\hcsp\\io\\Main.java");

        while(true) {
            int b = is.read();
            if (b == -1) {
                break;
            }
//            System.out.print((char)b);
        }

向输出流中写入字节:

OutputStream os = new FileOutputStream("C:\\Users\\Administrator\\Projects\\tmp\\read-write-files\\src\\main\\java\\com\\github\\hcsp\\io\\output.txt");
        os.write('/');
        os.write('/');
        os.write('o');
        os.write('k');

fork 一个子进程(命令行程序),并从标准输入中读取字节:

        ProcessBuilder pb = new ProcessBuilder("ls");
        Process process = pb.start();
        while(true) {
            int b = process.getInputStream().read();
            if (b == -1) {
                break;
            }
            System.out.print((char)b);
        }

        File home = new File("C:\\Users\\Administrator\\Desktop");
        System.out.println(home.isAbsolute());
        home.isFile();
        home.isAbsolute();
        home.isHidden();
        home.exists();
        System.out.println(home);
    }
}

2. FIle

别误会!File 并不代表一个“文件”,它只代表一个抽象的“文件路径”:
文件或者文件夹。
File API 很多,需要掌握的是解决问题的方法,不需要背 API:

File home = new File("C:\\Users\\Administrator\\Desktop");
System.out.println(home.isAbsolute());
home.isFile();
home.isAbsolute();
home.isHidden();
home.exists();
System.out.println(home);

3. BufferedReader/BufferedWriter读写文本文件

为了提高字符流读写效率,引入了缓冲机制,对字符批量读写。
BufferedReader 和 BufferedWriter 各拥有 8192 个字符缓冲区。
读取文本文件时,会先把读入的字符数据放入缓冲区,然后从缓冲区中一次性读取到程序中。
写入字符数据时,先存储至缓冲区,然后从缓冲区中一次性写出到目的地。
所以可以使用 java.io.BufferedReader/java.io.BufferedWriter 代替之前的 InputStream/OutputStream。
(Java Examples- BufferedReader and BufferedWriter)

二、NIO

NIO 是新的IO,来自Java7,是非阻塞的(Non-blocking) IO。
NIO 的 Path 就是旧版本的 FIle。
旧的 IO 是基于流的,优点是抽象良好,但缺点就是慢,一切操作只能一个字节一个字节。
NIO 是基于块的,读写多个块可以不按照顺序。
关于 NIO 只需要学习最常用的一个 Files 工具类,重要的方法有 readAllLines/write等,其他的以后用到再搜。

1. 缓冲

假如缓冲区大小是 1MB,则一次性攒够一百万个字节,然后一次性写入,避免了 CPU 频繁的等待硬盘寻址的时间。

2. 并发(多线程)

即同时向多个块读写数据

三、不要重复发明轮子(生产场景中)

  • FileUtils
  • IOUtils
public static String readFile(File  file) throws IOException {
    return FileUtils.readFileToString(file, Charset.defaultCharset());
}

public static String inputStreamToString(InputSream is) {
    return IOUtils.toString(is, Charset.defaultCharset());
}

四、IO实战

1. 使用多种方法读写文件

package com.github.hcsp.io;

import org.apache.commons.io.FileUtils;

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.nio.file.Files;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

public class FileAccessor {

    /**
     * 使用最原始的 FileInputStream,一个字符一个字符地读取
     *
     * @param file 待读取的文件
     * @return 将文件按行分割后的 List<String>
     * @throws IOException 如果读取时报错,比如没有权限,文件不存在等
     */
    public static List<String> readFile1(File file) throws IOException {
        List<String> result = new ArrayList<>();
        InputStream is = new FileInputStream(file);
        String line = "";
        while (true) {
            int b = is.read();
            if (b == -1) {
                break;
            }
            if (b != '\n') {
                line = String.join("", line, Character.toString((char) b));
            } else {
                result.add(line);
                line = "";
            }
        }
        return result;
    }

    /**
     * 使用 BufferedReader 一行一行地读取
     *
     * @param file 待读取的文件
     * @return 将文件按行分割后的 List<String>
     * @throws IOException 如果读取时报错,比如没有权限,文件不存在等
     */
    public static List<String> readFile2(File file) throws IOException {
        List<String> result = new ArrayList<>();
        FileReader fr = new FileReader(file);
        BufferedReader br = new BufferedReader(fr);
        while (true) {
            String line = br.readLine();
            if (line == null) {
                break;
            }
            result.add(line);
        }
        br.close();
        return result;
    }

    /**
     * 使用第三方库 Apache Commons IO 的 FileUtils 读取
     *
     * @param file 待读取的文件
     * @return 将文件按行分割后的 List<String>
     * @throws IOException 如果读取时报错,比如没有权限,文件不存在等
     */
    public static List<String> readFile3(File file) throws IOException {
        return FileUtils.readLines(file);
    }

    /**
     * 使用 Java 7+ 引入的 Files.write() 方法F
     *
     * @param file 待读取的文件
     * @return 将文件按行分割后的 List<String>
     * @throws IOException 如果读取时报错,比如没有权限,文件不存在等
     */
    public static List<String> readFile4(File file) throws IOException {
        return Files.readAllLines(file.toPath());
    }

    /**
     * 使用最原始的 FileOutputStream,一行一行地写入
     *
     * @param lines 待写入的数据
     * @param file  用来接收写入操作的文件
     * @throws IOException 如果读取时报错,比如没有权限,文件不存在等
     */
    public static void writeLinesToFile1(List<String> lines, File file) throws IOException {
        OutputStream os = new FileOutputStream(file);
        for (String line : lines) {
            os.write(line.getBytes());
            os.write('\n');
        }
    }

    /**
     * 使用 BufferedWriter 一行一行地写入
     *
     * @param lines 待写入的数据
     * @param file  用来接收写入操作的文件
     * @throws IOException 如果读取时报错,比如没有权限,文件不存在等
     */
    public static void writeLinesToFile2(List<String> lines, File file) throws IOException {
        FileWriter fw = new FileWriter(file);
        BufferedWriter bw = new BufferedWriter(fw);
        for (String line : lines) {
            bw.write(line);
            bw.newLine();
        }
        bw.close();
    }

    /**
     * 使用第三方库 Apache Commons IO 的 FileUtils 写入
     *
     * @param lines 待写入的数据
     * @param file  用来接收写入操作的文件
     * @throws IOException 如果读取时报错,比如没有权限,文件不存在等
     */
    public static void writeLinesToFile3(List<String> lines, File file) throws IOException {
        FileUtils.writeLines(file, lines);
    }

    /**
     * 使用 Java 7+ 引入的 Files.write() 方法
     *
     * @param lines 待写入的数据
     * @param file  用来接收写入操作的文件
     * @throws IOException 如果读取时报错,比如没有权限,文件不存在等
     */
    public static void writeLinesToFile4(List<String> lines, File file) throws IOException {
        Files.write(file.toPath(), lines);
    }

    public static void main(String[] args) throws IOException {
        File projectDir = new File(System.getProperty("basedir", System.getProperty("user.dir")));
        File testFile = new File(projectDir, "target/test.txt");
        List<String> lines = Arrays.asList("AAA", "BBB", "CCC");
        writeLinesToFile1(lines, testFile);
        writeLinesToFile2(lines, testFile);
        writeLinesToFile3(lines, testFile);
        writeLinesToFile4(lines, testFile);

        System.out.println(readFile1(testFile));
        System.out.println(readFile2(testFile));
        System.out.println(readFile3(testFile));
        System.out.println(readFile4(testFile));
    }
}

2. 给定一个Github仓库名读取前n个Pull request并保存至csvFile指定的文件中(未使用SDK)

package com.github.hcsp.io;

import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
import org.jsoup.nodes.Element;

import java.io.BufferedWriter;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.util.ArrayList;

public class Crawler {

    private static ArrayList<GitHubPullRequest> PULL_REQUESTS = new ArrayList<>();
    private static int numberLimitOfPR;

    /**
     * 给定一个仓库名,例如"golang/go",或者"gradle/gradle",读取前n个Pull request并保存至csvFile指定的文件中,格式如下:
     * number,author,title
     * 12345,blindpirate,这是一个标题
     * 12345,FrankFang,这是第二个标题
     *
     * @param repo    仓库名
     * @param n       前 n 个 Pull request
     * @param csvFile 用来存放 Pull request 信息的指定 csv 文件路径
     * @throws IOException 当写入出错时
     */
    public static void savePullRequestsToCSV(String repo, int n, File csvFile) throws IOException {

        numberLimitOfPR = n;
        int page = 1;
        getSpecifiedPageOfPullRequestsAndStoreWithinLimit(repo, page);
        while (true) {
            if (PULL_REQUESTS.size() < n) {
                getSpecifiedPageOfPullRequestsAndStoreWithinLimit(repo, ++page);
            } else {
                break;
            }
        }
        FileWriter fw = new FileWriter(csvFile);
        BufferedWriter bw = new BufferedWriter(fw);
        bw.write("number,author,title");
        bw.newLine();
        for (GitHubPullRequest pr : PULL_REQUESTS) {
            bw.write(pr.toString());
            bw.newLine();
        }
        bw.close();
        System.out.println(PULL_REQUESTS.size());
    }

    static class GitHubPullRequest {
        // Pull request的编号
        int number;
        // Pull request的标题
        String title;
        // Pull request的作者的GitHub id
        String author;

        GitHubPullRequest(int number, String title, String author) {
            this.number = number;
            this.title = title;
            this.author = author;
        }

        @Override
        public String toString() {
            return number + "," + author + "," + title;
        }
    }

    /**
     * 给定一个仓库名,例如"golang/go",或者"gradle/gradle",获取指定页的 Pull request 信息
     * 然后存够指定数量的 GitHubPullRequest 对象到 PULL_REQUESTS 中
     *
     * @param repo 仓库名
     * @param page 当前 Pull request 的页数
     * @throws IOException 当获取出错时
     */
    public static void getSpecifiedPageOfPullRequestsAndStoreWithinLimit(String repo, int page) throws IOException {
        Document doc = Jsoup.connect("https://github.com/" + repo + "/pulls" + "?page=" + page).get();
        ArrayList<Element> issues = doc.select(".js-issue-row");
        for (Element element : issues) {
            GitHubPullRequest pr = new GitHubPullRequest(
                    Integer.parseInt(element.attr("id").substring(6)),
                    element.select(".js-navigation-open").get(0).text(),
                    element.select(".muted-link").get(0).text()
            );
            if (PULL_REQUESTS.size() < numberLimitOfPR) {
                PULL_REQUESTS.add(pr);
            }
        }
    }

    public static void main(String[] args) throws IOException {
        savePullRequestsToCSV("golang/go", 40, new File("./temp.csv"));
    }

}

参考:
CPU:这个世界太慢了

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 215,539评论 6 497
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,911评论 3 391
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 161,337评论 0 351
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,723评论 1 290
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,795评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,762评论 1 294
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,742评论 3 416
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,508评论 0 271
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,954评论 1 308
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,247评论 2 331
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,404评论 1 345
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,104评论 5 340
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,736评论 3 324
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,352评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,557评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,371评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,292评论 2 352

推荐阅读更多精彩内容

  • 五、IO流 1、IO流概述 (1)用来处理设备(硬盘,控制台,内存)间的数据。(2)java中对数据的操作都是通过...
    佘大将军阅读 507评论 0 0
  • 概述: 1、IO流:即Input Output的缩写。 2、特点:1)IO流用来处理设备间的数据传输。2)Java...
    玉圣阅读 1,246评论 0 3
  • 1 I/O流的概念,分类2 I/O所有类的结构图及详解3 何为NIO,和传统I/O有何区别4 在开发中正确使用I/...
    艾剪疏阅读 493评论 0 2
  • 一.介绍 在我们学习字节流与字符流的时候,大家都进行过读取文件中数据的操作,读取数据量大的文件时,读取的速度会很慢...
    走着别浪阅读 339评论 0 2
  • 一、基础知识:1、JVM、JRE和JDK的区别:JVM(Java Virtual Machine):java虚拟机...
    杀小贼阅读 2,376评论 0 4