JavaIO流之字符流与字符缓冲流
为什么会出现字符流?
如果使用字节流,把文本文件的中文,读到内存中,有可能会出现乱码。
如果使用字节流,把中文写到文本文件的,有可能会出现乱码。
因为字节流一次只能读一个字节,而中文不论是GBK,还是UTF-8都是多个字节的,所以使用字节流每次只能读取其中的一部分,所以就会导致乱码的问题。
所以需要使用字符流
编码表
字符集
-
什么是字符集
是一个系统支持的所有字符的集合,包括各国家文字、标点符号、图形符号、数字等
l计算机要准确的存储和识别各种字符集符号,就需要进行字符编码,一套字符集必然至少有一套字符编码。常见字符集有ASCII字符集、GBXXX字符集、Unicode字符集等
-
常见的字符集
-
ASCII字符集:
ASCII:是基于拉丁字母的一套电脑编码系统,用于显示现代英语,主要包括控制字符(回车键、退格、换行键等)和可显示字符(英文大小写字符、阿拉伯数字和西文符号)
基本的ASCII字符集,使用7位表示一个字符,共128字符。ASCII的扩展字符集使用8位表示一个字符,共256字符,方便支持欧洲常用字符。是一个系统支持的所有字符的集合,包括各国家文字、标点符号、图形符号、数字等
-
GBXXX字符集:
GBK:最常用的中文码表。是在GB2312标准基础上的扩展规范,使用了双字节编码方案,共收录了21003个汉字,完全兼容GB2312标准,同时支持繁体汉字以及日韩汉字等
-
Unicode字符集:
UTF-8编码:可以用来表示Unicode标准中任意字符,它是电子邮件、网页及其他存储或传送文字的应用 中,优先采用的编码。互联网工程工作小组(IETF)要求所有互联网协议都必须支持UTF-8编码。它使用一至四个字节为每个字符编码
编码规则:
128个US-ASCII字符,只需一个字节编码
拉丁文等字符,需要二个字节编码
大部分常用字(含中文),使用三个字节编码
其他极少使用的Unicode辅助字符,使用四字节编码
-
码表发展简图
汉字存储的过程
重点:
在windows中,默认使用的是GBK码表,一个字符两个字节。
而在idea 和 以后的工作中默认使用Unicode的UTF-8编码格式,一个中文3个字节
字符串中的编码解码问题
编码的相关方法
方法名 | 说明 |
---|---|
byte[] getBytes() | 使用平台的默认字符集将该 String编码为一系列字节 |
byte[] getBytes(String charsetName) | 使用指定的字符集将该 String编码为一系列字节 |
解码的相关方法
方法名 | 说明 |
---|---|
String(byte[] bytes) | 使用平台的默认字符集解码指定的字节数组来创建字符串 |
String(byte[] bytes, String charsetName) | 通过指定的字符集解码指定的字节数组来创建字符串 |
Coding
public class OutputDemo03 {
public static void main(String[] args) throws UnsupportedEncodingException {
String s = "字符集的使用";
byte[] byte1 = s.getBytes();
System.out.println(Arrays.toString(byte1));
byte[] byte2 = s.getBytes("GBK");
System.out.println(Arrays.toString(byte2));
System.out.println(new String(byte1));
System.out.println(new String(byte2,"GBK"));
}
}
字符流
字符流介绍
由于字节流操作中文不是特别的方便,所以Java就提供字符流
字符流 = 字节流 + 编码表
中文的字节存储方式
用字节流复制文本文件时,文本文件也会有中文,但是没有问题,原因是最终底层操作会自动进行字节拼接成中文
如何识别是中文的呢?
汉字在存储的时候,无论选择哪种编码存储,第一个字节都是负数。所以只要见到中文计算机就知道这一个汉字。
字符流写数据
介绍
Writer: 用于写入字符流的抽象父类
FileWriter: 用于写入字符流的常用子类
构造方法
方法名 | 说明 |
---|---|
FileWriter(File file) | 根据给定的 File 对象构造一个 FileWriter 对象 |
FileWriter(File file, boolean append) | 根据给定的 File 对象构造一个 FileWriter 对象,是否以追加的方式写入。 |
FileWriter(String fileName) | 根据给定的文件名构造一个 FileWriter 对象 |
FileWriter(String fileName, boolean append) | 根据给定的文件名以及指示是否附加写入数据的 boolean 值来构造 FileWriter 对象 |
我们可以看一下FileWriter的源码,可以发现其底层实质上还是使用了字节输出流。
public FileWriter(File file) throws IOException {
super(new FileOutputStream(file));
}
成员方法
方法名 | 说明 |
---|---|
void write(int c) | 写一个字符 |
void write(char[] cbuf) | 写入一个字符数组 |
void write(char[] cbuf, int off, int len) | 写入字符数组的一部分 |
void write(String str) | 写一个字符串 |
void write(String str, int off, int len) | 写一个字符串的一部分 |
Coding
public class OutputDemo04 {
public static void main(String[] args) throws IOException {
//两类构造方法
FileWriter fw = new FileWriter("a.txt");
FileWriter fw2 = new FileWriter(new File("a.txt"));
//1.void write(int c)
fw.write(96);
//2.void write(char[] cbuf)
//3.void write(char[] cbuf, int off, int len)
char[] ch = {97,98,99,100,101,102};
fw.write(ch,0,3);
//4.void write(String str)
//5.void write(String str, int off, int len)
String line = "黑马程序员";
fw.write(line);
fw.close();
}
}
注意:
写出int类型的整数,实际写出的是整数在码表上对应的字符。
写出字符串数据,则是字符串本身原样写出。
刷新和关闭的方法
方法名 | 说明 |
---|---|
flush() | 刷新流,之后还可以继续写数据 |
close() | 关闭流,释放资源,但是在关闭之前会先刷新流。一旦关闭,就不能再写数据 |
即如果在代码中没有close有没有flush,则数据会呆在内存里,使用flush可以将内存中的数据刷写到文件中,但是这个流还是可以继续使用的,并没有被关闭。
字符流读数据
介绍
Reader: 用于读取字符流的抽象父类
FileReader: 用于读取字符流的常用子类
构造方法
方法名 | 说明 |
---|---|
FileReader(File file) | 在给定从中读取数据的 File 的情况下创建一个新 FileReader |
FileReader(String fileName) | 在给定从中读取数据的文件名的情况下创建一个新 FileReader |
成员方法
方法名 | 说明 |
---|---|
int read() | 一次读一个字符数据 |
int read(char[] cbuf) | 一次读一个字符数组数据 |
Coding
public class OutputDemo05 {
public static void main(String[] args) throws IOException {
FileReader fr = new FileReader("myCharStream\\b.txt");
//1.int read();一次读一个字符数据
int ch;
while ((ch=fr.read())!=-1) {
System.out.print((char)ch);
}
//2.int read(char[] cbuf):一次读一个字符数组数据
char[] chs = new char[1024];
int len;
while ((len = fr.read(chs)) != -1) {
System.out.print(new String(chs, 0, len));
}
fr.close();
}
}
案例需求——字符流用户注册案例
将键盘录入的用名和密码保存到本地实现永久化存储
实现步骤
获取用户输入的用户名和密码
将用户输入的用户名和密码写入到本地文件中
关流,释放资源
代码实现
public class CharStreamDemo8 {
public static void main(String[] args) throws IOException {
//需求: 将键盘录入的用户名和密码保存到本地实现永久化存储
//要求:用户名独占一行,密码独占一行
//分析:
//1,实现键盘录入,把用户名和密码录入进来
Scanner sc = new Scanner(System.in);
System.out.println("请录入用户名");
String username = sc.next();
System.out.println("请录入密码");
String password = sc.next();
//2.分别把用户名和密码写到本地文件。
FileWriter fw = new FileWriter("charstream\\a.txt");
//将用户名和密码写到文件中
fw.write(username);
//表示写出一个回车换行符 windows \r\n MacOS \r Linux \n
fw.write("\r\n");
fw.write(password);
//刷新流
fw.flush();
//3.关流,释放资源
fw.close();
}
}
字符缓冲流
介绍
BufferedWriter:将文本写入字符输出流,缓冲字符,以提供单个字符,数组和字符串的高效写入,可以指定缓冲区大小,或者可以接受默认大小。默认值足够大,可用于大多数用途
BufferedReader:从字符输入流读取文本,缓冲字符,以提供字符,数组和行的高效读取,可以指定缓冲区大小,或者可以使用默认大小。 默认值足够大,可用于大多数用途
构造方法
方法名 | 说明 |
---|---|
BufferedWriter(Writer out) | 创建字符缓冲输出流对象 |
BufferedReader(Reader in) | 创建字符缓冲输入流对象 |
使用方式与上一个章节使用的字节缓冲流类似
Coding
public class BufferedStreamDemo01 {
public static void main(String[] args) throws IOException {
//BufferedWriter(Writer out)
BufferedWriter bw = new BufferedWriter(new FileWriter("myCharStream\\bw.txt"));
bw.write("hello\r\n");
bw.write("world\r\n");
bw.close();
//BufferedReader(Reader in)
BufferedReader br = new BufferedReader(new FileReader("myCharStream\\bw.txt"));
//一次读取一个字符数据
// int ch;
// while ((ch=br.read())!=-1) {
// System.out.print((char)ch);
// }
//一次读取一个字符数组数据
char[] chs = new char[1024];
int len;
while ((len=br.read(chs))!=-1) {
System.out.print(new String(chs,0,len));
}
br.close();
}
}
字符缓冲流特有功能(独有!)
BufferedWriter:
方法名 | 说明 |
---|---|
void newLine() | 写一行行分隔符,行分隔符字符串由系统属性定义 |
相当于回车换行符。
BufferedReader:
方法名 | 说明 |
---|---|
String readLine() | 读一行文字。 结果包含行的内容的字符串,不包括任何行终止字符如果流的结尾已经到达,则为null |
Coding
public class BufferedStreamDemo02 {
public static void main(String[] args) throws IOException {
BufferedWriter bw = new BufferedWriter(new FileWriter("myCharStream\\bw.txt"));
for (int i = 0; i < 10; i++) {
bw.write("hello" + i);
bw.newLine();
bw.flush();
}
bw.close();
BufferedReader br = new BufferedReader(new FileReader("myCharStream\\bw.txt"));
String line;
while ((line=br.readLine())!=null) {
System.out.println(line);
}
br.close();
}
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 全程不用写代码,我用AI程序员写了一个飞机大战
· DeepSeek 开源周回顾「GitHub 热点速览」
· 记一次.NET内存居高不下排查解决与启示
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· .NET10 - 预览版1新功能体验(一)