Java 知识点整理-16.IO流之字符流

目录

字符流FileReader

字符流FileWriter

字符流的拷贝

什么情况下使用字符流

字符流是否可以拷贝非纯文本的文件?

自定义字符数组的拷贝(小数组)

带缓冲的字符流

带缓冲区的流中的特殊方法readLine()和newLine()

将文本反转

LineNumberReader类

装饰设计模式

使用指定的码表读写字符(转换流)

获取文本上字符出现的次数

练习 试用版软件的试用次数提示功能


字符流FileReader

字符流是什么?

字符流是可以直接读写字符的IO流。

字符流读取字符,首先要读取到字节数据,然后转为字符。如果要写出字符,需要把字符转为字节再写出。(读:字节→字符,写:字符→字节)

Reader类概述:

public abstract class Reader extends Object implements Readable, Closeable,用于读取字符流的抽象类。子类必须实现的方法只有read(char[], int, int)和close()。但是,多数子类将重写此处定义的一些方法,以提供更高的效率和/或其他功能。

java.io包下,使用需要导包。抽象类,不能直接用他创建对象。找他一个子类(实现类,举例:InputStreamReader)使用。是FileReader的爷爷类。

FileReader类概述

public class FileReader extends InputStreamReader 用来读取字符文件的便捷类。此类的构造方法假定默认字符编码和默认字节缓冲区大小都是适当的。要自己指定这些值,可以先在FileInputStream上构造一个InputStreamReader。

FileReader用于读取字符流。要读取原始字节流,请考虑使用FileInputStream。

java.io包下,使用需要导包。方法主要来自直接父类和间接父类。

FileReader类构造方法

FileReader(File file) 在给定从中读取数据的File的情况下创建一个新FileReader。

FileReader(String fileName) 在给定从中读取数据的文件名的情况下创建一个新FileReader。

Reader类成员方法

public int read() 读取单个字符。FileReader类覆盖类Reader类中的read方法。读取的字符,如果已到达流的末尾,则返回-1。通过GBK码表可以按照字节大小读取字符,码表如果发现字节为负数就一次读两个字节,即一个字符。

演示:

import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;

public class Demo1_FileReader {
	public static void main(String[] args) throws IOException {
//		demo_read_brfore();
		FileReader fr = new FileReader("xxx.txt");
		int c;	//注意,read方法返回值类型int
		
		while((c = fr.read()) != -1) {	//通过项目默认的码表一次读取一个字符。
			System.out.print((char)c);
		}
		
		fr.close();
	}

	public static void demo_read_brfore() throws FileNotFoundException, IOException {
		FileReader fr = new FileReader("xxx.txt");
		int x = fr.read();		//类型提升
		System.out.println(x);
		char c = (char)x;		//向下转型
		System.out.println(c);
		fr.close();
	}
}

字符流FileWriter

Writer类概述:

public abstract class Writer extends Object implements Appendable, Closeable, Flushable,写入字符流的抽象类。子类必须实现的方法有write(char[], int, int)、flush()和close()。但是,多数子类将重写此处定义的一些方法,以提供更高的效率和/或功能。

java.io包下,使用需要导包。也是抽象类,不能直接用他new对象。找他一个子类(实现类,举例:OutputStreamWriter)使用。是FileWriter的爷爷类。

FileWriter类概述

public class FileWriter extends OutputStreamWriter,用来写入字符文件的便携类。此类的构造方法假定默认字符编码和默认字节缓冲区大小都是可以接受的。要自己指定这些值,可以先在FileOutputStream上构造一个OutputStreamWriter。

文件是否可用或是否可以被创建取决于底层平台。特别是某些平台一次只允许一个FileWriter(或其他文件写入对象)打开文件进行写入。在这种情况下,如果所涉及的文件已经打开,则此类中的构造方法将失败。

FileWriter用于写入字符流。要写入原始字节流,请考虑使用FileOutputStream。

java.io包下,使用需要导包。方法主要来自直接父类和间接父类。

