文件操作与IO(3)

简介: 文件操作与IO(3)

文件内容的读写--数据流

这里我们将要讲到文件操作中的重要概念--流.

之前也在C语言讲解中提到了文件流的概念---读写文件内容

分为这几步:(1)打开文件;(2)读/写文件;(3)关闭文件.

数据流主要分为字节流和字符流.

字节流:以字节为单位进行读写(代表:InputStream,OutputStream).

字符流:以字符为单位进行读写,比如utf8表示汉字--即每次读写都得以3个字节(就是一个汉字)为单位进行读写.(代表:Reader:输入;Writer:输出.)

InputStream概述

方法

修饰符及返回值类型 方法签名 说明
int(实际是byte) read()

读取一个字节的数据,返回值代表读取到的

字节值,返回-1代表已经完全读完了

int read(byte[] b)

最多读取b.length字节的数据到b中,返回

实际读到的数量;-1代表已经读完了

int read(byte[] b, int off, int len)

其中off是offset(偏移量),最多读取len - off字节的数据

到b中,放在从off开始,返回实际读到的数量,-1代表读完

void close() 关闭字节流

注意:

1.byte[] b表示一个缓冲区,往往是一个内存空间,方法内部对数组内容进行修改,方法执行结束之后,方法外部,亦会生效.

2.在后面两个read方法中,read会尝试把数组填满,但文件剩余长度不足以填满.但文件剩余长度不足以填满.操作硬盘,本身就是比较低效的操作.出现次数越少越好.期望借助内存减少读写硬盘次数.

3.不同于内存的自动垃圾回收机制(GC),这里的字节流必须使用close()关闭(释放了文件相关资源).原因如下:我们在之前学进程PCB(一个或多个)时,中间就有个重要的属性--文件操作符表:记录打开了哪些文件.文件操作符表如果自动扩容,会付出很大代价,对于操作系统内核要求很高:每次打开一个文件,都需要在文件操作符表中占据一个位置的.如果不关闭的话,还一直代开就会导致文件操作符表耗尽.(文件操作符表的长度有上限,当文件操作符表被耗尽之后,后续再打开文件就会失败.进一步引发一系列逻辑问题).

说明

InputStream只是一个抽象类,要使用还需要具体的实现类.关于InputStream的实现类有很多,基本可以认为不同的输入设备都可以对应一个InputStream类,我们只关心从文件中读取,所以使用FileInputStream.

FileInputStream概述

构造方法

签名 说明
FileInputStream(File file) 利用File构造文件输入流
FileInputStream(String name) 利用文件路径构造文件输入流

代码示例

示例1

将文件完全读完的两种方式.相比较而言,后一种的IO次数更少,性能更好.

import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
 
//需要先在项目目录下准备好一个hello.txt的文件,里面填充一些内容
public class FileTest8 {
    public static void main(String[] args) throws IOException {
        //注意:这样使用try() {格式写的原因是因为try执行完之后可以自动调用close()方法
        try(InputStream is = new FileInputStream("hello-world.txt")) {
            while (true) {
                int b = is.read();
                if(b == -1) {
                    //代表文件已经全部读完
                    break;
                }
 
                System.out.printf("%c", b);
            }
        }
    }
}
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
 
public class FileTest9 {
    public static void main(String[] args) throws IOException {
        try (InputStream is = new FileInputStream("hello-world.txt")) {
            byte[] buf = new byte[1024];
            int len;
            while(true) {
                len = is.read(buf);
                if(len == -1) {
                    //代表文件已经全部读完
                    break;
                }
                for(int i = 0; i < len; i++) {
                    System.out.printf("%c", buf[i]);
                }
            }
        }
    }
}

示例2

这里我们把文件内容中填充中文看看,注意,写中文的时候使用UTF-8编码.hello-world.txt中填写"卢本伟牛逼"

注意:这里我利用了这几个中文的UTF-8编码长度刚好是三个字节和长度不超过1024字节的现状,但这种方式不是通用的.

import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
 
public class FileTest10 {
    public static void main(String[] args) throws IOException {
        try (InputStream is = new FileInputStream("hello-world.txt")) {
            while(true) {
                byte[] buf = new byte[1024];
                int n = is.read(buf);
                if(n == -1) {
                    break;
                }
                //此处String的构造是基于前n个字节,而不是整个数组
                String s = new String(buf, 0, n);
                System.out.println(s);
            }
        }
    }
}

运行结果:

利用Scanner进行字符读取

上述栗子中,我们看到了对字符类型直接使用InputStream进行读取是非常麻烦且困难的,所以,我们使用一种我们之前比较熟悉的类来完成该工作,就是Scanner类.

构造方法 说明
Scanner(InputStream is, String charset) 使用charset字符集进行is的扫描读取
import java.io.*;
import java.util.Scanner;
 
public class FileTest11 {
    public static void main(String[] args) throws IOException {
        try (InputStream is = new FileInputStream("hello-world.txt")) {
            try (Scanner sc = new Scanner(is, "UTF-8")) {
                while (sc.hasNext()) {
                    String s = sc.next();
                    System.out.println(s);
                }
            }
        }
    }
}

OutputStream概述

方法

修饰符及返回值类型 方法签名 说明
void  write(int b) 写入要给字节的数据
void  write(byte[] b) 将b这个字符数组中的数据全部写入os中
int write(byte[] b, int off, int len) 将b这个字符数组从off开始的数据写入os中,一共写len个
void  close() 关闭字节流
void flush()

重要:我们知道I/O的速度是很慢的,所以,大多OutputStream

为了减少设备操作的次数,再写数据的时候都会将数据暂时

写入内存的一个指定区域里,直到该区域满了或者其它指定

条件时才真正将数据写入设备中,这个区域一般称为缓冲区.

