字符流读写文本文件

字符流

当使用字节流读取文本文件时,可能会有一个小问题。就是遇到中文字符时,可能不会显示完整的字符,那是因为一个中文字符可能占用多个字节存储。所以Java提供一些字符流类,以字符为单位读写数据,专门用于处理文本文件。

输入流

输入字符流(Reader)

java.io.Reader 抽象类是表示用于读取字符流的所有类的超类,可以读取字符信息到内存中。下面是Reader的体系:

java 复制代码
public abstract class Reader implements Readable, Closeable

它是个抽象类,因此不能被实例化。

构造方法

Reader提供了两个构造方法供子类使用

java 复制代码
protected Reader() 
protected Reader(Object lock) 

实例方法

它定义了字符输入流的基本共性功能方法。

  • public void close() :关闭此流并释放与此流相关联的任何系统资源。
  • public int read() : 从输入流读取一个字符。
  • public int read(char[] cbuf) : 从输入流中读取一些字符
  • public abstract int read(char[] cbuf, int off, int len) : 从输入流中读取一些字符
  • public int read(CharBuffer target):将字符读入指定的字符缓冲区
  • public void mark(int readAheadLimit):标记流中的位置
  • boolean markSupported(): 是否支持 mark
  • public boolean ready() : 流是否准备好被读取
  • public void reset() : 重置流
  • public long skip(long n): 跳过字符

由于public abstract int read(char[] cbuf, int off, int len) 是抽象方法,所以所有子类必须实现此方法。

JDK10提供public long transferTo(Writer out)用于写出读取到的字符

FileReader

java.io.FileReader 类是读取字符文件的便利类。构造时使用系统默认的字符编码和默认字节缓冲区。

JDK11提供的构造方法中可以传入指定编码

java 复制代码
public class FileReader extends InputStreamReader

系统默认字符编码:

java 复制代码
Charset.defaultCharset(); // 系统默认编码
  • eclipse中默认为GBK
  • jetbrains idea中默认UTF8

编辑器的默认编码是可以更改的

构造方法

  • FileReader(File file) : 创建一个新的 FileReader ,给定要读取的File对象。
  • FileReader(String fileName) : 创建一个新的 FileReader ,给定要读取的文件的名称。

JDK11新增的构造

java 复制代码
FileReader(File file, Charset charset)
FileReader(String fileName, Charset charset)

例子:

java 复制代码
File file = new File("D:\\temp\\a.txt");
FileReader reader = new FileReader(file);
FileReader reader1 = new FileReader("D:\\temp\\a.txt");

构造方法内部都是调用了父类的构造方法,传入一个FileInputStream对象。

实例方法

FileReader中的所有方法都来自其父类InputStreamReader,没有重写任何方法同时也没有扩展的方法。也就是说,学习了FileReader同时也学习了InputStreamReader.

读取单个字符

read() 方法,每次可以读取一个字符的数据,提升为int类型,读取到文件末尾,返回 -1 ,循环读取

java 复制代码
package com.itlaobing.tor;

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

public class FileReaderTest {

	public static void main(String[] args) throws IOException {
		FileReader fr = new FileReader("D:/temp/reader.txt");
		int ch = fr.read();//读取单个字符
		System.out.println((char)ch);
		fr.close();//关闭流
	}
}

自行将上面代码修改为读取整个文本内容。

读取多个字符

read(char[] cbuf) ,每次读取cbuf的长度个字符到数组中,返回读取到的有效字符个数,读取到末尾时,返回 -1

创建reader2.txt内容为:

properties 复制代码
云创动力在西安学习中心

例子:

java 复制代码
package com.itlaobing.tor;

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

public class FileReaderTest2 {

	public static void main(String[] args) throws IOException {
		// 使用文件名称创建流对象
		FileReader fr = new FileReader("D:/temp/reader2.txt");
		// 定义变量,保存有效字符个数
		int len;
		// 定义字符数组,作为装字符数据的容器
		char[] cbuf = new char[2];
		// 循环读取
		while ((len = fr.read(cbuf)) != -1) {
			System.out.println(new String(cbuf));
		}
		// 关闭资源
		fr.close();
	}

}

输入结果为:

java 复制代码
云创
动力
在西
安学
习中
心中

我们发现多了一个字符,这是为什么呢?尝试将代码中的:

java 复制代码
System.out.println(new String(cbuf));

修改为:

java 复制代码
System.out.println(new String(cbuf, 0, len));

BufferedReader