FileWriter的构造方法

FileWriter(File file) 根据给定的File对象构造一个FileWriter对象。

FileWriter(File file, boolean append) 根据给定的File对象构造一个FileWriter对象。

FileWriter(String fileName) 根据给定的文件名构造一个FileWriter对象。

FileWriter(String fileName, boolean append) 根据给定的文件名构造一个FileWriter对象。

FileWriter类重写父类的write方法

public void write(String str) 写一个字符串。

public void write(char[] cbuf) 写入一个字符数组。

write方法可以自动把字符转为字节写出。

演示:

import java.io.FileWriter;
import java.io.IOException;

public class Demo2_FileWriter {
	public static void main(String[] args) throws IOException {
		FileWriter fw = new FileWriter("yyy.txt");		//没有文件,就帮你创建一个,前提保证路径存在。有文件则先清空文件,如果不想清空原有字节数据,则在第二个参数加true。
		fw.write("大家好,java基础快结束了,大家要努力,要坚持!!");	//字符流之所以可以直接写出字符串,其实底层把字符串转成了字节数组,然后再拆成一个个字写入硬盘文件节通过编码表翻译后就成了我们能看的懂得文字。
		fw.write(97);
		fw.close();
	}
}

字符流的拷贝

演示:

import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;

public class Demo3_Copy {
	public static void main(String[] args) throws IOException {
		FileReader fr = new FileReader("xxx.txt");
		FileWriter fw = new FileWriter("zzz.txt");
		
		int c;
		while((c = fr.read()) != -1) {
			fw.write(c);	//无需强转,写的时候去除掉了读的时候加上的16个0.
		}
		
		fr.close();;
		fw.close();  //如果不关流,文件会读不到字节,说明字节数据在缓冲区里。但我们并没有定义任何的缓冲区,说明底层在实现这个功能。Writer类中有一个2k的小缓冲区,如果不关流,内容还在缓冲区中;关流,则会先将缓冲区中的内容刷新到硬盘文件上后,再关闭流。
		
		//FileWriter的爷爷类Writer的源码
/*		private char[] writeBuffer;		//定义写的缓冲数组
	    private static final int WRITE_BUFFER_SIZE = 1024;		//1024个字符,2048个字节,相当于2k。
*/
	}
}

什么情况下使用字符流

字符流也可以拷贝文本文件,但不推荐使用。

因为读取时会把字节转为字符,写出时还要把字符转回字节。(拷贝过程:硬盘文件读取 字节转字符→内存接收字符→硬盘文件写入 字符转字节。比较字节流拷贝过程:硬盘文件读取 字节→内存接收字节→硬盘文件写入 字节 ,内存相当于字节搬运工)

所以无论拷贝纯文本还是非纯文本都推荐字节流。

程序需要读取一段文本,或者需要写出一段文本的时候可以使用字符流。只读或只写的时候推荐字符流。因为读取的时候是通过码表按照字符的大小读取的,所以不会出现半个中文导致乱码。而且写出的时候可以直接将字符串写出,不用转换为字节数组。


字符流是否可以拷贝非纯文本的文件?

不可以拷贝非纯文本的文件(图片、视频和音频等)。

因为在读的时候会通过码表将字节转换为字符。但在转换过程中,两个字节拼起来可能找不到对应的字符,就会用?代替。写出的时候会将字符转换成字节写出去,如果是?则会直接写出,这样写出之后的文件的字节就乱了,看不了了。


自定义字符数组的拷贝(小数组)

演示:

import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;

public class Demo4_SmallArray {
	public static void main(String[] args) throws IOException {
		FileReader fr = new FileReader("xxx.txt");
		FileWriter fw = new FileWriter("yyy.txt");
		
		char[] arr = new char[1024];
		int len;
		while((len = fr.read(arr)) != -1) {		//将文件上的数据读取到字符数组中
			fw.write(arr, 0, len);		//将字符数组中的数据写到文件上
		}
		
		fr.close();
		fw.close();
	}
}

