Loading

Java基础-day19

Java 提供了许多实现文件输入 / 输出的类。 这些类可以分为文本 I/O 类二进制 I/O 类

在Java中如何处理文本I/O

使用Scanner类读取文本数据,使用PrintWriter类写文本数据

为了将文本写入一个名为temp.txt的文件中,可以使用PrintWriter类创建一个对象:

PrintWriter output = new PrintWriter("temp.txt");

可以调用该对象的print方法向文件写入一个字符串

output.print("Java 101");

之后关闭文件

output.close();

从文件temp.txt中读入数据:

Scanner input = new Scanner(new File("temp.txt"));
System.out.println(input.nextLine());

Snipaste_2020-05-04_20-04-53

文本I/O 与 二进制I/O

二进制 I / O 不涉及编码和解码 , 因此比文本 I / O 更加高效

Snipaste_2020-05-04_20-14-06

为了保持一致性,使用扩展名.txt来命名文本文件,使用.dat来命名二进制文件

二进制I/O类

抽象类 InputStream 是读取二进制数据的根类 , 抽象类 OutputStream 是写入二进制数据的根类

Snipaste_2020-05-04_20-17-31

Snipaste_2020-05-04_20-22-35

**二进制 I / O 类 中 的 所 有 方 法 都 声 明 为 抛 出java.io.IOException java.io. IOException的子类 **

FileInputStream FileOutputStream

FileInputStream : 从文件读取字节

FileOutputStream :向文件写入字节

  1. 构造一个FileInputStream对象
Snipaste_2020-05-04_20-31-18

如果试图为一个不存在的文件创建 FilelnputStream 对象 , 将会发生java.io.FileNotFoundException 异常

  1. 构造一个FileOutputStream对象
Snipaste_2020-05-04_20-33-41
  • 如果这个文件不存在 , 就会创建一个新文件 。 如果这个文件已经存在 , 前两个构造方法将会删除文件的当前内容 。 为了既保留文件现有的内容又可以给文件追加新数据 , 将最后两个构造方法中的参数 append 设置为 true 。

  • 几乎所有的I/O类中的方法都会抛出异常java.io.IOException

使用二进制 I / O 将从 1 到 10 的 10 个字节值写入一个名为 temp . dat 的文件 , 再把它们从文件中读出来

import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;

public class TestFileStream {
    public static void main(String[] args) throws IOException {
        try(            // 利用try-with-resources来声明和创建输入流
            FileOutputStream output = new FileOutputStream("temp.dat");
                ){
            for (int i = 1; i <= 10; i++)
                output.write(i);    // 也可以写成 output.write((byte)i)
        }

        try(            // 利用try-with-resources来声明和创建输出流
                FileInputStream input = new FileInputStream("temp.dat");
                ){
            int value;
            while((value = input.read()) != -1) // -1意味着文件结束
                System.out.print(value + " ");
        }
    }
}
1 2 3 4 5 6 7 8 9 10

java.io.InputStreamjava.io.OutputStream实现了AutoClosable接口,该接口中定义了close()方法,用于关闭资源。任何AutoClosable类型的对象可以用于try-with-resources语法中实现自动关闭。

FileInputStream类的实例可以作为参数去构造一个Scanner对象,而FileOutputStream类的实例可以作为参数构造一个PrinterWriter对象

new PrintWriter(new FileOutputStream("temp.txt", true));

FilterInputStreamFilterOutputStream

Snipaste_2020-05-04_20-59-58

DataInputStreamDataOutputStream

DataInputStream从数据流读取字节,并且将他们转换为合适的基本类型值或字符串

DataOutputStream将基本类型的值或字符串转换为字节,并且将字节输出到数据流

Snipaste_2020-05-04_21-59-51Snipaste_2020-05-04_21-59-57

Snipaste_2020-05-04_21-59-57

二进制I/O中的字符与字符串

— 个统一码由两个字节构成。

  1. writeChar(char c)方法将字符c的统一码写入输出流
  2. writeChars(String s)方法将字符串s中所有字符的统一码写到输出流中
  3. writeBytes(String s)方法将字符串s中每个字符统一码的低字节写到输出流,统一码的高字节被丢弃,该方法适用于由ASCII码字符构成的字符串(因为ASCII码仅仅存储统一码的低字节)
    • 如果一个字符串包含非ASCII码的字符,必须使用writeChars方法实现写入这个字符串
  4. writeUTF(String s)方法将两个字节的长度信息写入输出流,后面紧跟的是字符串s中每个字符的改进版UTF-8的形式
    • readUTF()方法读取一个使用writeUTF方法写入的字符串
    • UTF-8格式具有存储每个ASCII码就节省一个字节的优势