但造成一个结果,就是我们写的数据,很可能会遗留一部分在缓冲区中.需要在最后或者合适的位置,调用flush(刷新)操作,

将数据刷新到设备中.

说明

Output同样只是一个抽象类,要使用还需要具体的实现类.我们现在还是只关心写入文件中,所以使用FileOutputStream.

利用OutputStreamWriter进行字符写入

示例

注意,此处OutputStream默认情况下,会把之前的文件内容都清空掉,然后重新开始写(清空)

不是write引起的,而是打开操作引起的.

写文件的时候,也不是说,非得把文件内容清空,也可以使用追加写的方式,不清空文件内容,把新的内容写到文件末尾.

eg.OutputStream os = new FileOutputStream("test.txt", true);//将后面的append追加视为true即可.

import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
 
public class FileTest12 {
    public static void main(String[] args) throws IOException {
        try (OutputStream os = new FileOutputStream("hello-world.txt")) {
            os.write('l');
            os.write('b');
            os.write('w');
            os.write('n');
            os.write('b');
            //不要忘记flush
            os.flush();
        }
    }
}
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
 
public class FileTest13 {
    public static void main(String[] args) throws IOException {
        try(OutputStream os = new FileOutputStream("hello-world.txt")) {
            byte[] b = new byte[] {
                    (byte)'G', (byte)'o', (byte)'o', (byte)'d'
            };
            os.write(b);
            //不要忘记flush
            os.flush();
        }
    }
}
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
 
public class FileTest14 {
    public static void main(String[] args) throws IOException {
        try (OutputStream os = new FileOutputStream("hello-world.txt")) {
            byte[] b = new byte[] {
                    (byte)'b', (byte)'y', (byte)'e'
            };
            os.write(b, 0, 3);
 
            //不要忘记flush
            os.flush();
        }
    }
}
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
 
public class FileTest15 {
    public static void main(String[] args) throws IOException {
        try (OutputStream os = new FileOutputStream("hello-world.txt")) {
            String s = "Nothing";
            byte[] b = s.getBytes();
            os.write(b);
 
            //不要忘记flush
            os.flush();
        }
    }
}
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
 
public class FileTest16 {
    public static void main(String[] args) throws IOException {
        try (OutputStream os = new FileOutputStream("hello-world.txt")) {
            String s = "从今天开始这里叫卢本伟广场";
            byte[] b = s.getBytes("utf-8");
            os.write(b);
            //不要忘记flush
            os.flush();
        }
    }
}

利用PrintWriter找到我们熟悉的方法

上述,我们其实已经完成输入工作,但总是有所不方便,我们接下来将用OutputStream处理下,使用PrintWriter类来完成输出,因为

PrintWriter类中提供了我们熟悉的print/printf/println方法.

OutputStream os = ...;

OutputStreamWriter osWriter = new OutputStreamWriter(os, "utf-8");

PrintWriter writer = new PrintWriter(osWriter);

//接下来就可以方便的使用writer提供的各种方法了

writer.print("Hello");

writer.println("你好");

writer.printf("%d: %s\n", 1, "没有什么");

//不要忘记flush

writer.flush();

示例:

import java.io.*;
 
public class FileTest17 {
    public static void main(String[] args) throws IOException {
        try (OutputStream os = new FileOutputStream("output.txt")) {
            try (OutputStreamWriter osWriter = new OutputStreamWriter(os, "utf-8")) {
                try (PrintWriter writer = new PrintWriter(osWriter)) {
                    writer.println("我是第一行");
                    writer.print("我的第二行\r\n");
                    writer.printf("%d: 我的第三行\r\n", 1 + 1);
 
                    writer.flush();
                }
            }
        }
    }
}


相关文章
|
7月前
|
存储 Unix Java
文件操作和IO(1)
文件操作和IO(1)
75 0
|
7月前
文件操作与IO(一些小项目)
文件操作与IO(一些小项目)
|
6月前
|
Linux 网络安全 开发工具
【linux】基础IO |文件操作符
【linux】基础IO |文件操作符
48 0
|
6月前
|
Java
文件操作与IO(3) 文件内容的读写——数据流
文件操作与IO(3) 文件内容的读写——数据流
46 0
|
6月前
|
Java Windows
文件操作和IO(2):Java中操作文件
文件操作和IO(2):Java中操作文件
30 0
|
7月前
|
存储 JSON 安全
Python中的文件操作与文件IO操作
【5月更文挑战第14天】在Python中,文件操作是常见任务,包括读取、写入和处理文件内容。`open()`函数是核心,接受文件路径和模式(如&#39;r&#39;、&#39;w&#39;、&#39;a&#39;、&#39;b&#39;和&#39;+&#39;)参数。本文详细讨论了文件操作基础,如读写模式,以及文件IO操作,如读取、写入和移动指针。异常处理是关键,使用`try-except`捕获`FileNotFoundError`和`PermissionError`等异常。进阶技巧涉及`with`语句、`readline()`、`os`和`shutil`模块。数据序列化与反序列化方面,介绍了
68 0
|
7月前
|
Unix Linux 开发工具
【探索Linux】P.11(基础IO,文件操作)
【探索Linux】P.11(基础IO,文件操作)
43 0
|
7月前
|
Java
文件操作与IO(2)
文件操作与IO(2)
|
7月前
|
存储 Unix Java
文件操作和IO(1)
文件操作和IO(1)
|
4月前
|
存储 Java
【IO面试题 四】、介绍一下Java的序列化与反序列化
Java的序列化与反序列化允许对象通过实现Serializable接口转换成字节序列并存储或传输,之后可以通过ObjectInputStream和ObjectOutputStream的方法将这些字节序列恢复成对象。