带缓冲的字符流

BufferedReader类概述

public class BufferedReader extends Reader,从字符输入流中读取文本,缓冲各个字符,从而实现字符、数组和行的高效读取。

可以指定缓冲区的大小,或者可使用默认的大小。大多数情况下,默认值就足够大了。

通常,Reader所作的每个读取请求都会导致对底层字符或字节流进行相应的读取请求。因此,建议用BufferedReader包装所有其read()操作可能开销很高的Reader(如FileReader和InputStreamReader)。例如,BufferedReader in = new BufferedReader(new FileReader("foo.in"));将缓冲指定文件的输入。如果没有缓冲,则每次调用read()或readLine()都会导致从文件中读取字节,并将其转换为字符后返回,而这是极其低效的。

java.io包下,使用需要导包。

BufferedReader类的构造方法

BufferedReader(Reader in) 创建一个使用默认大小输入缓冲区的缓冲字符输入流。Reader是一个抽象类,不能直接new,所以我们可以传他的子类对象。

BufferedReader(Reader in, int sz) 创建一个使用指定大小输入缓冲区的缓冲字符输入流。

BufferedReader类的成员方法

public int read() 读取单个字符。

public int read(char[] cbuf, int off, int len) 将字符读入数组的某一部分。

read方法读取字符时会一次读取若干字符到缓冲区,然后逐个返回给程序,降低读取文件的次数,提高效率。

BufferedWriter类概述

public class BufferedWriter extends Writer,将文本写入字符流,缓冲各个字符,从而提供单个字符、数组和字符串的高效写人。

可以指定缓冲区的大小,或者接受默认的大小。在大多数情况下,默认值就足够大了

通常Writer将其输出立即发送到底层字符或字节流。除非要求提示输出,否则建议用BufferedWriter包装所有其write()操作可能开销很高的Writer(如FileWriters和OutputStreamWriters)。例如,PrintWriter out = new PrintWriter(new BufferedWriter(new FileWriter("foo.out")));将缓冲PrintWriter对文件的输出。如果没有缓冲,则每次调用print()方法都会导致将字符转换为字节,然后立即写入到文件,而这是极其低效的。

java.io包下,使用需要导包。

BufferedWriter类的构造方法

BufferedWriter(Writer out) 创建一个使用默认大小输出缓冲区的缓冲字符输出流。Writer是一个抽象类,不能直接new,所以我们可以传他的子类对象。

BufferedWriter(Writer out, int sz) 创建一个使用给定大小输出缓冲区的缓冲字符输出流。

BufferedWriter类的成员方法

public void write(char[] cbuf, int off, int len) 写入字符数组的某一部分。

public void write(iint c) 写入单个字符。

public void write(String s, int off, int len) 写入字符串的某一部分。

write方法写出字符时会先写到缓冲区,缓冲区写满时才会写到文件,降低写文件的次数,提高效率。

演示:

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;

public class Demo5_Buffered {
	public static void main(String[] args) throws IOException {
		BufferedReader br = new BufferedReader(new FileReader("xxx.txt"));
		BufferedWriter bw = new BufferedWriter(new FileWriter("yyy.txt"));
		
		int c;
		while((c = br.read()) != -1){
			bw.write(c);
		}
		
		br.close();
		bw.close();
		
		//根据底层源码知缓冲区大小均为8192个字符是16k.
		/*private static int defaultCharBufferSize = 8192;
		private static int defaultCharBufferSize = 8192;*/
	}
}

带缓冲区的流中的特殊方法readLine()和newLine()

BufferedReader类的成员方法:

public String readLine() 读取一个文本行(一行字符但不包含换行、回车符号)。通过下列字符之一即可认为某行已终止:换行('\n')、回车('\r')或回车后直接跟着换行。

返回:包含该行内容的字符串,不包含任何终止字符,如果已到达流末尾,则返回null(返回还是-1,但是该方法的返回值类型是引用数据类型,接收不了-1。所以当返回-1时,底层做判断如果返回的是-1,底层就返回null)。