writeUTF("ABCDEF")写入文件的8个字节(00 06 41 42 43 44 45 46),头两个字节存储的是字符串中的字符个数

创建DataInputStream类和DataOutputStream

根据上面的UML图,通过下面构造方法来创建DataInputStream类和DataOutputStream

public DataInputStream(InputStream instream);
public DataOutputStream(OutputStream outStream);
DataInputStream input = new DataInputStream(new FileInputStream("in.dat")); // 为文件in.dat创建一个输入流
DataOutputStream output = new DataOutputStream(new FileOutputStream("out.dat"));	// 为文件out.dat创建一个输出流

例子:将学生的名字和分数写入名为temp.dat的文件中

import java.io.*;

public class TestDataStream {
    public static void main (String[] args) throws IOException {
        try (
                DataOutputStream output = 
            new DataOutputStream(new FileOutputStream("temps.dat"));
        ){
            output.writeUTF("yzy");
            output.writeDouble(89.0);
            output.writeUTF("wjy");
            output.writeDouble(90.2);
        }

        try(
                DataInputStream input = 
            new DataInputStream(new FileInputStream("temps.dat"));
         ){
            for (int i = 1; i <= 2; i++)
                System.out.println(input.readUTF() + " " + input.readDouble());
        }
    }
}
yzy 89.0
wjy 90.2
Snipaste_2020-05-05_09-34-34

检测文件的末尾

如果到达InputStream 的末尾之后还继续从中读取数据 , 就会发生 EOFException 异常 。
这个异常可以用来检査是否已经到达文件末尾

import java.io.*;

public class TestEOF {
    public static void main(String[] args) throws IOException {
        try {
            try (
                    DataOutputStream output =
                            new DataOutputStream(new FileOutputStream("testEOF.dat"));
            ) {
                output.writeDouble(1.1);
                output.writeDouble(2.2);
                output.writeDouble(3.3);
            }

            try(
                    DataInputStream input =
                            new DataInputStream(new FileInputStream("testEOF.dat"));
                    ){
                while(true){
                    System.out.println(input.readDouble());
                }
            }
        }
        catch (EOFException ex){
            System.out.println("All data were read");
        }
        catch (IOException ex){
            ex.printStackTrace();
        }
    }
}
1.1
2.2
3.3
All data were read

BufferedInputStreamBufferedOutputStream

这两个类可以通过减少磁盘读写次数来提高输入和输出速度

Snipaste_2020-05-05_09-42-59

这两个类没有包含新的方法,所有方法继承自InputStream类和OutputStream

Snipaste_2020-05-05_09-46-58

Snipaste_2020-05-05_09-47-07

如果没有指定缓冲区大小 , 默认的大小是 512 个字节

DataOutputStream output = new DataOutputStream(
	new BufferedOutputStream(new FileOutputStream("temp.dat")));

DataInputStream input = new DataInputStream(
    new BufferedInputStream(new FileInputStream("temp.dat")));

实例学习:复制文件

import java.io.*;

public class Copy {
    public static void main(String[] args) throws IOException {
        if (args.length != 2){
            System.out.println(" Usage : java Copy sourceFile targetfile");
            System.exit(1);
        }

        File sourceFile = new File(args[0]);
        if (!sourceFile.exists()){
            System.out.println("sourceFile does not exist");
            System.exit(2);
        }

        File targetFile = new File(args[1]);
        if (targetFile.exists()){
            System.out.println("targetFile already exist");
            System.exit(3);
        }

        try(
                BufferedInputStream input = new BufferedInputStream(
                        new FileInputStream(sourceFile));
                BufferedOutputStream output = new BufferedOutputStream(
                        new FileOutputStream(targetFile));
                ){
            int r, numberOfByteCopied = 0;
            while ((r = input.read()) != -1){
                output.write((byte)r);
                numberOfByteCopied++;
            }
            System.out.println("number of copied : " + numberOfByteCopied);
        }

    }
}
D:\JAVA_IDEA\JavaProjects\Chapter05\src>java Copy testCopy.txt target.txt
number of copied : 23
Snipaste_2020-05-05_10-39-08

对象I/O

ObjectInputStream类和ObjectOutputStream类可以用于读/写可序列化的对象,也可以实现基本数据类型与字符串的输人和输出,包含了DataInputStream类和DataOutputStream类的所有功能

Snipaste_2020-05-05_10-45-18

public ObjectInputStream(InputStream in)	// 创建一个对象输入流
public ObjectOutputStream(OutputStream out) // 创建一个对象输出流
  1. 姓名 、 分数和当前日期写入名为 object.dat 的文件
