小知识,大挑战!本文正在参与“程序员必备小知识”创作活动。
文章目录
Reader/Writer
介绍
File用法一章中我们大致了解了 Java Stream 相关类是用来处理字节流的,但不适合用来字符流。
Reader 是Java IO 中所有 Reader 的基类。Reader 与 InputStream 类似,不同点在于,Reader 基于字符而非基于字节。换句话说,Reader 用于读取文本,而 InputStream 用于读取原始字节。
Writer 是 Java IO 中所有 Writer 的基类。与 Reader 和 InputStream 的关系类似,Writer 基于字符而非基于字节,Writer 用于写入文本,OutputStream 用于写入字节。
你通常会使用 Reader 和 Writer 的子类,而不会直接使用 Reader 和 Writer。
常用的 Reader/Writer
Writer 方法
方法 | 描述 |
---|---|
write(int c) | 写入单个字符c |
write(char[] buff) | 写入全部字符数组 buff |
write(char[] buff,int from,int length) | 写入字符数组 buff 中开始为 from 长度为 length 的某一部分。 |
write(String s) | 写入字符串的全部字符 |
Reader 方法
方法 | 描述 |
---|---|
void mark(int readAheadLimit) | 标记流中的当前位置。 |
int read() | 读取单个字符。读取char字符,会在前面补2个0字节转成 int,读取结束再读取返回-1 |
int read(char[] cbuf) | 将字符读入数组。读取一批字符放入 char[] 数组,并返回这一批的数量, 读取结束再读取返回 -1 |
abstract int read(char[] cbuf, int off, int len) | 将字符读入数组的某一部分。 |
int read(CharBuffer target) | 试图将字符读入指定的字符缓冲区。 |
boolean ready() | 判断是否准备读取此流。 |
InputStreamReader/OutputStreamWriter
介绍
InputStreamReader 类是从字节流到字符流的桥接器:它使用指定的字符集读取字节并将它们解码为字符。 它使用的字符集可以通过名称指定,或者可以接受平台的默认字符集。
每次调用一个 InputStreamReader 的read()
方法都可能导致从底层字节输入流中读取一个或多个字节。
为了实现字节到字符的有效转换,可以从基础流中提取比满足当前读取操作所需的更多字节。为了获得最高效率,请考虑在 BufferedReader 中包装InputStreamReader
构造方法

创建一个使用默认字符集的 InputStreamReader
InputStreamReader(InputStresam in)
创建使用指定字符集的 InputStreamReader
InputStreamReader(InputStresam in,String charsetName)
OutputStreamWriter 类似
栗子1:写入练习
OutputStreamWriter out = new OutputStreamWriter(
new FileOutputStream("d:/abc/f7"));
out.write("abc中文");
out.close();
//
out = new OutputStreamWriter(
new FileOutputStream("d:/abc/f8"), "UTF-8");
out.write("abc中文");
out.close();
/
DataOutputStream dos = new DataOutputStream(
new FileOutputStream("d:/abc/f9"));
dos.writeChars("abc中文");
dos.close();
运行程序会在 D 盘 abc 文件夹下生成 f7、f8、f9 三个文件,用十六进制查看内容分别为:
f7
61 62 63 E4 B8 AD E6 96 87
英文单字节,中文3字节
默认 UTF-8
f8
61 62 63 E4 B8 AD E6 96 87
英文单字节,中文3字节
UTF-8
f9
00 61 00 62 00 63 4E 2D 65 87
英文2个字节,中文2个字节
unicode
栗子2:读取练习
读取刚才的 f7 文件
InputStreamReader in = new InputStreamReader(
new FileInputStream("d:/abc/f7"));
int c;
while ((c = in.read()) != -1) {
System.out.println((char) c);
}
in.close();
运行结果:
如果指定了错误的编码,输出会不正确
InputStreamReader in = new InputStreamReader(
new FileInputStream("d:/abc/f9"), "GBK");
int c;
while ((c = in.read()) != -1) {
System.out.println((char) c);
}
in.close();
运行结果:
栗子3:记事簿
xml
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/activity_main"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:padding="20dp">
<EditText
android:id="@+id/editText"
android:layout_width="match_parent"
android:layout_height="200dp"
android:hint="记事内容" />
<Button
android:id="@+id/saveButton"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="保存" />
<Button
android:id="@+id/readButton"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="读取" />
<TextView
android:id="@+id/textView"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
</LinearLayout>
java
public class MainActivity extends AppCompatActivity {
EditText editText;
Button saveButton;
Button readButton;
TextView textView;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
editText = findViewById(R.id.editText);
saveButton = findViewById(R.id.saveButton);
readButton = findViewById(R.id.readButton);
textView = findViewById(R.id.textView);
saveButton.setOnClickListener(view -> save());
readButton.setOnClickListener(view -> show());
}
private void save() {
try {
/**
* OutputStreamWriter
* FileOutputStream
* note.txt
* */
OutputStreamWriter out = new OutputStreamWriter(
new FileOutputStream(getExternalFilesDir(null) + "/note.txt")
);
out.write(editText.getText().toString());
out.close();
Toast.makeText(this, "保存成功", Toast.LENGTH_SHORT).show();
} catch (Exception e) {
Toast.makeText(this, "保存失败", Toast.LENGTH_SHORT).show();
}
}
private void show() {
try {
InputStreamReader in = new InputStreamReader(
new FileInputStream(getExternalFilesDir(null) + "/note.txt")
);
textView.setText("");
char[] buff = new char[8192];
int n;
while ((n = in.read(buff)) != -1) {
String s = new String(buff, 0, n);
textView.append(s);
}
in.close();
Toast.makeText(this, "读取文件完成", Toast.LENGTH_SHORT).show();
} catch (Exception e) {
e.printStackTrace();
Toast.makeText(this, "读取文件失败", Toast.LENGTH_SHORT).show();
}
}
}
运行结果
字节流和字符流
1、计算机存储的单位是字节,如尽管 txt 文本中有中文汉字这样的字符,但是对计算机而言,其是字节形式存在的
2、如果使用字符流读取一个 utf-8 的文件,会默认使用 gbk 码表将其转换为字符
3、字节本质是 8 个二进制位,且不同的字符集对同一字节解码后的字符结果是不同的,因此在读取字符时务必要指定合适的字符集,否则读取的内容会产生乱码
4、 那么就需要一个流把字节流读取的字节进行缓冲而后在通过指定字符集解码成字符返回,因而形式上看是字符流
5、InputStreamReader 流就是起这个作用,实现从字节流到字符流的转换
比较项 | 字节流 | 字符流 |
---|---|---|
基类 | OutputStream/InputStream | Writer/Reader |
操作的数据单位 | 字节 | 字符(多个字节) |
缓冲区 | 可以有也可以无 | 有 |
适用数据 | 任何 | 只适用于文本类数据 |