BufferedWriter类的成员方法:

public void newLine()写入一个行分隔符(newLine可以输出一个跨平台的换行符号"\r\n")。

演示:

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;

public class Demo6_Buffered {
	public static void main(String[] args) throws IOException {
//		demo_read();
		BufferedReader br = new BufferedReader(new FileReader("zzz.txt"));		//他也相当于对FileReader进行一个包装
		BufferedWriter bw = new BufferedWriter(new FileWriter("aaa.txt"));
		
		String line;
		while((line = br.readLine()) != null) {
			bw.write(line);    
			bw.newLine(); 		//读的时候没有回车换行,写的时候要加上。newLine()的作用正是写出回车换行符。
//			bw.write("\r\n");
		}
		
		br.close();
		bw.close();
	}

	public static void demo_read() throws FileNotFoundException, IOException {
		BufferedReader br = new BufferedReader(new FileReader("zzz.txt"));
		
		String line;
		while((line = br.readLine()) != null) {
			System.out.println(line);
		}
		
		br.close();
	}
}

newLine()与使用\r\n换行的区别:newLine()是跨平台的方法,\r\n只支持Windows系统


将文本反转

练习:将一个文本文档上的文本反转,第一行和倒数第一行交换,第二行和倒数第二行交换。

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.util.ArrayList;

/**
 * 练习:将一个文本文档上的文本反转,第一行和倒数第一行交换,第二行和倒数第二行交换。
 * 分析:
 * 1.创建输入输出流对象(带缓冲区)。
 * 2.创建集合对象,目的:将数据进行临时存储,倒着遍历写出。
 * 3.将读到的数据存储在集合中。
 * 4.倒着遍历集合,将数据写到文件上。
 * 5.关流。
 */
public class Test1 {
	public static void main(String[] args) throws IOException {
		//1.创建输入输出流对象(带缓冲区)。
		BufferedReader br = new BufferedReader(new FileReader("zzz.txt"));
		//2.创建集合对象,目的:将数据进行临时存储,倒着遍历写出。
		ArrayList<String> list = new ArrayList<>();
		//3.将读到的数据存储在集合中。
		String line;
		while((line = br.readLine()) != null) {
			list.add(line);
		}
		br.close();		//关流。
		
		//4.倒着遍历集合,将数据写到文件上。
		BufferedWriter bw = new BufferedWriter(new FileWriter("revxxx.txt"));
		for(int i = list.size() - 1; i >= 0; i--) {
			bw.write(list.get(i));
			bw.newLine();		//添加回车换行
		}
		//5.关流。
		bw.close();
	}
}

注意事项:流对象尽量晚开早关(什么时候用到什么时候开,用完之后立刻关)。


LineNumberReader类

LineNumberReader是BufferedReader的子类,具有相同的功能,并且可以统计行号调用getLineNumber()方法可以获取当前行号,调用setLineNumber()方法可以设置当前行号。

LineNumberReader概述

public class LineNumberReader extends BufferedReader,跟踪行号的缓冲字符输入流。此类定义了setLineNumber(int)和getLineNumber(),他们可分别用于设置和获取当前行号。

默认情况下,行编号从零开始。该行号随数据读取在每个行结束符处递增,并且可以通过调用setLineNumber(int)更改行号。但要注意的是,setLineNumber(int)不会实际更改流中的当前位置:它只更改将由getLineNumber()返回的值。

可认为行在遇到以下符号之一时结束:换行符('\n')、回车符('\r')、回车后紧跟换行符。

LineNumberReader的构造方法

LIneNumberReader(Reader in) 使用默认输入缓冲区的大小创建新的行编号reader。

LineNumberReader(Reader in, int sz) 创建新的行编号reader,将字符读入给定大小的缓冲区。

LineNumberReader的成员方法

public int getLineNumber() 获得当前行号。

