蓝桥杯Java课程学习——IO(三)
文章目录
字符流
字符流以字符为单位,根据码表映射字符,一次可能读多个字节,只能处理字符类型的数据。
java.io 包中专门用于字符流处理的类,是以 Reader 和 Writer 为基础派生的一系列类。
同类 InputStream 和 OutputStream 一样,Reader 和 Writer 也是抽象类,只提供了一系列用于字符流处理的接口。它们的方法与类 InputStream 和 OutputStream 类似,只不过其中的参数换成字符或字符数组。
基类 Reader 的方法
方法 | 返回值 |
---|---|
close() | void |
mark (int readAheadLimit) | void |
markSupported() | boolean |
read() | int |
read(char[] cbuf, int off,int len) | int |
ready() | boolean |
reset() | void |
skip(long n) | long |
基类Writer 的方法
方法 | 返回值 |
---|---|
close() | void |
flush() | void |
write(char[] cbuf) | void |
write(char[] cbuf, int off,int len) | void |
write(int c) | void |
write(String str) | void |
write(String str, int off, int len) | void |
其他的类。
- 对字符数组进行处理: CharArrayReader、CharArrayWrite。
- 对文本文件进行处理:FileReader、FileWriter。
- 对字符串进行处理:StringReader、StringWriter。
- 过滤字符流:FilterReader、FileterWriter。
- 管道字符流:PipedReader、PipedWriter。
- 行处理字符流:LineNumberReader。
- 打印字符流:PrintWriter。
转换流
InputStreamReader 和 OutputStreamWriter 是java.io
包中用于处理字符流的最基本的类,用来在字节流和字符流之间作为中介:从字节输入流读入字节,并按编码规范转换为字符;往字节输出流写字符时先将字符按编码规范转换为字节。使用这两者进行字符处理时,在构造方法中应指定一定的平台规范,以便把以字节方式表示的流转换为特定平台上的字符表示。
InputStreamReader(InputStream in); //缺省规范说明
//指定规范 enc
InputStreamReader(InputStream in, String enc);
OutputStreamWriter(OutputStream out); //缺省规范说明
//指定规范 enc
OutputStreamWriter(OutputStream out, String enc);
如果读取的字符流不是来自本地时(比如网上某处与本地编码方式不同的机器),那么在构造字符输入流时就不能简单地使用缺省编码规范,而应该指定一种统一的编码规范“ISO 8859_1”,这是一种映射到 ASCCII 码的编码方式,能够在不同平台之间正确转换字符。
InputStreamReader ir = new InputStreamReader(is,"8859_1");
缓冲流
类 BufferedInputStream
和 BufferedOutputStream
实现了带缓冲的过滤流,它提供了缓冲机制,把任意的 I/O 流“捆绑”到缓冲流上,可以提高 I/O 流的读取效率。
在初始化时,除了要指定所连接的 I/O 流之外,还可以指定缓冲区的大小。缺省时是用 32 字节大小的缓冲区;最优的缓冲区大小常依赖于主机操作系统、可使用的内存空间以及机器的配置等;一般缓冲区的大小为内存页或磁盘块等的整数倍。
BufferedInputStream 的数据成员 buf
是一个位数组,默认为 2048 字节。当读取数据来源时例如文件,BufferedInputStream 会尽量将buf
填满。当使用 read() 方法时,实际上是先读取buf
中的数据,而不是直接对数据来源作读取。当buf
中的数据不足时,BufferedInputStream 才会再实现给定的 InputStream 对象的read()
方法,从指定的装置中提取数据。
BufferedOutputStream 的数据成员buf
是一个位数组,默认为 512 字节。当使用write()
方法写入数据时,实际上会先将数据写至buf
中,当buf
已满时才会实现给定的 OutputStream 对象的 write()
方法,将 buf
数据写至目的地,而不是每次都对目的地作写入的动作。
对于 BufferedOutputStream,只有缓冲区满时,才会将数据真正送到输出流,但可以使用 flush() 方法人为地将尚未填满的缓冲区中的数据送出。
构造方法
//[ ]里的内容代表可选参数
BufferedInputStream(InputStream in [, int size])
BufferedOutputStream(OutputStream out [, int size])
BufferedReader 和 BufferedWrite
同样的,为了提高字符流处理的效率,java.io 中也提供了缓冲流 BufferedReader 和 BufferedWrite。其构造方法与 BufferedInputStream 和 BufferedOutPutStream 相类似。另外,除了 read() 和 write() 方法外,它还提供了整行字符处理方法:
- public String readLine():BufferedReader 的方法,从输入流中读取一行字符,行结束标志
\n
、\r
或者两者一起(这是根据系统而定的) - public void newLine():BufferedWriter 的方法,向输出流中写入一个行结束标志,它不是简单地换行符
\n
或\r
,而是系统定义的行隔离标志(line separator)。
数据流
接口DataInput
和 DataOutput
,设计了一种较为高级的数据输入输出方式:除了可处理字节和字节数组外,还可以处理int
、float
、boolean
等基本数据类型,这些数据在文件中的表示方式和它们在内存中的一样,无须转换,如 read()
, readInt()
, readByte()
…; write()
, writeChar()
, writeBoolean()
… 此外,还可以用 readLine()
方法读取一行信息。
常用方法
方法 | 返回值 | 说明 |
---|---|---|
readBoolean() | boolean | |
readByte() | byte | |
readShort() | short | |
readChar() | char | |
readInt() | int | |
readLong() | long | |
readDouble() | double | |
readFloat() | float | |
readUnsignedByte() | int | |
readUnsignedShort() | int | |
readFully(byte[] b) | void | 从输入流中读取一些字节,并将它们存储在缓冲区数组 b 中 |
reaFully(byte[] b, int off,int len) | void | 从输入流中读取 len 个字节 |
skipBytes(int n) | int | 与 InputStream.skip 等价 |
readUTF() | String | 按照 UTF-8 形式从输入中读取字符串 |
readLine() | String | 按回车 (\r) 换行 (\n) 为分割符读取一行字符串,不完全支持 UNICODE |
writeBoolean(boolean v) | void | |
writeByte(int v) | void | |
writeShort(int v) | void | |
writeChar(int v) | void | |
writeInt(int v) | void | |
writeLong(long v) | void | |
writeFloat(float v) | void | |
writeDouble(double v) | void | |
write(byte[] b) | void | 与 OutputStream.write 同义 |
write(byte[] b, int off, int len) | void | 与 OutputStream.write 同义 |
write(int b) | void | 与 OutputStream.write 同义 |
writeBytes(String s) | void | 只输出每个字符的低 8 位;不完全支持 UNICODE |
writeChars(String s) | void | 每个字符在输出中都占两个字节 |
数据流类 DataInputStream
和 DataOutputStream
的处理对象除了是字节或字节数组外,还可以实现对文件的不同数据类型的读写:
- 分别实现了
DataInput
和DataOutput
接口。 - 在提供字节流的读写手段同时,以统一的形式向输入流中写入
boolean
,int
,long
,double
等基本数据类型,并可以再次把基本数据类型的值读取回来。 - 提供了字符串读写的手段。
数据流可以连接一个已经建立好的数据对象,例如网络连接、文件等。数据流可以通过如下方式建立:
FileInputStream fis = new FileInputStream("file1.txt");
FileOutputStream fos = new FileOutputStream("file2.txt");
DataInputStream dis = new DataInputStream(fis);
DataOutputStream dos = new DataOutputStream(fos);
实例
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
public class DataStream {
public static void main(String[] args) throws IOException{
//向文件 a.txt 写入
FileOutputStream fos = new FileOutputStream("a.txt");
DataOutputStream dos = new DataOutputStream(fos);
try {
dos.writeBoolean(true);
dos.writeByte((byte)123);
dos.writeChar('J');
dos.writeDouble(3.1415926);
dos.writeFloat(2.122f);
dos.writeInt(123);
}
finally {
dos.close();
}
//从文件 a.txt 读出
FileInputStream fis = new FileInputStream("a.txt");
DataInputStream dis = new DataInputStream(fis);
try {
System.out.println("\t" + dis.readBoolean());
System.out.println("\t" + dis.readByte());
System.out.println("\t" + dis.readChar());
System.out.println("\t" + dis.readDouble());
System.out.println("\t" + dis.readFloat());
System.out.println("\t" + dis.readInt());
}
finally {
dis.close();
}
}
}
读写对象
可以通过 ObjectOutputStream 和 ObjectInputStream 将对象输入输出。 将对象的状态信息转换为可以存储或者传输的形式的过程又叫序列化。
import java.io.*;
public class ReadWriteObject{
public static void main(String[] args){
File file = new File("/home/project/user.file");
try(ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream(file))){
oos.writeObject(new User("shiyanlou", "password"));
oos.flush();
} catch(IOException e){
e.printStackTrace();
}
try(ObjectInputStream ois = new ObjectInputStream(new FileInputStream(file))){
User user = (User) ois.readObject();
System.out.println(user.toString());
} catch(IOException | ClassNotFoundException e){
e.printStackTrace();
}
}
//静态内部类 必须实现Serializable
static class User implements Serializable {
private String username;
private String password;
public User(String username, String password) {
this.username = username;
this.password = password;
}
@Override
public String toString() {
return "User{" +
"username='" + username + '\'' +
", password='" + password + '\'' +
'}';
}
}
}
要被序列化的对象必须继承Serializable
类
NIO
Java NIO(New IO) 发布于 JDK1.4,用于代替 Java 标准 IO 。Java NIO 是面向缓存的、非阻塞的 IO,而标准 IO 是面向流的,阻塞的 IO。
Buffer(缓冲区)
- NIO 读取或者写入数据都要通过 Buffer
- 通过 allocate() 方法分配 Buffer,Buffer 不可实例化,Buffer 是抽象类,需要使用具体的子类,比如 ByteBuffer。
- Buffer 的参数:
capacity
:缓冲区的容量position
:当前指针位置,没读取一次缓冲区数据或者写入缓冲区一个数据那么指针将会后移一位limit
:限制指针的移动,指针不能读取 limit 之后的位置mark
:如果设置该值,那么指针将移动到 0 - position 的位置
最后可以这几个参数的关系如下:mark <= position <= limit <= capacity
实例
import java.io.File;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;
import java.nio.charset.Charset;
import java.nio.charset.CharsetDecoder;
import java.util.Scanner;
public class NioDemo {
public static void main(String[] args) {
try {
File file = new File("/home/project/nio.txt");
if (!file.exists()) {
file.createNewFile();
}
//创建channel nio通过channel来连接文件 相当于桥梁
FileChannel writeChannel = new RandomAccessFile(file, "rw").getChannel();
//创建一个ByteBuffer 容量为100
ByteBuffer byteBuffer = ByteBuffer.allocate(100);
System.out.println("请输入字符串");
Scanner in = new Scanner(System.in);
String s = in.nextLine();
//将字符串写入到缓冲区
byteBuffer.put(s.getBytes());
System.out.println("写入数据后指针变化-position:" + byteBuffer.position() + " limit:" + byteBuffer.limit() + " capacity :" + byteBuffer.capacity());
//为输出数据做准备 将limit移动到position位置,position置0
byteBuffer.flip();
System.out.println("flip后指针变化-position:" + byteBuffer.position() + " limit:" + byteBuffer.limit() + " capacity :" + byteBuffer.capacity());
//将缓冲区写入channel
writeChannel.write(byteBuffer);
//清除缓冲区 为下次写入或者读取数据做准备 恢复到初始状态 position=0 limit=capacity=100 因为我们这里分配的容量大小为100
byteBuffer.clear();
System.out.println("clear后指针变化-position:" + byteBuffer.position() + " limit:" + byteBuffer.limit() + " capacity :" + byteBuffer.capacity());
//关闭channel
writeChannel.close();
FileChannel readChannel = new RandomAccessFile(file, "r").getChannel();
//从channel中将数据读取到缓冲区
while (readChannel.read(byteBuffer) != -1) {
//为读取数据做准备
byteBuffer.flip();
//输出数据 设置解码器
Charset charset = Charset.forName("UTF-8");
CharsetDecoder decoder = charset.newDecoder();
System.out.println("读取结果:" + decoder.decode(byteBuffer));
//清除缓冲区
byteBuffer.clear();
}
readChannel.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
/*
结果:
请输入字符串
Helloboys
写入数据后指针变化-position:9 limit:100 capacity :100
flip后指针变化-position:0 limit:9 capacity :100
clear后指针变化-position:0 limit:100 capacity :100
Helloboys
*/