Java进阶-IO(2)

话接上回,继续java IO部分的学习。上一次说完了字节流的读写数据,这次介绍一下字符流的读写数据。
在这里插入图片描述

一、字符流及其读/写数据

1、字符流

1.1 概述

1)背景
由于字节流操作中文不是特别方便,所以java就提供了字符流。

字符流=字节流+编码表(即字符流的底层还是字节流)

2)问题:用字节流复制文本文件,文本文件中也有中文,但是不会出现编码问题的原因?如何识别是中文?

最终底层操作会自动进行字节拼接成中文。

识别中文:汉字在存储时无论选择哪种编码存储,第一个字节都是负数。

3)一个汉字存储(不同编码占用字节数不同)

- 采用GBK编码,占用2个字节
- UTF-8编码,占用3个字节注:getBytes()方法:得到字符对应的字节数组,如:
String s="abc";
byte[] bys=s.getBytes();
System.out.println(Arrays.toString(bys));  //Arrays工具类的toString方法,打印结果为[97,98,99]
1.2 字符编码(简单了解)

1)概述

ISO8859-1:属于单字节编码,最多只能表示 0~255 的字符范围。
GBK/GB2312:中文的国标编码,用来表示汉字,属于双字节编码。GBK 可以表示简体中文和繁体中文,而 GB2312 只能表示简体中文(GBK 兼容 GB2312Unicode:是一种编码规范,是为解决全球字符通用编码而设计的。UTF-8UTF-16 是这种规范的一种实现,该编码不兼容 ISO8859-1 编码。Java 内部采用此编码。
UTFUTF 编码兼容了 ISO8859-1 编码,同时也可以用来表示所有的语言字符,但 UTF 编码是不定长编码,每一个字符的长度为 1~6 个字节不等(一般在中文网页中使用此编码,可以节省空间)

2)字符串中的编码解码

注:按哪种编码存储(编码),就必须按该种编码解析(解码),否则会乱码

编码(按某种规则,将字符存储到计算机中)

byte[] getBytes():使用平台默认字符集将该String编码为一系列字节,并将结果存储到新的字节数组中。
byte[] getBytes(String charsetName):通过指定的字符集将该String编码为一系列字节,并将结果存储到新的字节数组中

解码(将储存在计算机中的二进制数按照某种规则解析显示)

String(byte[] bytes):通过使用平台的默认字符集解码指定的字节数组来构造新的String
String(bytes,String charsetName):通过指定的字符集解码指定的字节数组来构造新的String

3)字符流中的编码解码
字符流抽象基类(父类)

Reader:字符输入流的抽象类
Writer:字符输出流的抽象类

字符流中与编码解码相关的两个类

转换流:将字节流转换为字符流

InputStreamReader(常用构造方法:2个)
- InputStreamReaderInputStream in) // 默认字符集
- InputStreamReaderInputStream in,String charsetName) // 指定字符集OutputStreamWriter(常用构造方法:2个)
- OutputStreamWriterOutputStream out)
- OutputStreamWriterOutputStream out,String charsetName)

示例1(字符串中的编码解码)

import java.io.UnsupportedEncodingException;
import java.util.Arrays; // 导入Arrays操作类public class StringDemo {public static void main(String[] args) throws UnsupportedEncodingException {String s="中国"; // 创建一个字符串对象/*编码1.byte[] getBytes()2.byte[] getBytes(String charsetName)*/byte[] bys = s.getBytes(); // getBytes()方法返回一个字节数组,ctrl+alt+v快捷键:生成左边System.out.println(Arrays.toString(bys)); // [-28, -72, -83, -27, -101, -67],一个汉字占3字节,说明是UTF-8编码byte[] bys1 = s.getBytes("UTF-8"); // [-28, -72, -83, -27, -101, -67]System.out.println(Arrays.toString(bys1)); // 调用Arrays工具类中的toString()方法,返回对象的字符串显示byte[] bys2 = s.getBytes("GBK");System.out.println(Arrays.toString(bys2)); // [-42, -48, -71, -6], GBK编码,一个汉字占2个字节/*解码1.String(byte[] bytes)2.String(bytes,String charsetName)*/String ss= new String(bys);String ss1=new String(bys,"UTF-8");String ss2=new String(bys,"GBK"); // bys用UTF-8编码,却用GBK解码,所以输出会乱码String ss3=new String(bys2,"GBK"); // bys2用GBK编码,也用GBK解码,故不会乱码System.out.println(ss); // 中国System.out.println(ss1); // 中国System.out.println(ss2); // 涓  浗System.out.println(ss3); // 中国// 重要结论:按哪种编码存储(编码),就必须按该种编码解析(解码),否则会乱码}
}