public void setLineNumber(int lineNumber) 设置当前行号。

演示:

import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import java.io.LineNumberReader;

public class Demo7_LineNumberReader {
	public static void main(String[] args) throws IOException {
		LineNumberReader lnr = new LineNumberReader(new FileReader("zzz.txt"));
		
		String line;
		lnr.setLineNumber(100);
		while((line = lnr.readLine()) != null) {
			System.out.println(lnr.getLineNumber() + ":" + line);
		}
		
		lnr.close();
		//LineNumberReader底层源码
/*		private int lineNumber = 0;  //初始化值为0,每readLine一次,lineNumber就会自增一次。getLineNumber获取的就是自增后的结果。
		
		public String readLine() throws IOException {
	        synchronized (lock) {
	            String l = super.readLine(skipLF);
	            skipLF = false;
	            if (l != null)
	                lineNumber++;
	            return l;
	        }
	    }
*/	}
}

装饰设计模式

获取被装饰类的引用→在构造方法中传入被装饰类的对象→对原有的功能进行升级。

BufferedReader(Reader in) 就是一个装饰设计模式。

装饰设计模式的好处是:

相较于继承降低了耦合性,装饰类和被装饰类是两个类,被装饰类的变化和装饰类的变化无关。且可以有不同的装饰类。

演示:

public class Demo6_Wrap {
	public static void main(String[] args) {
		NbStudent ns = new NbStudent(new Student());
		ns.code();
	}
}

interface Coder {
	public void code();
}

class Student implements Coder {
	public void code() {
		System.out.println("javase");
		System.out.println("javaweb");
	}
}

class NbStudent implements Coder {
	//1.获取被装饰类的引用
	private Student s;		//获取学生引用
	
	//2.在构造方法中传入被装饰类的对象
	public NbStudent(Student s) {
		this.s = s;
	}

	//3.对原有的功能进行升级
	public void code() {
		s.code();
		System.out.println("ssh");
		System.out.println("数据库");
		System.out.println("大数据");
		System.out.println("...");
	}
}

使用指定的码表读写字符(转换流)

FileReader是使用默认码表读取文件,如果需要使用指定码表读取,那么可以使用InputStreamReader(字节流, 编码表)。

FileWriter是使用默认码表写出文件,如果需要使用指定码表写出,那么可以使用OutputStreamWriter(字节流, 编码表)。

InputStreamReader类概述

public class InputStreamReader extends Reader,InputStreamReader是字节流通向字符流的桥梁:它使用指定的charset读取字节并将其解码为字符。它使用的字符集可以由名称指定或显式给定,或者可以接受平台默认的字符集。

每次调用InputStreamReader中的一个read()方法都会导致从底层输入流读取一个或多个字节。要启用从字节到字符的有效转换,可以提前从底层流读取更多的字节,使其超过满足当前读取操作所需的字节。

为了达到最高效率,可以考虑在BufferedReader内包装InputStreamReader。例如:BufferedReader in = new BufferedReader(new InputStreamReader(System.in));

java.io包下,使用需要导包。他本身是字符流。是FileReader的父类。

InputStreamReader类的构造方法

InputStreamReader(InputStream in, String charsetName) 创建使用指定字符集的InputStreamReader。不能直接创建InputStream对象,所以我们使用其子类对象。

OutputStreamWriter类概述

public class OutputStreamWriterextends Writer,OutputStreamWriter是字符流通向字节流的桥梁:可使用指定的charset将要写入流中的字符编码成字节。它使用的字符集可以由名称指定或显式给定否则将接受平台默认的字符集。

每次调用write()方法都会导致在给定字符(或字符集)上调用编码转换器。在写入底层输出流之前,得到的这些字节将在缓冲区中累积。可以指定此缓冲区的大小,不过,默认的缓冲区对多数用途来说已足够大。注意,write()的字符没有缓冲。为了达到最高效率,可要考虑将OutputStreamWriter包装到BufferedWrite中,以避免频繁调用转换器。例如:Writer out = new BufferedWrite(new OutputStreamWriter(System.out));

