Java 文件基本技术(二):二进制文件和字节流

  1. InputStream 中常见的方法有哪些?
    答:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    // 基本方法
    public abstract int read() throws IOException;
    public int read(byte[] b) throws IOException;
    public int read(byte b[], int off, int len) throws IOException;
    public void close() throws IOException; // 流读取结束后,关闭,释放相关资源

    // 高级方法
    public long skip() throws IOException;
    public int available() throws IOException;
    public synchronized void mark(int readlimit);
    public boolean markSupported();
    public synchronized void reset() throws IOException;
  1. OutputStream 中常见方法有哪些?
    答:

    1
    2
    3
    4
    5
    public abstract void write(int b) throws IOException;
    public void write(byte b[]) throws IOException;
    public void write(byte b[], int off, int len) throws IOException;
    public void flush() throws IOException; // 将缓冲而未实际写入的数据进行实际写入
    public void close() throws IOException; // 一般会先调用 flush() 方法,然后再释放流占用的系统资源
  2. 【笔试题】将字符串 “hello, 123, 老马” 写到文件 hello.txt 中?
    答:

    1
    2
    3
    4
    5
    6
    7
    8
    OutputStream output = new FileOutputStream("hello.txt");
    try {
    String data = "hello, 123, 老马";
    byte[] bytes = data.getBytes(Charset.forName("UTF-8"));
    output.write(bytes);
    } finally {
    output.close();
    }
  1. 【笔试题】接上题,将上面写入的文件 “hello.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
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    InputStream input = new FileInputStream("hello.txt");
    try {

    ------

    // 方式一:假定一次 read 调用就读到了所有内容,且假定字节长度不超过 1024
    byte[] buf = new byte[1024];
    int bytesRead = input.read(buf);

    // 方式二:为了确保读到所有内容,可以逐个字节读取直到文件结束
    int b = -1;
    int bytesRead = 0;
    while((b=input.read()) != -1) {
    buf[bytesRead++] = (byte)b;
    }

    // 方式三:在没有缓冲的情况下逐个字节读取性能很低,可以使用批量读入且确保读到结尾。
    // 不过,这还是假定文件内容长度不超过一个固定的大学 1024,如果不确定文件内容的长度,但不希望一次性分配过大的 byte 数组,又希望将文件内容全部读入,可以借助 ByteArrayOutputStream
    byte[] buf = new byte[1024];
    int off = 0;
    int bytesRead = 0;
    while((bytesRead = input.read(buf, off, 1024-off)) != -1) {
    off += bytesRead;
    }
    String data = new String(buf, 0, off, "UTF-8");

    // 方式四:使用 ByteArrayOutputStream,改进上面读写文件代码,确保将所有文件内容读入
    InputStream input = new FileInputStream("hello.txt);
    try {
    ByteArrayOutputStream output = new ByteArrayOutputSteam();
    byte[] buf = new byte[1024];
    int bytesRead = 0;
    while((bytesRead = input.read(buf)) != -1) {
    output.write(buf, 0, bytesRead);
    }
    String data = output.toString("UTF-8);
    System.out.println(data);
    } finally {
    input.close();
    }

    ------

    String data = new String(buf, 0, bytesRead, "UTF-8");
    System.out.println(data);
    } finally {
    input.close();
    }
  1. 【笔试题】保存一个学生列表到文件中?
    答:

    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
    // 学生类
    class Student {
    String name;
    int age;
    double score;
    // 省略构造方法和 getter/setter 方法
    }

    // 学生列表内容
    List<Student> students = Arrays.asList(new Student[] {new Student("张三", 18, 80.9d), new Student("李四", 17, 67.5d)});

    // 将该列表内容写到文件 students.dat 中
    public static void writeStudents(List<Student> students) throws IOException {
    DataOutputStream output = new DataOutputStream(new FileOutputSteam("students.dat));
    try {
    output.writeInt(students.size());
    for(Student s : students) {
    output.writeUTF(s.getName());
    output.writeInt(s.getAge());
    output.writeDouble(s.getScore());
    }
    } finally {
    output.close();
    }
    }
  1. 【笔试题】接上题,从文件中读进来?
    答:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    public static List<Student> readStudents() throws IOException {
    DataInputStream input = new DataInputStream(new FileInputStream("students.dat"));
    try {
    int size = input.readInt();
    List<Student> students = new ArrayList<Student> (size);
    for(int i=0; i<size; i++) {
    Student s = new Student();
    s.setName(input.readUTF());
    s.setAge(input.readInt());
    s.setScore(input.readDouble());
    student.add(s);
    }
    return students;
    } finallly {
    input.close();
    }
    }

    // 使用 DataInputStream/DataOutputStream 读写对象,非常灵活,但比较麻烦,所以 Java 提供了序列化机制
  1. 【笔试题】复制输入流的内容到输出流?
    答:

    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
    public static void copy(InputStream input, OutputStream output) throws IOException {
    byte[] buf = new byte[1024];
    int bytesRead = 0;
    while((bytesRead = input.read(buf)) != -1) {
    output.write(buf, 0, bytesRead);
    }
    }

    // 实际上,在 Java 9 中,InputStream 类增加了一个方法 transferTo(),可以实现相同的功能,实现是类似的
    public long transferTo(OutputStream out) throws IOException {
    Object.requireNonNull(out, "out");
    long transferred = 0;
    byte[] buffer = new byte[DEFAULT_BUFFER_SIZE]; // buf 大小是 8192
    int read;
    while((read = this.read(buffer, 0, DEFAULT_BUFFER_SIZE)) >= 0) {
    out.write(buffer, 0, read);
    transferred += read;
    }
    return transferred;
    }

    // 将文件读入字节数组
    public static byte[] readFileToByteArray(String fileName) throws IOException {
    InputStream input = new FileInputStream(fileName);
    ByteArrayOutputStream output = new ByteArrayOutputStream();
    try {
    copy(intpu, output);
    return output.toByteArray();
    } finally {
    input.close();
    }
    }

    // 将字节数组写到文件
    public static void writeByteArrayToFile(String fileName, byte[] data) throws IOException {
    OutputStream output = new FileOutputStream(fileName);
    try {
    output.write(data);
    } finally {
    output.close();
    }
    }