java.io.BufferedReader 从字符输入流读取文本,缓冲字符,以提供字符,数组和行的高效读取。可以指定缓冲区大小,或者可以使用默认大小。 默认值足够大,可用于大多数用途。 它是一个缓冲流。

java 复制代码
public class BufferedReader extends Reader

构造方法

BufferedReader提供了两个构造方法

java 复制代码
//创建使用默认大小的输入缓冲区的缓冲字符输入流
public BufferedReader(Reader in)
//创建使用指定大小的输入缓冲区的缓冲字符输入流
public BufferedReader(Reader in, int sz)

读取一行字符

BufferedReader继承了Reader中所有public方法,还扩展了一个方法:

java 复制代码
public String readLine() //读取一行文字

readLine()读取完毕时,返回null

创建bufferReader.txt文件,并写入如下内容:

properties 复制代码
床前明月光,
疑是地上霜。
举头望明月,
低头思故乡。

例子:

java 复制代码
package com.itlaobing.demo.pm;

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

public class BufferedReaderTest {

	public static void main(String[] args) throws IOException {
		// 创建流对象
		BufferedReader reader = new BufferedReader(new FileReader("D:/temp/bufferReader.txt"));
		// 定义字符串,保存读取的一行文字
		String line = null;
		// 循环读取,读取到最后返回null
		while ((line = reader.readLine()) != null) {
			System.out.print(line);
			System.out.println("------");
		}
		// 关闭流
		reader.close();
	}

}

InputStreamReader

java.io.InputStreamReaderReader的子类,是从字节流到字符流的桥:它读取字节,并使用指定的Charset将其解码为字符 。 它使用的字符集可以由名称指定,也可以被明确指定,或者可以接受平台的默认字符集。

java 复制代码
public class InputStreamReader extends Reader

构造方法

InputStreamReader中提供了四个构造方法:

java 复制代码
//创建一个使用默认字符集的InputStreamReader
public InputStreamReader(InputStream in)
//创建一个使用命名字符集的InputStreamReader
public InputStreamReader(InputStream in, String charsetName)
//创建一个使用给定字符集的InputStreamReader
public InputStreamReader(InputStream in, Charset cs)
//创建一个使用给定字符集解码器的InputStreamReader
public InputStreamReader(InputStream in, CharsetDecoder dec)

其中常用的是前三个。

实例方法

InputStreamReader继承了Reader中的所有public方法,并扩展了getEncoding()方法。

java 复制代码
public String getEncoding()//获取使用的字符编码名称

其他方法在之前都有讲过,所以在此不再描述。

指定编码读取

在创建InputStreamReader对象时,指定编码字符集。

例子:

创建InputStreamReader.txt文件,内容如下:

properties 复制代码
大家好,我是渣渣辉。
是兄弟就来**蓝月砍我。
java 复制代码
package com.itlaobing.demo.pm;

import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStreamReader;

public class InputStreamReaderTest {

	public static void main(String[] args) throws IOException {
		// 定义文件路径,文件为gbk编码
		String FileName = "D:\\temp\\InputStreamReader.txt";
		
		// 创建流对象,默认UTF8编码
		InputStreamReader isr = new InputStreamReader(new FileInputStream(FileName));
		// 创建流对象,指定GBK编码
		InputStreamReader isr2 = new InputStreamReader(new FileInputStream(FileName), "GBK");
		// 定义变量,保存字符
		int read;
		// 使用默认编码字符流读取,乱码
		while ((read = isr.read()) != -1) {
			System.out.print((char) read); 
		}
		isr.close();
		System.out.println();
		
		// 使用指定编码字符流读取,正常解析
		while ((read = isr2.read()) != -1) {
			System.out.print((char) read);
		}
		isr2.close();
	}

}

输出结果如下:

java 复制代码
��Һã����������ԡ�
���ֵܾ���**���¿��ҡ�
大家好,我是渣渣辉。
是兄弟就来**蓝月砍我。

输出流

字符输出流【Writer】

java.io.Writer 抽象类是表示用于写出字符流的所有类的超类,将指定的字符信息写出到目的地。下面是Writer的体系:

java 复制代码
public abstract class Writer implements Appendable, Closeable, Flushable

构造方法

Writer提供了两个构造方法:

java 复制代码
protected Writer();
protected Writer(Object lock)

实例方法