import java.io.*;
public class TestObjectOutputStream{
    public static void main(String[] args) throws IOException{
        try(
            ObjectOutputStream output = 
                new ObjectOutputStream(new FileOutputStream("object.dat"));
        ){
            output.writeUTF("John");
            output.writeDouble(33.3);
            outout.writeObject(new java.util.Date());	// 将对象写入文件
        }
    }
}

可以添加缓冲区:

ObjectOutputStream output = new ObjectOutputStream(
new BufferedOutputStream(new FileOutputStream("object.dat")));
  1. 读回这些对象:按照写入时的类型和顺序。为了得到所需的类型 , 必须使用 Java 安全的类型转换
import java.io.*;
public class TestObjectOUtputStream{
    public static void main(String[] args)
    throws ClassNotFoundException, IOException{
        try(
            ObjectInputStream input = 
            new ObjectInputStream(new FileInputStream("object.dat"));
        ){
            String name = input.readUTF();
            double score = input.readDouble();
            java.util.Date date = (java.util.Date)(input.readObject());
            // readObject()方法可能会拋出异常java.lang.ClassNotFoundException
            // readObject()读入的是Object对象,所以应将他转换为Date类型
            System.out.println(name + " " + score + " " + date);
        }
    }
}

Serializable接口

Snipaste_2020-05-05_11-15-41

Snipaste_2020-05-05_11-16-42

序列化数组

如果数组中的所有元素都是可序列化的 , 这个数组就是可序列化的 。 一个完整的数组可以用 writeObject 方法存入文件 , 随后用 readObject 方法恢复

import java.io.*;

public class TestObjectStreamWithArray {
    public static void main(String[] args) throws IOException, ClassNotFoundException {
        int[] num = {1, 2, 3, 4, 5};
        String[] str = {"shang", "shan", "da", "lao", "hu"};

        try (
                ObjectOutputStream output =
                        new ObjectOutputStream(new FileOutputStream("testOb.dat", true));
                ){
            output.writeObject(num);
            output.writeObject(str);
        }

        try (
                ObjectInputStream input =
                        new ObjectInputStream(new FileInputStream("testOb.dat"));
                ){
            int[] number = (int[])(input.readObject());
            String[] strings = (String[])(input.readObject());

            for (int i = 0; i < number.length; i++){
                System.out.print(number[i] + " ");
            }
            System.out.println();

            for (int i = 0; i < strings.length; i++){
                System.out.print(strings[i] + " ");
            }
        }
    }
}
1 2 3 4 5 
shang shan da lao hu 

随机访问文件

Java 提供了RandomAccessFile类 , 允许从文件的任何位置进行数据的读写

  1. 上面学习的都是只读的和只写的,这称为顺序流, 使用顺序流打开的文件称为**顺序访问文件 **。 顺序访问文件的内容不能更新 。

  2. Java 提供了RandomAccessFile类 , 允许在文件的任意位置上进行读写 。 使用 RandomAccessFile 类打开的文件称为随机访问文件

Snipaste_2020-05-05_11-36-05

RandomAccessFile raf = new RandomAccessFile("test.dat", "rw"); 	// rw为可读写的
  • 如果文件 test.dat 已经存在 , 则创建raf以便访问这个文件
  • 如果test.dat不存在,则创建一个名为 test.dat 的新文件,再创建 raf 来访问这个新文件

随机访问文件是由字节序列组成的。一个称为文件指针 的特殊标记定位这些字节中的某个字节的位置

Snipaste_2020-05-05_11-40-52
import java.io.*;

public class TestRandomAccessFile {
    public static void main(String[] args) throws IOException {
        try(
           RandomAccessFile inout = new RandomAccessFile("inout.dat", "rw");
        ){
            inout.setLength(0); // 清空文件
            for (int i = 0; i < 200; i++){  // 写入200个数字
                inout.writeInt(i);
            }

            System.out.println("目前文件长度为:" + inout.length());

            inout.seek(0);  // 将文件指针移动到头位置
            System.out.println("第一个数:" + inout.readInt());

            inout.seek(9 * 4);  // 将文件指针移到第10个数位置
            System.out.println("第10个数:" + inout.readInt());

            inout.seek(inout.length()); // 将文件指针移动末尾
            inout.writeInt(999);

            System.out.println("现在文件长度为:" + inout.length());
        }
    }
}
目前文件长度为:800
第一个数:0
第10个数:9
现在文件长度为:804

Write by Gqq

posted @ 2020-05-05 11:54  ZHGQCN  阅读(124)  评论(0编辑  收藏  举报