编码表
为什么出现字符流
- 一个汉字存储:
如果是GBK编码,占用2个字节
如果是UTF-8编码,占用3个字节 - 字节流读取字符数据会出现问题,比如使用UTF-8编码的汉字,一个汉字占用3个字节,如果字节一个一个读取,没有办法拼成汉字。
- 由于字节流操作中文不是特别方便,所以java提供了字符流。字符流 = 字节流 + 编码表
- 用字节流复制文本文件时,文本文件也会有中文,但是没有问题,
原因是:最终底层操作会自动进行字节拼接成中文,如何识别是中文呢?汉字在存储的时候,无论选择哪种编码存储,第一个字节都是负数。
字符集
- 计算机中存储的信息都是用二进制数表示的,我们在屏幕上看到的英文、汉字等字符是二进制转换之后的结果
- 按照某种规则,将字符存储到计算机中,称为编码。反之,将存储在计算机中的二进制数按照某种规则解析显示出来,称为解码。注意,按照A编码存储,必须按照A编码解析,这样才能显示正确的文本符号,否则就会导致乱码现象。
- 字符编码:就是一套自然语言的字符与二进制数之间的对应规则(A,65)
- 字符集:是一个系统支持的所有字符的集合,包括各国家文字、标点符号、图形符号、数字等;计算机要准确的存储和识别各种字符集符号,就需要进行字符编码,一套字符集必然至少有一套字符编码。常见字符集有ASCII字符集、GBXXX字符集、Unicode字符集。
ASCII字符集:
- ASCII(American Standard Code for Information Interchange,美国信息交换标准代码):是基于拉丁字母的一套电脑编码系统,用于显示现代英语,主要包括控制字符(回车键、退格、换行键等)和可显示字符(英文大小写字符、阿拉伯数字和西文符号)
- 基本的ASCII字符集,使用7位表示一个字符,共128字符。ASCII的扩展字符集使用8位表示一个字符,共256字符,方便支持欧洲常用字符。是一个系统支持的所有字符的集合,包括各国家文字、标点符号、图形符号、数字等。
GBXXX字符集:
- GB2312:简体中文码表。一个小于127的字符的意义与原来相同,但两个大于127的字符连在一起时,就表示一个汉字,这样大约可以组合了包含7000多个简体汉字。此外数学符号、罗马希腊的字母、日文的假名等都编进去了,连在ASCII里本来就有的数字、标点、字母都统统重新编了两个字节长的编码,这就是常说的“全角”字符,而原来在127号以下的那些就叫“半角”字符了。
- GBK:最常用的中文码表。是在GB2312标准基础上的扩展规范,使用了双字节编码方案,共收录了21003个汉字,完全兼容GB2312标准,同时支持繁体汉字以及日韩汉字等。其中GB2312标准下两个字节都多于0x7f才可以表示一个汉字;
- GB18030:最新的中文码表,收录汉字70244个,采用多字节编码,每个字可以由1个、2个或4个字节组成。支持中国国内少数民族的文字,同时支持繁体汉字以及日韩汉字等。
Unicode字符集:
- 为表达任意语言的任意字符而设计,是业界的一种标准,也称为统一码、标准万国码。它最多使用4个字节的数字来表达每个字母、符号、或者文字。有三种编码方案:UTF-8、UTF-16和UTF-32。最为常用的UTF-8编码。
- UTF-8编码:可以用来表示Unicode标准中任意字符,它是电子邮件、网页及其他存储或传送文字的应用中,优先采用的编码。互联网工作小组(IETF)要求所有互联网协议都必须支持UTF-8编码。它使用一至四个字节为每个字符编码。
- 编码规则:①128个US-ASCII字符,只需一个字节编码;②拉丁文的等字符,需要二个字节编码;③大部分常用字(含中文),使用三个字节编码;④其他极少使用的Unicode辅助字符,使用四字节编码。
字符串中的编码解码问题
编码: 按照字符集将String编码为字节存放到字节数组中
方法名 | 说明 |
---|---|
byte[] getBytes() | 使用平台默认字符集将该String编码为一系列字节,将结果存储到新的字节数组中 |
byte[] getBytes(String charsetName) | 使用指定的字符集将该String编码为一系列字节,将结果存储到新的字节数组中 |
解码: 按照字符集用字节数组构造新的String
方法名 | 说明 |
---|---|
String(byte[] bytes) String构造方法 | 通过使用平台的默认字符集解码指定的字节数组来构建新的String |
String(byte[] bytes,String charsetName) String构造方法 | 通过指定的字符集解码指定的字节数组来构建新的String |
static String toString(byte[] a) Arrays的静态方法 | 返回指定数组的内容的字符串表示形式 |
案例:编解码测试
// 37
public class Demo2 {
public static void main(String[] args) throws UnsupportedEncodingException {
//定义一个字符串
String s = "中国";
// 编码
byte[] bys = s.getBytes();
// 输出内容是:[-28, -72, -83, -27, -101, -67]
# 说明:
// byte[] bys = s.getBytes("UTF-8");
// 使用这个方法 需要抛出一个异常,throws UnsupportedEncodingException
// 输出结果是:[-28, -72, -83, -27, -101, -67]
// 说明idea中编码方式默认是UTF-8
// byte[] bys = s.getBytes("GBK");
// 输出结果是:[-42, -48, -71, -6]
System.out.println(Arrays.toString(bys));
// 解码
// String ss = new String(bys);
// 输出是 中国 编码解码采用的都是idea默认的字符集UTF-8
# 说明:
String ss = new String(bys,"GBK");
// 输出是 涓浗 由于编码解码不匹配 所以导致输出结果和原本文件不一致
System.out.println(ss);
}
}
字符流
字符流中编解码问题
字符流抽象基类:
类名 | 说明 |
---|---|
Reader | 字符输入流的抽象类 |
Writer | 字符输出流的抽象类 |
字符流中和编码解码问题相关的两个类:
说明 | InputStreamReader | OutputStreamWriter |
---|---|---|
所属包 | 在java.io包下,使用的时候需要导包 | 在java.io包下,使用的时候需要导包 |
含义 | 从字节流到字符流的桥梁:它读取字节,并使用指定的charset将其解码为字符。 它使用的字符集可以由名称指定(即被明确指定),或者可以接收平台的默认字符集。 | 从字符流到字节流的桥梁:它读取字符,并使用指定的charset将其编码为字节。 它使用的字符集可以由名称指定(即被明确指定),或者可以接收平台的默认字符集。 |
字符流中的方法介绍
方法名 | 说明 |
---|---|
OutputStreamWriter(OutputStream out) | 创建一个使用默认字符编码的OutputStreamWriter |
OutputStreamWriter(OutputStream out,String charsetName) | 创建一个使用命名字符集的OutputStreamWriter |
InputStreamReader (InputStream in) | 创建一个使用默认字符集的InputStreamReader |
InputStreamReader(InputStream in,String charsetName) | 创建一个使用命名字符集的InputStreamReader |
FileWriter (String name) | 创建一个使用默认字符编码的OutputStreamWriter |
FileReader (String name) | 创建一个使用默认字符编码的InputStreamReader |
为了便捷书写,使用简化字符流名称
转换流名 | 对应子类名 | 说明 |
---|---|---|
InputStreamReader | FileReader(String fileName) | 用于读取字符文件的便捷类 |
OutputStreamWriter | FileWriter(String fileName) | 用于写入字符文件的便捷类 |
案例:
// 37-Demo3
public class Demo3 {
public static void main(String[] args) throws IOException {
/* OutputStreamWriter osw = new OutputStreamWriter(new FileOutputStream(".\\osw.txt"));
osw.write("中国");
osw.close();*/
* 说明:
// OutputStreamWriter 和 write 方法都是用默认编码方式,写入文件的内容是 中国
OutputStreamWriter osw = new OutputStreamWriter(new FileOutputStream(".\\osw.txt"),"GBK");
osw.write("中国");
osw.close();
* 说明:
//OutputStreamWriter 采用GBK方式解码 , write用默认编码方式 UTF-8,写入文件的内容是 乱码
// 查看OutputStreamWriter 类中的write方法,没有参数可以设置编码方式。
* 说明:
// InputStreamReader isr = new InputStreamReader(new FileInputStream(".\\osw.txt"));
// 输出结果 得到乱码,因为编码是 GBK 解码采用默认方式UTF-8
* 说明:
InputStreamReader isr = new InputStreamReader(new FileInputStream(".\\osw.txt"),"GBK");
// 输出结果 是中国。
// 一次读取一个字符数据
int ch;
while((ch = isr.read()) != -1){
System.out.print((char)ch);
}
isr.close();
}
}
字符流写数据(数组/字符串)
方法名 | 说明 |
---|---|
void write(int c) | 写一个字符 |
void write(char[] cbuf) | 写人一个字符数组 |
void write(char[] cbuf,int off,int len) | 写入字符数组的一部分,从off开始,长度为len的数组 |
void write(String str) | 写一个字符串 |
void write(String str,int off,int len) | 写一个字符串的一部分 |
案例:
// 37
public class Demo4 {
public static void main(String[] args) throws IOException {
OutputStreamWriter osw = new OutputStreamWriter(new FileOutputStream(".\\osw.txt"));
# 说明:
// write(char c) 写一个字符
osw.write(97);
// 视频中这样的形式没有将字符写入文件,但是自己操作时却可以将此字符写入。原因是写了osw.close()这个内容。
// 这里我们采用的是字符流写数据,不能直接将字符写入文件,因为它最终需要通过字节流去写数据的,现在它还存在于缓冲区中,通过使用 方法 void flush() 可以将数据从缓冲区中直接写入到文件中去。
# 说明:
void flush():刷新流
// 为什么不使用flush刷新流但是字符可以写进去,原因是:close方法是先刷新,再关闭流。
// 释放资源close方法已经使用了,之后是不能再写数据了。
* 说明:
// write(char[] cbys):写一个字符数组
char[] cbys = {'a','b','c','d','e'};
// osw.write(cbys);
* 说明:
// write(char[] cbys,int offset,int len):写一个字符数组的一部分
// osw.write(cbys, 0, 3);
* 说明:
//write(String str):写一个字符串
// osw.write("xhjab");
* 说明:
// write(String str,int offset,int len):写一个字符串的一部分
osw.write("abcxhj", 3, 3);
// 释放资源
osw.close();
}
}
注意
2022/4/14 |
---|
通过字符流写数据,最终还是要通过字节流去写数据的。 字符流写数据不能直接写在文件中,而是先在缓冲区中,可以通过flush方法写入文件中 也可以不使用flush方法,而是在字符流写数据结束的时候 使用close方法,该方法会先执行flush、再关闭流 |
字符流读数据(数组)
方法名 | 说明 |
---|---|
int read() | 一次读一个字符数据 |
int read(char[] cbug) | 一次读一个字符数组数据 |
理解
字符流读数据的read操作只匹配字符流写数据写一个字节/字节数组/字节数组的一部分。write(int by)、write(byte[] bytes),write(byte[] bytes,int off,int len)
案例:
// 37-Demo5
public class Demo5 {
public static void main(String[] args) throws IOException {
InputStreamReader isr = new InputStreamReader(new FileInputStream(".\\test.txt"));
# 说明:
// int read() :一次读取一个字符
/* int ch;
while((ch = isr.read()) != -1){
System.out.print((char)ch);
}*/
# 说明:
// int read(char[] cbuf):一次读取一个字符数组
char[] cbuf = new char[1024];
int len;
while((len = isr.read(cbuf)) != -1){
System.out.println(new String(cbuf,0,len));
}
isr.close();
}
}
字符流读写数据模板
1 创建字符流输入流对象、字符输出流对象
InputStreamReader isr = new InputStreamReader(new FileInputStream(文件路径));
OutputStreamWriter isw = new OutputStreamWriter(new FileOutputStream(文件路径));
2 一次读写一个字节
int by;
while((by = isr.read()) != -1){
osw.write(by);
// 控制台输出
System.Output.println((char)by);
}
3 一次读写一个字符数组
char[] chs = new char[1024];
int len;
while((len = isr.read(chs)) != -1){
osw.write(chs);
osw.write(chs,o,len);
}
4 字符流关闭
isr.close();
osw.close();
案例:复制java文件
需求:把模块目录下的“xxx”复制到模块目录下的“xxx”
// 37-Demo6
public class Demo6 {
public static void main(String[] args) throws IOException {
InputStreamReader isr = new InputStreamReader(new FileInputStream(".\\Demo5.java"));
OutputStreamWriter osw = new OutputStreamWriter(new FileOutputStream(".\\copy.java"));
// 读一个字符写一个字符
/* int ch;
while ((ch = isr.read()) != -1) {
osw.write(ch);
}*/
// 一次读写一个字符数组数据
char[] chs = new char[1024];
int len;
while((len = isr.read(chs)) != -1){
osw.write(chs,0,len);
}
isr.close();
osw.close();
}
}
案例:复制java文件(改进版FileReader、FileWriter)
需求:把模块目录下的“xxx”复制到模块目录下的“xxx”
分析:转换流的名字较长,为了方便书写,提供了对应的子类。
转换流名 | 对应子类名 | 说明 |
---|---|---|
InputStreamReader | FileReader(String fileName) | 用于读取字符文件的便捷类 |
OutputStreamWriter | FileWriter(String fileName) | 用于写入字符文件的便捷类 |
化简过程
//源地址:原地址路径--读数据--Reader--InputStreamReader--FileReader
InputStreamReader isr = new InputStreamReader(new FileInputStream("文件路径"));
FileReader fr = new FileReader("文件路径");
//目的地址:目的地址路径--写数据--Writer--OutputStreamWriter--FileWriter
OutputStreamWriter osw = new OutputStreamWriter(new FileOutputStream("文件路径"));
FileWriter fw = new FileWriter("文件路径");
案例:
// 37-Demo7
public class Demo7 {
public static void main(String[] args) throws IOException {
FileReader fr = new FileReader(".\\Demo5.java");
FileWriter fw = new FileWriter(".\\copy.java");
char[] chs = new char[1024];
int len;
while((len = fr.read(chs)) != -1){
fw.write(chs,0,len);
}
fr.close();
fw.close();
}
}
字符缓冲流
输入流/输出流
介绍 | 字符缓冲输入流 | 字符缓冲输出流 |
---|---|---|
名字 | BufferedReader | BufferedWriter |
解释 | 从字符输入流读取文本,缓冲字符,以提供字符、数组和行的高效读取。 可以指定缓冲区大小,或者可以使用默认大小,默认值足够大。 | 将文本写入字符输出流,缓冲字符,以提供单个字符、数组和字符串的高效写入。 可以指定缓冲区大小,或者可以接受默认大小,默认值足够大。 |
类声明 | public class BufferedReader extends Reader | public class BufferedWriter extends Writer |
构造方法 | BufferedReader(Reader in) | BufferedWriter (Writer out) |
读数据和写数据
读数据:read(int by) read(byte[] byte)
写数据:write(int by)、write(byte[] bytes)、write(byte[] bytes,int off,int len)、write(String str)、write(String str,int off,int len)
案例:
// 37
public class Demo8 {
public static void main(String[] args) throws IOException {
/* BufferedWriter rw = new BufferedWriter(new FileWriter(".\\rw.txt"));
rw.write("sciens\r\n");
rw.write("technology\r\n");
rw.close();*/
BufferedReader br = new BufferedReader(new FileReader(".\\rw.txt"));
// 一次读写一个字符
/* int ch;
while((ch = br.read()) != -1){
System.out.print((char)ch);
}*/
System.out.println("--------------");
// 一次读取一个字符数组
char[] chs = new char[1024];
int len;
while((len = br.read(chs)) != -1){
System.out.print(new String(chs,0,len));
}
br.close();
}
}
案例:复制java文件(字符缓冲流改进版)
需求:把模块目录下的“xxx”复制到模块目录下的“xxx”
代码:
// 37
public class Demo9 {
public static void main(String[] args) throws IOException {
BufferedReader br = new BufferedReader(new FileReader(".\\Demo5.java"));
BufferedWriter bw = new BufferedWriter(new FileWriter(".\\copy.java"));
/* int ch;
while((ch = br.read()) != -1){
bw.write((char)ch);
}*/
char[] chs = new char[1024];
int len;
while((len = br.read(chs)) != -1){
bw.write(chs,0,len);
}
br.close();
bw.close();
}
}
字符缓冲流特有功能
所属类 | 方法名 | 说明 |
---|---|---|
BufferedWriter | void newLine() | 写一行 行分隔符,行分割符字符串由系统属性定义 |
BufferedReader | public String readLine() | 读一行文字,结果包含行的内容的字符串,不包含任何行终止字符,如果流的结尾已经到达,则为null 感觉这个属性是为了配合OutputStreamWriter类中的Writer的写字符串的方法 |
使用字符缓冲流写数据特殊功能的步骤:
1 创建字符缓冲输入流对象
BufferedReader br = new BufferedReader(new fileReader("文件路径"));
2 创建字符缓冲输出流对象
BufferedWriter bw = new BufferedWriter(new fileWriter("文件路径"));
3 使用readLine读取一行文字,并写入字符输出流对象。
String line;
while((line = br.readline())!= null) {
bw.write(line);
bw.newLine();
bw.flush();
}
案例:复制java文件(readLine)
代码:
// 38-
public class Demo2 {
public static void main(String[] args) throws IOException {
BufferedReader br = new BufferedReader(new FileReader(".\\Demo5.java"));
BufferedWriter bw = new BufferedWriter(new FileWriter(".\\copy.java"));
String line;
while((line = br.readLine()) != null){
bw.write(line);
// 单使用readLine方法,读取不会读入换行符之类的内容。写入的内容都在一行中。
bw.newLine();
bw.flush();
}
br.close();
bw.close();
}
}
注意
2022/4/14 |
---|
单独使用readLine方法读取数据 不会读入换行符之类的内容,换行需要执行newLine方法 |
字符流案例
案例1:集合到文件(基本数据)
需求:
把ArrayList集合中的字符串数据写入到文本文件。
要求:
一个字符串元素作为文件中的一行数据。
思路
1 创建ArrayList<String> 集合
ArrayList<String> array = new ArrayList<String>();
array.add("集合元素");
2 创建字符缓冲输出流对象
BufferedWriter bw = new BufferedWriter(new FileWriter("文件路径"));
3 使用增强for循环遍历集合,集合元素写入文件中write方法
for(String s:array){
fw.write(s);
//一个字符串作为一行数据
fw.newLine();
fw.flush();
}
4 释放资源
fw.close();
代码:
// 38
public class Demo3 {
/*
需求:把ArrayList集合中的字符串数据写入到文本文件。
要求:一个字符串元素作为文件中的一行数据。
*/
public static void main(String[] args) throws IOException {
// 创建ArrayList集合
ArrayList<String> array = new ArrayList<String>();
//集合添加元素
array.add("sensors");
array.add("application");
array.add("keyboard");
// 创建字符缓冲输出流对象
BufferedWriter bw = new BufferedWriter(new FileWriter(".\\test38.txt"));
// 遍历集合得到每一个字符串
for(String st:array){
bw.write(st);
bw.newLine();
bw.flush();
}
// 释放资源
bw.close();
}
}
案例2:文件到集合
需求:
把文本文件中的数据读取到集合中,并遍历集合。
要求:
文件中每一行数据是一个集合元素(说明要使用BufferedReader的特殊方法readLine)
思路
1 创建ArrayList集合
ArrayList<String> array = new ArrayList<String>();
2 创建字符缓冲输入流对象
BufferedReader br = new BufferedReader(new FileReader("文件路径"));
3 使用方法readLine读取一行数据,并使用add方法添加到集合中
String line;
while((line = br.readline()) != null){
array.add(line);
}
4 释放字符流资源
br.close();
5 遍历集合 采用迭代器遍历
Iterator<String> it = array.iterator();
while(it.hasNext()){
System.out.println(it.next());
}
代码:
// 38-
public class Demo4 {
public static void main(String[] args) throws IOException {
// 创建字符缓冲输入流对象
BufferedReader br = new BufferedReader(new FileReader(".\\test38.txt"));
// 创建ArrayList集合
ArrayList<String> array = new ArrayList<String>();
// 读数据 使用特有方法readLine
String ss;
while((ss = br.readLine()) != null){
array.add(ss);
}
// 释放资源
br.close();
// 遍历集合
for(String st:array){
System.out.println(st);
}
}
}
案例3:点名器ArrayList+random
需求:
一个文件里面存储了班级同学的姓名,每一个姓名占一行,要求通过程序实现随机点名器
思路:
1 创建字符缓冲流输入流对象,将一行数据读入ArrayList集合
BufferedReader br = new BufferedReader(new FileReader("文件路径"));
ArrayList<String> array = new ArrayList<String>();
String line;
while((line = br.readLine()) != null){
array.add(line);
}
br.close();
2 产生随机数,随机数是[0,array.size()),通过get(index)方法得到对应的值
Random r = new Random();
int x = r.nextInt(array.size());
System.out.println(array.get(x));
//38-
public class Demo5 {
public static void main(String[] args) throws IOException {
// 创建字符缓冲输入流对象
BufferedReader br = new BufferedReader(new FileReader(".\\test38.txt"));
// 创建ArrayList集合
ArrayList<String> array = new ArrayList<String>();
// 通过br读取文件中的内容
String line;
while((line = br.readLine()) != null){
// 读取后的数据存储在集合中
array.add(line);
}
// 释放资源
br.close();
// 用Random产生一个随机数 表示随机点名
Random r = new Random();
int index = r.nextInt(array.size());
// 产生的随机数 作为索引在集合中找对应的值并输出
System.out.println("幸运者是:"+array.get(index));
}
}
不太记得内容是:
1 使用Random产生一个随机数
Random r = new Random();
2 产生一个[0,array.size())的随机数
int index = r.nextInt(array.size());
3 通过索引号找集合中的元素
array.get(index);
案例4:集合到文件(数据=对象)
需求:
把ArrayList集合中的学生数据写入到文本文件。
要求:
每一个学生对象的数据作为文件中的一行数据
格式是:学号,姓名,年龄,居住地 举例:001,Bob,30,西安
思路
1 创建ArrayList集合并添加学生对象
ArrayList<Student> array = new ArrayList<Student>();
array.add(学生对象);
2 创建字符缓冲输出流对象
BufferedWriter bw = new BufferedWriter(new FileWriter("文件路径"));
3 遍历集合,将集合元素按照指定格式存放到文件中
for(Student stu : array){
StringBuilder sb = new StringBuilder();
sb.append(stu.getXXX()).append(",").append(stu.getXXX()).append(",");
}
bw.write(sb.toString());
bw.flush();
bw.close();
代码:
// 38-
public class Demo6 {
public static void main(String[] args) throws IOException {
/* 需求:把ArrayList集合中的学生数据写入到文本文件。
要求:每一个学生对象的数据作为文件中的一行数据
格式是:学号,姓名,年龄,居住地 举例:001,Bob,30,西安
*/
// 创建ArrayList集合
ArrayList<Student> array = new ArrayList<Student>();
// 创建学生对象
Student st1 = new Student(001,"汪苏泷",33,"西安");
Student st2 = new Student(002,"许嵩",30,"西安");
Student st3 = new Student(003,"白敬亭",31,"西安");
// 将学生对象添加到集合中
array.add(st1);
array.add(st2);
array.add(st3);
// 创建字符缓冲输出流对象
BufferedWriter bw = new BufferedWriter(new FileWriter(".\\test38.txt"));
// 遍历集合,将每一个学生信息写入文件中
for(Student st:array){
// 这种形式不对。书写的学号和年龄是乱码
/* bw.write(st.getNum());
bw.write(",");
bw.write(st.getName());
bw.write(",");
bw.write(st.getAge());
bw.write(",");
bw.write(st.getLocal());
bw.newLine();
bw.flush();*/
StringBuilder sb = new StringBuilder();
sb.append(st.getNum()).append(",").append(st.getName()).append(",").append(st.getAge()).append(",").append(st.getLocal());
bw.write(sb.toString());
bw.newLine();
bw.flush();
}
// 释放资源
bw.close();
}
}
案例5:文件到集合(数据=对象)
需求:
把文本文件中的数据读取到集合中,并遍历集合。
要求:
文件中的每一行数据是一个学生对象的成员变量。举例:001,汪苏泷,30,西安。
思路
1 创建字符缓冲输入流对象
BufferedReader br = new BufferedReader(new FileReader("文件路径"));
2 创建集合对象
ArrayList<Student> array = new ArrayList<Student>();
3 使用readLine读取数据,将读取的字符串采用split分割,然后创建学生对象,并对所有值进行设置setXXX方法 最后学生元素添加到集合中
String line;
while((line = br.readline())!= null){
String[] strs = line.split(',');
Student st = new Student();
st.setId(Integer.parseInt.(strs[0]));
// 使用Integer.parseInt方法将字符串转为int类型
st.setName(strs[1]);
st.setAge(Integer.parseInt.(strs[2]));
st.setAddress(strs[3]);
array.append(st);
}
4 遍历集合
使用增强for循环
代码:
// 38
public class Demo7 {
public static void main(String[] args) throws IOException {
/*
需求:把文本文件中的数据读取到集合中,并遍历集合。
要求:文件中的每一行数据是一个学生对象的成员变量。举例:001,汪苏泷,30,西安。
*/
// 创建字符缓冲输入流对象
BufferedReader br = new BufferedReader(new FileReader(".\\test38.txt"));
// 创建ArrayList集合
ArrayList<Student> array = new ArrayList<Student>();
// 使用字符缓冲输入流对象的读数据方法
String line;
while((line = br.readLine()) != null){
//将得到字符串使用split进行分割,得到一个字符串数组
String[] sArray = line.split(",");
// 创建学生对象
Student st = new Student();
st.setNum(Integer.parseInt(sArray[0]));
st.setName(sArray[1]);
st.setAge(Integer.parseInt(sArray[2]));
st.setLocal(sArray[3]);
array.add(st);
}
// 释放资源
br.close();
// 遍历集合
for(Student s : array){
System.out.println(s.getNum() + "," + s.getName() + "," + s.getAge() + "," + s.getLocal());
}
}
}
案例6:集合到文件(数据排序TreeSet)
需求:
键盘录入5个学生信息(姓名,语文成绩,数学成绩,英语成绩)。
要求:
按照学生成绩总分从高到低写入文本文件。
格式:
姓名,语文成绩,数学成绩,英语成绩 举例:汪苏泷,98,99,100
思路:
1 创建键盘录入学生信息的函数
public void writeInformation(TreeSet set){
Scanner sc = new Scanner(System.in);
Student stu = new Student();
String name = sc.nextLine();
int score0 = sc.nextInt();
int score1 = sc.nextInt();
int score2 = sc.nextInt();
stu.setName(name);
stu.setScore0(score0);
stu.setScore1(score1);
stu.setScore2(score2);
set.add(stu);
}
2 创建TreeSet集合 并按照总分从高到低排序,总分相同 按照语文成绩排序 依次是数学、姓名
TreeSet<Student> setTree = new TreeSet<Student>(new comparator<Student>{
@Override
public int compare(Student s1,Student s2){
// 总分从高到低
int num = s1.getSum() - s2.getSum();
// 次要条件
int num1 = num ==0 ? s1.getChinese()-s2.getChinese() : num;
int num2 = num1 ==0 ? s1.getMath()-s2.getMath() : num1;
int num3 = num2 == 0 ? s1.getName()-s2.getName(): num2;
return num3;
}
})
3 创建字符缓冲输出流对象 将集合数据写入文件中
BufferedWriter bw = new BufferedWriter(new FileWriter("文件路径"));
for(Student stu : setTree){
StringBuilder sb = new StringBuilder();
sb.append(stu.getName()).append(",").append(stu.getChinese()).append(",").append(stu.getMath()).append(",").append(stu.getEnglish());
bw.write(sb.toString());
bw.newLine();
bw.flush();
}
bw.close();
//
public class Demo {
public static void main(String[] args) throws IOException {
/*需求:键盘录入5个学生信息(姓名,语文成绩,数学成绩,英语成绩)。
要求:按照学生成绩总分从高到低写入文本文件
格式:姓名,语文成绩,数学成绩,英语成绩 举例:汪苏泷,98,99,100
*/
// 创建学生类
// 创建TreeSet集合 通过比较器排序进行排序
TreeSet<Student> tSet = new TreeSet<Student>(new Comparator<Student>() {
@Override
public int compare(Student s1, Student s2) {
// 正数 将要存储的数据 放于 现存数据 之后
// 待存数据 s1 现存数据 s2
// 成绩总分从高到低
int num = s2.getSum() - s1.getSum();
// 次要条件
int num2 = num==0?s1.getChineseScore() - s2.getChineseScore():num;
int num3 = num2==0?s1.getMathScore() - s2.getMathScore():num2;
int num4 = num3==0?s1.getName().compareTo(s2.getName()):num3;
return num4;
}
});
// 键盘录入学生信息
for(int i = 0;i<5;i++){
Scanner sc = new Scanner(System.in);
System.out.println("请输入第" + (i+1) + "个学生信息");
System.out.println("姓名:");
String name = sc.nextLine();
System.out.println("语文成绩:");
int chinese = sc.nextInt();
System.out.println("数学成绩:");
int math = sc.nextInt();
System.out.println("英语成绩:");
int english = sc.nextInt();
// 创建学生对象
Student st = new Student();
st.setName(name);
st.setChineseScore(chinese);
st.setMathScore(math);
st.setEnglishScore(english);
// 将学生信息添加到集合中
tSet.add(st);
}
// 创建字符输出流对象
BufferedWriter bw = new BufferedWriter(new FileWriter(".\\test3.txt"));
// 遍历TreeSet集合,将学生信息按照指定格式拼接成字符串,将集合中内容写入文件。
for(Student st : tSet){
StringBuilder sb = new StringBuilder();
sb.append(st.getName()).append(",").append(st.getChineseScore()).append(",").append(st.getMathScore()).append(",").append(st.getEnglishScore()).append(",").append(st.getSum());
// 调用字符缓冲输出流对象的方法写数据
bw.write(sb.toString());
bw.newLine();
bw.flush();
}
// 释放资源
bw.close();
}
}
案例7:复制单级文件夹
单级文件夹 = 文件夹中只有文件而没有目录。
需求:
把“xxx”这个文件夹复制到模块目录xxx下。
public class Demo {
public static void main(String[] args) throws IOException {
// 创建数据源目录的File对象
File fSrc = new File("E:\\222\\javaSE");
// 获取数据源目录file对象的名称 也就是javaSE
String fName = fSrc.getName();
// 创建目的地目录file对象,路径名是模块名 + fName组成
File fDes = new File(".\\",fName);
// 判断目的地目录对应的file是否存在,如果不存在,就创建
if(!fDes.exists()){
fDes.mkdir();
// System.out.println("创建成功");
}
// 获取数据源目录下的所有文件的File数组
File[] srcfiles = fSrc.listFiles();
// 遍历File数组,得到每一个file对象,该file对象,其实就是数据源文件
for(File srcfile:srcfiles){
// 获取每一个数据文件的名字
String srcfileName = srcfile.getName();
// System.out.println(srcfileName);
// 创建目的地文件file对象
File desfile = new File(fDes,srcfileName);
// String path = desfile.getPath();
// System.out.println(path);
// 复制文件
copyfile(srcfile,desfile);
}
// System.out.println(fName);
}
// 复制文件方法
private static void copyfile(File srcfile, File desfile)throws IOException {
BufferedInputStream bis = new BufferedInputStream(new FileInputStream(srcfile));
BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(desfile));
// int[] num =new int[1024];
// 这里是字节数组 byte
byte[] bys = new byte[1024];
int len;
while((len = bis.read(bys)) != -1){
bos.write(bys, 0, len);
}
bis.close();
bos.close();
}
}
案例8:复制多级文件夹(需要使用到递归)
多级文件夹 = 文件夹 里 还有文件夹。
需求:把“xxx”复制到某盘目录下
public class Demo2 {
public static void main(String[] args) throws IOException {
// 创建数据源路径 file对象
File srcFile = new File("E:\\222");
// 创建数据目的路径 组合 源路径名字的 file对象
File destFile = new File("c:\\");
// 写方法实现文件夹的复制
copyFolder(srcFile,destFile);
}
// 复制文件夹
private static void copyFolder(File srcFile,File destFile) throws IOException{
// 判断数据源是否是目录
if(srcFile.isDirectory()){
// 是目录
// 在目的地址下创建和数据源file名称一样的目录
String srcFileName = srcFile.getName();
File newFolder = new File(destFile,srcFileName);
if(!newFolder.exists()){
newFolder.mkdir();
}
// 获取数据源file下的所有文件或者目录的file数组
File[] filesArray = srcFile.listFiles();
for(File fi : filesArray){
copyFolder(fi,newFolder);
}
}else{
// 说明是文件,直接复制,用字节流
File newdestFile = new File(destFile,srcFile.getName());
copyFile(srcFile, newdestFile);
}
}
// 字节缓冲流复制文件
private static void copyFile(File srcfile,File desfile) throws IOException {
BufferedInputStream bis = new BufferedInputStream(new FileInputStream(srcfile));
BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(desfile));
byte[] bys = new byte[1024];
int len;
while((len = bis.read(bys)) != -1){
bos.write(bys, 0, len);
}
bis.close();
bos.close();
}
}
案例9:复制文件的异常处理
try…catch…finally的做法:
try{
可能出现异常的代码;
}catch(异常类名 变量名){
异常的处理代码;
}finally{
执行所有清除操作;
}
JDK7改进方案:
try(定义流对象){
可能出现异常的代码;
}catch(异常类名 变量名){
异常的处理代码;
}
自动释放资源
JDK9改进方案:
定义输入流对象;
定义输出流对象;
try(输入流对象;输出流对象){
可能出现异常的代码;
}catch(异常类名 变量名){
异常的处理代码;
}
自动释放资源
案例:
public class Demo3 {
public static void main(String[] args) {
}
// 在方法外面抛出异常 throws IOException
public static void method1() throws IOException {
FileReader fr = new FileReader("test38.txt");
FileWriter fw = new FileWriter("fw.txt");
char[] chs = new char[1024];
int len;
while((len = fr.read(chs)) != -1){
fw.write(chs, 0, len);
}
fr.close();
fw.close();
}
// try..catch..finally..
public static void method2(){
FileReader fr = null;
FileWriter fw = null;
try{
fr = new FileReader("test38.txt");
fw = new FileWriter("fw.txt");
char[] chs = new char[1024];
int len;
while((len = fr.read(chs)) != -1){
fw.write(chs, 0, len);
}
}catch(IOException e){
e.printStackTrace();
}finally{
if(fr != null){
try {
fr.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if(fw != null){
try {
fw.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
// JDK7 改进方案
public static void method3(){
try(FileReader fr = new FileReader("test38.txt");
FileWriter fw = new FileWriter("fw.txt");){
char[] chs = new char[1024];
int len;
while((len = fr.read(chs)) != -1){
fw.write(chs, 0, len);
}
}catch(IOException e){
e.printStackTrace();
}
}
// JDK9 的改进方案
public static void method4() throws IOException{
FileReader fr = new FileReader("test38.txt");
FileWriter fw = new FileWriter("fw.txt");
try(fr;fw){
char[] chs = new char[1024];
int len;
while((len = fr.read(chs)) != -1){
fw.write(chs, 0, len);
}
}catch(IOException e){
e.printStackTrace();
}
}
}
字符流总结
说明 | Reader | Writer |
---|---|---|
转换流 将字节流转为字符流 | InputStreamReader(InputStream is) | OutputStreamWriter(OutputStream os) |
简化转化流书写 | FileReader(String filename) | FileWriter(String filename) |
字符缓冲流 | BufferedReader(Reader reader) | BufferedWriter (Writer writer) |
注意
简化的转换流 参数只支持String类型;
Scanner
概述
public final class Scanner extends Object implements Iterator< String>,closeable
一个简单的文本扫描器,可以使用正则表达式解析原始类型和字符串。
常用方法
方法 | 介绍 |
---|---|
Scanner(InputStream source) | Scanner sc = new Scanner(System.in); 构造一个新的Scanner,产生从指定的输入流扫描的值 |
int nextInt() | 将输入的下一个标记扫描为int |
String nextLine() | 将此扫描仪推进到当前行并返回跳过的输入。 |