运行结果

[-28, -72, -83, -27, -101, -67]
[-28, -72, -83, -27, -101, -67]
[-42, -48, -71, -6]
中国
中国
涓  浗
中国

示例2(字符流中的编码解码)

import java.io.*;public class ConversionStreamDemo {public static void main(String[] args) throws IOException {
//        OutputStreamWriter(OutputStream out) 默认字符编码
//        OutputStreamWriter(OutputStream out,String charsetName) 指定字符编码//        FileOutputStream fos=new FileOutputStream("C:\\Users\\ASUS\\Desktop\\project1\\src\\osw.txt");
//        OutputStreamWriter osw=new OutputStreamWriter(fos);// 操作1:写数据// 创建对象,默认字符编码//OutputStreamWriter osw=new OutputStreamWriter(new FileOutputStream("C:\\Users\\ASUS\\Desktop\\project1\\src\\osw.txt")); // 平台默认为UTF-8编码// 创建对象,第二种构造方法OutputStreamWriter osw=new OutputStreamWriter(new FileOutputStream("C:\\Users\\ASUS\\Desktop\\project1\\src\\osw.txt"),"GBK");osw.write("中国"); // 写入数据// 释放资源osw.close();// 操作2:读数据// 创建对象(两种构造方法)//InputStreamReader isr=new InputStreamReader(new FileInputStream("C:\\Users\\ASUS\\Desktop\\project1\\src\\osw.txt")); //以GBK编码,UTF-8解码,会出现乱码InputStreamReader isr=new InputStreamReader(new FileInputStream("C:\\Users\\ASUS\\Desktop\\project1\\src\\osw.txt"),"GBK"); //以GBK编码,GBK解码,不乱码// 一次读取一个字符数据int ch;while ((ch= isr.read())!=-1){ // 未到文件末尾,还有数据System.out.print((char) ch); // 强转为字符类型,注意不要加ln}// 释放资源isr.close();}
}

运行结果

中国
1.3 字符流读数据

层级关系(父–>子):Reader(抽象类)---->InputStreamReader---->FileReader(都在java.io包下)

1)Reader类常用子类

Reader 类是所有字符流输入类的父类,常用子类:(参考JDK帮助文档)
- CharArrayReader 类:将字符数组转换为字符输入流,从中读取字符。
- StringReader 类:将字符串转换为字符输入流,从中读取字符。
- BufferedReader 类:为其他字符输入流提供读缓冲区。
- PipedReader 类:连接到一个 PipedWriter- InputStreamReader 类:将字节输入流转换为字符输入流,可以指定字符编码。-InputStream 类相同,在 Reader 类中也包含 close()mark()skip()reset() 等方法,可参考 InputStream 类的方法

2)Reader类中的read()方法(重载–3个)

int read()    从输入流中读取一个字符,并把它转换为 0~65535 的整数。如果返回 -1, 则表示已经到了输入流的末尾。
为了提高 I/O 操作的效率,通常使用以下两种 read()方法
int read(char[] cbuf)    从输入流中读取若干个字符,并把它们保存到参数 cbuf 指定的字符数组中。 该方法返回读取的字符数,如果返回 -1,则表示已经到了输入流的末尾
int read(char[] cbuf,int off,int len)    从输入流中读取若干个字符,并把它们保存到参数 cbuf 指定的字符数组中。其中,off 指定在字符数组中开始保存数据的起始下标,len 指定读取的字符数。该方法返回实际读取的字符数,如果返回 -1,则表示已经到了输入流的末尾

示例