Writer类中提供了以下方法:

  • public abstract void close() :关闭此输出流并释放与此流相关联的任何系统资源。
  • public abstract void flush() :刷新此输出流并强制任何缓冲的输出字符被写出。
  • public void write(int c) :写出一个字符。
  • public void write(char[] cbuf) :将 cbuf.length字符从指定的字符数组写出此输出流。
  • public abstract void write(char[] b, int off, int len) :从指定的字符数组写出 len字符,从偏移量 off开始输出到此输出流。
  • public void write(String str) :写出一个字符串。

FileWriter

java.io.FileWriter类是写出字符到文件的便利类。构造时使用系统默认的字符编码和默认字节缓冲区。

java 复制代码
public class FileWriter extends OutputStreamWriter

构造方法

FileWriter类提供了5个构造方法:

java 复制代码
//构造一个给定文件名的FileWriter对象
public FileWriter(String fileName);
//构造一个FileWriter对象,给出一个带有布尔值的文件名,表示是否附加写入的数据
public FileWriter(String fileName, boolean append);
//给一个File对象构造一个FileWriter对象
public FileWriter(File file);
//给一个File对象构造一个FileWriter对象
public FileWriter(File file, boolean append);
//构造与文件描述符关联的FileWriter对象
public FileWriter(FileDescriptor fd) 

常用的是前4个构造方法。

JDK11新增的构造

java 复制代码
public FileWriter(String fileName, Charset charset)
public FileWriter(String fileName, Charset charset, boolean append)
public FileWriter(File file, Charset charset)
public FileWriter(File file, Charset charset, boolean append)   

实例方法

FileWriter类中的所有方法都来自其父类OutputStreamWriter,没有重写任何方法同时也没有扩展的方法。也就是说,学习了FileWriter同时也学习了OutputStreamWriter

写出单个字符

write(int b) 方法,每次可以写出一个字符数据。

例子:

java 复制代码
package com.itlaobing.demo.pm;

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

public class FileWriterTest {

	public static void main(String[] args) throws IOException {
		FileWriter writer = new FileWriter("D:/temp/fileWriter.txt");
		
		writer.write(97); //a
		writer.write('a'); //a
		writer.write("你好"); //你好
		 
		
		writer.flush();//刷新缓冲区,流对象可以继续使用
		writer.close();//关闭流,释放系统资源。关闭前会刷新缓冲区
	}
}

执行完以上代码后,在D:/temp/文件夹中会多出一个fileWriter.txt文件,文件内容是:

properties 复制代码
aa你好

文件的字符编码集是系统默认的字符编码集,此处我们的默认编码集是UTF-8

写出多个字符

  • write(char[] cbuf)
  • write(char[] b, int off, int len)

BufferedWriter

java.io.BufferedWriter是将文本写入字符输出流,缓冲字符,以提供单个字符,数组和字符串的高效写入。 可以指定缓冲区大小,或者可以接受默认大小。 默认值足够大,可用于大多数用途。是一个缓冲流。

java 复制代码
public class BufferedWriter extends Writer

构造方法

BufferedWriter提供了两个构造方法:

java 复制代码
//创建使用默认大小的输出缓冲区的缓冲字符输出流
public BufferedWriter(Writer out);
//创建一个新的缓冲字符输出流,使用给定大小的输出缓冲区
public BufferedWriter(Writer out, int sz);

实例方法

BufferedWriter继承了Writer中所有public方法,还扩展了一个方法:

java 复制代码
public void newLine();//写一个行分隔符,由系统属性定义符号

例子:

java 复制代码
package com.itlaobing.tor;

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

public class BufferWriterTest {

	public static void main(String[] args) throws IOException {
		// 创建流对象
		BufferedWriter bw = new BufferedWriter(new FileWriter("d:/temp/bufferedWriter.txt"));
		// 写出数据
		bw.write("云创动力");
		// 写出换行
		bw.newLine();
		bw.write("XX学院");
		bw.newLine();
		bw.write("IT特种兵");
		bw.newLine();
		// 释放资源
		bw.close();

	}
}

运行以上程序后,会在创建一个新文件,编码字符集为默认编码字符集,此处为UTF-8,内容为:

properties 复制代码
云创动力
XX学院
IT特种兵

OutputStreamWriter

java.io.OutputStreamWriter ,是Writer的子类,是从字符流到字节流的桥梁。使用指定的字符集将字符编码为字节。它的字符集可以由名称指定,也可以接受平台的默认字符集。

java 复制代码
public class OutputStreamWriter extends Writer

构造方法

OutputStreamWriter提供了四个构造方法:

java 复制代码
//创建一个使用默认字符编码的OutputStreamWriter
public OutputStreamWriter(OutputStream out);
//创建一个使用命名字符集的OutputStreamWriter。
public OutputStreamWriter(OutputStream out, String charsetName);
//创建一个使用给定字符集的OutputStreamWriter
public OutputStreamWriter(OutputStream out, Charset cs);
//创建一个使用给定字符集编码器的OutputStreamWriter
public OutputStreamWriter(OutputStream out, CharsetEncoder enc);

实例方法

OutputStreamWrter的方法大都继承自Writer,除了getEncoding()方法。

例子:

java 复制代码
package com.itlaobing.tor;

import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStreamWriter;
import java.nio.charset.Charset;

public class OutputStreamWriterTest {

	public static void main(String[] args) throws IOException {
		FileOutputStream out = new FileOutputStream("d:/temp/outStreamWriter.txt");
		Charset set = Charset.forName("utf-8");
		OutputStreamWriter writer = new OutputStreamWriter(out, set);
		System.out.println("编码格式为: " + writer.getEncoding());
		writer.write("站在那别动,我去买橘子");
		writer.close();
	}

}

增强try

JDK 7之前我们使用try-catchtry-catch-finally来处理异常。在使用资源的时最后都需要关闭资源,所以一般我们在finally处关闭资源。比如:

java 复制代码
public static void main(String[] args) throws IOException {
		FileOutputStream out = null;
		OutputStreamWriter writer = null;
		try {
			out = new FileOutputStream("d:/temp/outStreamWriter.txt");
			Charset set = Charset.forName("utf-8");
			writer = new OutputStreamWriter(out, set);
			System.out.println("编码格式为: " + writer.getEncoding());
			writer.write("站在那别动,我去买橘子");
		} catch (IOException e) {
			e.printStackTrace();
		}finally {
            //关闭资源
			writer.close();
			out.close();
		}
	}

然而有的时候我们会忘记关闭资源。那么有没有更好的方法呢?

jdk1.7开始, Java 7增强了try语句的功能------它允许在try关键字后跟一对圆括号(),圆括号可以声明,初始化一个或多个资源,此处的资源指得是那些必须在程序结束时必须关闭的资源(比如流操作,数据库连接,网络连接等),try语句在该语句结束时自动关闭这些资源。这种称为try-with-resources语句

java 复制代码
Charset set = Charset.forName("utf-8");
		try (FileOutputStream out = new FileOutputStream("d:/temp/outStreamWriter.txt"); OutputStreamWriter writer = new OutputStreamWriter(out, set);){
			System.out.println("编码格式为: " + writer.getEncoding());
			writer.write("站在那别动,我去买橘子");
		} catch (IOException e) {
			e.printStackTrace();
		}

像这样的话,执行完会自动关闭,不用我们在finally中关闭,也再也不用担心忘记关闭了。

注意:只要这些资源实现类实现了CloseableAutoCloseable接口,就可以自动关闭。几乎所有的资源都可以用这种方式实现自动关闭资源。

如果try()里面有多个资源,用;分开,资源的close方法的调用顺序与它们的创建顺序相反。带有资源的try语句可以像一般的try语句一样具有catch和finally块。在try-with-resources语句中,任何catch或finally块都是在声明的资源被关闭后才会执行的

相关推荐
FreedomLeo11 小时前
Python机器学习笔记(十三、k均值聚类)
python·机器学习·kmeans·聚类
星光樱梦1 小时前
32. 线程、进程与协程
python
阿正的梦工坊1 小时前
深入理解 PyTorch 的 view() 函数:以多头注意力机制(Multi-Head Attention)为例 (中英双语)
人工智能·pytorch·python
西猫雷婶1 小时前
python学opencv|读取图像(十九)使用cv2.rectangle()绘制矩形
开发语言·python·opencv
liuxin334455662 小时前
学籍管理系统:实现教育管理现代化
java·开发语言·前端·数据库·安全
海绵波波1072 小时前
flask后端开发(10):问答平台项目结构搭建
后端·python·flask
码农W2 小时前
QT--静态插件、动态插件
开发语言·qt
ke_wu2 小时前
结构型设计模式
开发语言·设计模式·组合模式·简单工厂模式·工厂方法模式·抽象工厂模式·装饰器模式
赵谨言2 小时前
基于python网络爬虫的搜索引擎设计
爬虫·python·搜索引擎
code04号2 小时前
python脚本:批量提取excel数据
开发语言·python·excel