java.io包下,使用需要导包。他本身也是字符流。是FileWriter的父类。

OutputStreamWriter类的构造方法

OutputStreamWriter(OutputStream out, String charsetName) 创建使用指定字符集的OutputStreamWriter。不能直接创建OutputStream对象,所以我们使用其子类对象。

演示:

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.io.UnsupportedEncodingException;

public class Demo9_TransIO {
	public static void main(String[] args) throws IOException {
//		demo1_oldRead();
//		demo2_lowRead();
		//进行又一次包装,是为了更高效的读和写。
		BufferedReader br = new BufferedReader(new InputStreamReader(new FileInputStream("utf-8.txt"), "utf-8"));
		BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(new FileOutputStream("gbk.txt"), "gbk"));
		
		int c;
		while((c = br.read()) != -1) {
			bw.write(c);
		}
		
		br.close();
		bw.close();
	}

	public static void demo2_lowRead() throws UnsupportedEncodingException, FileNotFoundException, IOException {
		InputStreamReader isr = new InputStreamReader(new FileInputStream("utf-8.txt"), "utf-8");	//指定码表读字符
		OutputStreamWriter osw = new OutputStreamWriter(new FileOutputStream("gbk.txt"), "gbk");	//指定码表写字符
		
		int c;
		while((c = isr.read()) != -1) {
			osw.write(c);
		}
		
		isr.close();
		osw.close();
	}

	public static void demo1_oldRead() throws FileNotFoundException, IOException {
		//用默认编码表读写,出现乱码。
		FileReader fr = new FileReader("utf-8.txt");  //urf-8中一个中文代表3个字节
		FileWriter fw = new FileWriter("gbk.txt");
		
		int c;
		while((c = fr.read()) != -1) {
			fw.write(c);
		}
		
		fr.close();
		fw.close();
	}
}

总结:utf-8文件用字节流来读,因为字符流相当于内置了一个编码表即平台默认的编码表,他会把文件中的字节转化成字符。但utf-8文件用的编码表是utf-8,如果用gbk去转换就会乱码,所以用字节流FileInputStream来读。这一步,读到每一个字节。

InputStreamReader对字节流进行一次包装,格式:InputStreamReader(字节流, 编码表) 通过指定编码表将字节转换为字符,故称其为字节通向字符的桥梁。这一步,将字节转换成字符。

BufferedReader对InputStreamReader进行二次包装。这一步,更高效,将字符存储在缓冲区里,再一个一个给java程序。

java程序再把字符一个一个的存储到BufferedWriter缓冲区里。缓冲区存满后,往外写先通过OutputStreamWriter把字符拆成字节,格式:OutputStreamWriter(字节流, 编码表) 通过指定的编码表将字符转换为字节,故称其为字符通向字节的桥梁。最后字节流OutputStream再将字节写入硬盘文件。


获取文本上字符出现的次数

练习:获取一个文本上每个字符出现的次数,将结果写在times.txt上。(面试题,io与集合的配合使用)

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.util.TreeMap;

/**
 * 练习:获取一个文本上每个字符出现的次数,将结果写在times.txt上。(面试题,io与集合的配合使用)
 * 分析:
 * 1.创建带缓冲的输入流对象。
 * 2.创建双列集合对象TreeMap。
 * 3.将独到的字符存储在双列集合中,存储的时候要做判断。如果不包含这个键,就将键和一存储;如果包含这个键,就将该键和值加一存储。
 * 4.关闭输入流。
 * 5.创建输出流对象。
 * 6.遍历集合,将集合中的内容写到times.txt中。
 * 7.关闭输出流。
 */