import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStreamReader;public class InputStreamReaderDemo {public static void main(String[] args) throws IOException {// 创建一个默认字符集的InputStreamReader对象InputStreamReader isr=new InputStreamReader(new FileInputStream("D:\\Ultimate JavaCode\\src\\test6\\文本复制案例.txt"));// 读数据
//        方法1
//        int ch;
//        while ((ch= isr.read())!=-1){ // 是否读到文件末尾,即判断文件数据是否全部读完
//            System.out.print((char) ch); // 强转为字符类型输出显示在控制台上
//        }
//        方法2char[] chs=new char[1024]; // 大小为1024的整数倍int len;while ((len=isr.read(chs))!=-1){ // read(byte[] b)方法System.out.print(new String(chs,0,len)); // 转化为字符串对象输出显示在控制台}// 释放资源isr.close();}
}

3)字符文件输入流
FileReader类(构造方法–2个重载)

FileReader(File file):在给定要读取数据的文件的情况下创建一个新的 FileReader 对象。其中,file 表示要从中读取数据的文件。
FileReader(String fileName):在给定从中读取数据的文件名的情况下创建一个新 FileReader 对象。其中,fileName 表示要从中读取数据的文件的名称,表示的是一个文件的完整路径。注:在创建 FileReader 对象时若引发 FileNotFoundException 异常,需要使用 try catch 语句捕获该异常。

示例(使用字符流复制java文件)
用转换流InputStreamReader和OutputStreamWriter实现字符流复制java文件

转换流作用:将字节流转换为字符流

import java.io.*;// 实现字符流复制java文件
public class FileReaderDemo {public static void main(String[] args) throws IOException {InputStreamReader isr=new InputStreamReader(new FileInputStream("D:\\Ultimate JavaCode\\ConversionStreamDemo.java"));OutputStreamWriter isw=new OutputStreamWriter(new FileOutputStream("D:\\Ultimate JavaCode\\Copy.java"));// 1.一次读取一个字符数据
//        int ch;
//        while ((ch=isr.read())!=-1){ // 读数据
//            isw.write(ch); // 复制文件
//        }// 2.一次读取一个字符数组数据char[] chs=new char[1024]; // 1024的整数倍int len;while((len=isr.read(chs))!=-1){isw.write(chs,0,len);}// 释放资源isw.close();isr.close();}
}

由于转换流创建对象代码较冗长,可以使用其子类FileReader和FileWriter实现

但若要想解决编码问题,一定要用转换流,因为其在创建时可以指定编码类型

import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
// FileReader和FileWriter类分别为InputStreamReader和OutputStreamWriter的子类(继承),因此可以调用其方法
public class FileReaderDemo1 {public static void main(String[] args) throws IOException {// 根据数据源创建字符输入流对象FileReader fr=new FileReader("D:\\Ultimate JavaCode\\ConversionStreamDemo.java");// 根据目的地创建字符输出流对象FileWriter fw=new FileWriter("D:\\Ultimate JavaCode\\Copy1.java");// 1.一次读取一个字符数据
//        int ch;
//        while ((ch=fr.read())!=-1){ // 读数据
//            fw.write(ch); // 写入复制
//        }// 2.一次读取一个字符数组char[] chs=new char[1024]; // 1024的整数倍int len;while((len=fr.read(chs))!=-1){fw.write(chs,0,len);}// 释放资源fw.close();fr.close();}
}

4)字符缓冲区输入流
BufferedReader 类

主要用于辅助其他字符输入流,它带有缓冲区,可以先将一批数据读到内存缓冲区。接下来的读操作就可以直接从缓冲区中获取数据,而不需要每次都从数据源读取数据并进行字符编码转换,可提高数据的读取效率。

构造方法(重载–2个)

BufferedReader(Reader in):创建一个 BufferedReader 来修饰参数 in 指定的字符输入流
BufferedReader(Reader in,int size):创建一个 BufferedReader 来修饰参数 in 指定的字符输入流,参数 size 则用于指定缓冲区的大小,单位为字符。

示例(字符缓冲流)

import java.io.*;public class BufferedStreamDemo {public static void main(String[] args) throws IOException {// 创建字符缓冲输出流对象
//        BufferedWriter bw=new BufferedWriter(new FileWriter("D:\\Ultimate JavaCode\\src\\test6\\bw.txt"));
//        // 写数据
//        bw.write("hello\n"); // 添加换行符换行
//        bw.write("javaee\n");
//        // 释放资源
//        bw.close();// 创建字符缓冲输入流对象BufferedReader br=new BufferedReader(new FileReader("D:\\Ultimate JavaCode\\src\\test6\\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.println(new String(chs,0,len)); // 转换为字符串类型输出显示在控制台上}// 释放资源br.close();}
}

运行结果

hello
javaee
1.4 字符流写数据

层级关系(父–>子):Writer(抽象类)---->OutputStreamWriter---->FileWriter(都在java.io包下)

1)Writer类常用子类

Writer 类是所有字符输出流的父类,常用子类:
- CharArrayWriter 类:向内存缓冲区的字符数组写数据。
- StringWriter 类:向内存缓冲区的字符串(StringBuffer)写数据。
- BufferedWriter 类:为其他字符输出流提供写缓冲区。
- PipedWriter 类:连接到一个 PipedReader- OutputStreamWriter 类:将字节输出流转换为字符输出流,可以指定字符编码。-OutputStream 类相同,Writer 类也包含 close()flush() 等方法,可参考 OutputStream 类的方法

2)FileWriter类(用于写入字符文件,重载–4个)