public class Test3 {
	public static void main(String[] args) throws IOException {
		//1.创建带缓冲的输入流对象。
		BufferedReader br = new BufferedReader(new FileReader("zzz.txt"));
		//2.创建双列集合对象TreeMap。
		TreeMap<Character, Integer> tm = new TreeMap<>();
		//3.将独到的字符存储在双列集合中,存储的时候要做判断。如果不包含这个键,就将键和一存储;如果包含这个键,就将该键和值加一存储。
		int ch;
		while((ch = br.read()) != -1) {
			char c = (char)ch;			//强制类型转换
/*			if(!tm.containsKey(c)) {
				tm.put(c, 1);
			}else {
				tm.put(c, tm.get(c) + 1);
			}
*/
			tm.put(c, !tm.containsKey(c) ? 1 : tm.get(c) + 1);
		}
		//4.关闭输入流。
		br.close();
		//5.创建输出流对象。
		BufferedWriter bw = new BufferedWriter(new FileWriter("times.txt"));
		//6.遍历集合,将集合中的内容写到times.txt中。
		for(Character key : tm.keySet()) {
			//优化程序,将\t\n\r直接表示出来。
			switch (key) {
			case '\t':
				bw.write("\\t" + "=" + tm.get(key));
				break;
			case '\n':
				bw.write("\\n" + "=" + tm.get(key));
				break;
			case '\r':
				bw.write("\\r" + "=" + tm.get(key));
				break;
			default:
				bw.write(key + "=" + tm.get(key));	//写出键和值
				break;
			}
			bw.newLine();	//换行
		}
		//7.关闭输出流。
		bw.close();
	}
}

练习 试用版软件的试用次数提示功能

当我们下载一个试用版软件,没有购买正版的时候,每执行一次就会提醒我们还有多少次使用机会。用学过的IO流知识,模拟试用版软件,试用10次机会。执行一次就提示一次您还有几次机会,如果次数到了提示请购买正版。

import java.io.BufferedReader;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;

/**
 * 当我们下载一个试用版软件,没有购买正版的时候,每执行一次就会提醒我们还有多少次使用机会。
 * 用学过的IO流知识,模拟试用版软件,试用10次机会。执行一次就提示一次您还有几次机会,如果次数到了提示请购买正版。
 * 分析:
 * 1.创建带缓冲的输入流对象,因为要使用readLine方法,可以保证数据的原样性。
 * 2.将读到的字符串转换为int数。
 * 3.对int数进行判断,如果大于0,就将其自减写回去;如果不大于0,就提示请购买正版。
 * 4.在if判断中,要将自减的结果打印,并将结果通过输出流写到文件上。不能以数字的形式写回去(97→a),应该以字符串形式写会去,所以用字符输出流即可。
 * 5.关流。
 */
public class Test4 {
	public static void main(String[] args) throws IOException {
		//1.创建带缓冲的输入流对象,因为要使用readLine方法,可以保证数据的原样性。
		BufferedReader br = new BufferedReader(new FileReader("config.txt"));	//config.txt中的内容 10
//		FileWriter fw = new FileWriter("config.txt");  //千万不要这么写,程序运行到这一步,会将文件config.txt中的内容清空掉,我们就读不到10了。
		//2.将读到的字符串转换为int数。
		String line = br.readLine();
		int times = Integer.parseInt(line);			//将数字字符串转换为数字。
		//3.对int数进行判断,如果大于0,就将其自减写回去;如果不大于0,就提示请购买正版。
		if(times > 0) {
			//4.在if判断中,要将自减的结果打印,并将结果通过输出流写到文件上。不能以数字的形式写回去(97→a),应该以字符串形式写会去,所以用字符输出流即可。
			System.out.println("您还有" + times-- + "次机会。");
			FileWriter fw = new FileWriter("config.txt");
			fw.write(times + "");
			fw.close(); //如果不关流,内容在2k的内存缓冲区中储存未刷新到硬盘文件中,且之前创建输出流对象的时候将文件内容清空掉了。所以再次读取文件并转换数据类型时程序会报NumberFormatException。
		}else {
			System.out.println("您的试用次数已到!请购买正版!");
		}
		//关闭流
		br.close();
	}
}

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值