FileWriter(File file):在指定 File 对象的情况下构造一个 FileWriter 对象。
FileWriter(File file,boolean append):在指定 File 对象的情况下构造一个 FileWriter 对象,如果 append 的值为 true,则将字节写入文件末尾,而不是写入文件开始处。
FileWriter(String fileName):在指定文件名的情况下构造一个 FileWriter 对象。其中,fileName 表示要写入字符的文件名,表示的是完整路径。
FileWriter(String fileName,boolean append):在指定文件名以及要写入文件的位置的情况下构造 FileWriter 对象。其中,append 是一个 boolean 值,如果为 true,则将数据写入文件末尾,而不是文件开始处

3)字符缓冲输出流
BufferedWriter 类

主要用于辅助其他字符输出流,同样带有缓冲区,可以先将一批数据写入缓冲区,当缓冲区满了后再将缓冲区的数据一次性写到字符输出流,其目的是为了提高数据的写效率。

构造方法(重载–2个)

BufferedWriter(Writer out):创建一个 BufferedWriter 来修饰参数 out 指定的字符输出流,默认大小。
BufferedWriter(Writer out,int size):创建一个 BufferedWriter 来修饰参数 out 指定的字符输出流,参数 size 则用于指定缓冲区的大小,单位为字符。
- BufferedWriter 类的使用与 FileWriter 类相同,不再重述。

5)字符流写数据的五种方式

write(int c)  写一个字符
write(char[] cbuf)  写入一个字符数组
write(char[] cbuf,int off,int len)  写入字符数组的一部分
write(String str)  写一个字符串
write(String str,int off,int len)  写一个字符串的一部

注:关于flush()和 close()方法

flush():刷新流,还能继续写数据
close():关闭流之前先刷新流,一旦关闭后不能再写数据

示例(字符流写数据)

import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStreamWriter;
// OutputStreamWrite类:将字节输出流转换为字符输出流,可以指定字符编码。
public class OutputStreamWriteDemo {public static void main(String[] args) throws IOException {// 创建OutputStreamWriter对象(使用默认字符编码)OutputStreamWriter osw=new OutputStreamWriter(new FileOutputStream("D:\\Ultimate JavaCode\\src\\test6\\osw_2_12"));/* 写入数据(5种方式,重载)write(int c)  写一个字符write(char[] cbuf)  写入一个字符数组write(char[] cbuf,int off,int len)  写入字符数组的一部分write(String str)  写一个字符串write(String str,int off,int len)  写一个字符串的一部*///        osw.write(97);
//        // 字符流写数据不能直接写到文件中,因为最终要通过字节流FileOutputStream来写,还存储在缓冲区里面
//        osw.flush(); // 刷新流,将数据从缓冲区刷新入文件,还能继续写数据
//        osw.write(98);
//        osw.flush();
//        osw.write(99);
//        osw.close(); // 关闭流之前先刷新流,一旦关闭后不能再写数据。整个结果:abc//        char[] chs={'a','b','c','d','e'};
//        osw.write(chs); // abcde
//        osw.write(chs,0,chs.length); // abcde
//        osw.write(chs,1,3); // bcd//        osw.write("abcdef"); // abcdefosw.write("abcdef",1,3); // bcd// 释放资源osw.close();}
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/505506.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

AXI4—FULL

文章目录 手册解读AXI—FULL协议特性AXI—FULL架构信号描述代码编写手册解读 AXI—FULL协议特性 支持的主要功能: 数据通道与地址通道分离支持不对齐传输,使用数据掩码实现突发传输只需要一个突发基地址读写通道分离AXI—FULL架构 关于读操作 关于写操作

SpringMVC自定义视图解析器

/** * 使用View接口完成请求转发|重定向 * 解释: * SpringMVC的官方,提供了一个叫做View的接口,告诉开发人员 * DispatcherServlet底层会调用View接口的实例化对象中的逻辑方法 * 来完成对应的请求转发和重定向。 * 使用: * 1. 单元方法的返回值为View接…

把Anaconda添加进环境变量的方法(解决pip识别不到环境的问题)

找到你的Anaconda的安装根目录 比如我的是在:C:\ProgramData\Anaconda3 那么只需要将以下目录添加进环境变量即可: C:\ProgramData\Anaconda3C:\ProgramData\Anaconda3\ScriptsC:\ProgramData\Anaconda3\Library\binC:\ProgramData\Anaconda3\condabin…

取送货问题(Pickup and Delivery Problem)

取送货问题及其变体 广义取送货问题(General Pickup and Delivery Problems,GPDP)可以分为两类: Vehicle Routing Problems with Backhauls,VRPB:从配送中心(depot)取货运输货物到客…

备战蓝桥杯---动态规划的一些思想1

话不多说,直接看题: 目录 1.双线程DP 2.正难则反多组DP 3.换个方向思考: 1.双线程DP 可能有人会说直接贪心:先选第1条的最优路径,再选第2条最优路径。 其实我们再选第1条时,我们怎么选会对第2条的路径…

在线开源免费问卷调查系统

在线开源免费问卷调查系统 平台简介 本项目旨在提供一个简单易用的问卷调查平台,帮助用户创建、分享问卷,并收集、分析调查数据。我们希望能够为各行各业的调查需求提供一种高效、便捷的解决方案。 项目特点 用户友好:清晰直观的用户界面…

华为数通方向HCIP-DataCom H12-821题库(多选题:21-40)

第21题 管理员在配置 VRRP 时,下面哪些不是必须配置的? A.抢占模式 B.抢占延时 C.虚拟IP 地址 D.虚拟路由器的优先级 【参考答案】ABD 【答案解析】 VRRP的作用之一是提供一个虚拟的IP地址,用作默认网关,用来实现冗余和故障转移。因此,配置虚拟IP地址是必须的。华为设备vr…

深入sizeof与strlen

一、sizeof与strlen的对比 sizeofstrlensizeof是单目操作符strlen是库函数,使用需要包含头文件string.hsizeof计算操作数所占用的内存,单位是字节strlen是求字符串长度,统计的是\0之前字符的个数不关注内存中存放什么数据 关注内存总是否有\0…

数据结构开篇

目录 一. 如何学好数据结构二. 基本概念和术语2.1 区分数据、数据元素、数据项、数据对象2.2 数据结构2.2.1 逻辑结构2.2.2 存储结构 2.3 数据类型和抽象数据类型2.4 抽象数据类型的实现 \quad 一. 如何学好数据结构 勤于思考;多做练习;多上机;善于寻求帮助;不怕困难&#xff…

基于springboot+vue的计算机课程管理平台(前后端分离)

博主主页:猫头鹰源码 博主简介:Java领域优质创作者、CSDN博客专家、阿里云专家博主、公司架构师、全网粉丝5万、专注Java技术领域和毕业设计项目实战,欢迎高校老师\讲师\同行交流合作 ​主要内容:毕业设计(Javaweb项目|小程序|Pyt…

python爬虫之selenium知识点记录

selenium 一、前期准备 1、概述 selenium本身是一个自动化测试工具。它可以让python代码调用浏览器。并获取到浏览器中加载的各种资源。 我们可以利用selenium提供的各项功能。 帮助我们完成数据的抓取。 2、学习目标 掌握 selenium发送请求,加载网页的方法 掌…

划分开始结束位置设置标记

划分开始结束位置 初始音轨如下图所示 在想开始地方单击左键,长按直到你想要的结束位置松开。就可以划分开始和结束位置 设置标记 方式1 :直接点击该图标 方式二:使用快捷键M 设置标记点可以自定义名称方便检索标记点