JavaIO:字节流与字符流

流在实际中分为输入流与输出流两种,输入与输出是一种相对的概念,关键看参考点。
在Java中针对数据流的操作也分为输入与输出两种方式,而且针对此操作提供了以下两类支持

  • 字节流:InputStream OutputStream
  • 字符流:Reader Writer

markmark

常见的为以下几类

mark

流的基本操作形式
1.用File类定义要操作文件的路径
2.字节流或字符流的子类为父类对象实例化
3.数据读写
4.资源关闭
字节与字符的区别:字节流直接与终端文件进行数据交互,字符流需要经过数据缓冲区处理才与终端文件数据交互,可以使用flush()强制清空缓冲区

字节输出流:OutputStream

OutputStream类中提供了3个输出方法write(int b)write(byte[] bwrite(byte[] b, int off, int len),分别输出单个字节、全部字节数组、部分字节数组。
OutputStream本身是一个抽象类,提供的子类可以使用,如FileOutputStream

FileOutputStream类

FileOutputStream有两个构造函数:FileOutputStream(File file)FileOutputStream(File file, boolean append),分别代表覆盖和追加

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// 字节文件内容输出
File file = new File("C:\\Users\\24346\\Documents\\Java\\HelloWorld\\src\\JavaIO\\test.txt");
if(!file.getParentFile().exists()) {
file.getParentFile().mkdirs();
}
// 使用OutputStream及子类FileOutputStream进行实例化
//OutputStream output = new FileOutputStream(file, true); // 追加操作
OutputStream output = new FileOutputStream(file); // 不追加
String str = "hello you have written me\n";
byte data[] = str.getBytes(); // 将字符串变为字节数组
output.write(data); // 输出内容
output.close(); // 关闭资源
// 可以进行单字节操作
for(int i=0; i<data.length; i++) {
System.out.print(data[i]+",");
}System.out.println();

字节输入流:InputStream

OutputStream类对应,InputStream类也有3个read方法:read()read(byte[] b)read(byte[] b, int off, int len),分别用于读取单个字节、读取到字节数组、读取到部分字节数组,返回值都是读取的字节长度,若以读取到末尾则返回-1。

InputStream也是抽象类,提供了子类可以使用,如FileInputStream类。

FileInputStream类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
// 字节文件数据读取
if(!file.exists()) {
System.out.println("file error");
}else {
InputStream input = new FileInputStream(file);
byte data2[] = new byte[1024]; // 准备一个指定大小的数组
int len = input.read(data2); // 读取数据,保存到字节数组
input.close(); // 关闭资源
System.out.println("文件大小:"+len);
System.out.println(new String(data2, 0, len));
}

// 循环实现输入流(逐字节读取)
if(file.exists()) {
InputStream input2 = new FileInputStream(file);
byte data3[] = new byte[1024];
int foot = 0;
int temp = 0;
while((temp = input2.read()) != -1) {
data3[foot++] = (byte)temp;
}
input2.close();
System.out.println("-----------------------\n");
System.out.println(new String(data3, 0, foot));
}

字节输出流:Writer

关注三个方法:append(CharSequence csq)write(String str)write(char[] cbuf)

FileWriter类

两个构造方法:FileWriter(File file)FileWriter(File file, boolean append)

1
2
3
4
5
6
7
8
9
10
// 字符文件内容输出
File file2 = new File("C:\\Users\\24346\\Documents\\Java\\HelloWorld\\src\\JavaIO\\test2.txt");
if(!file2.getParentFile().exists()) { // 判断目录是否存在
file.getParentFile().mkdirs(); // 创建目录
}
Writer out = new FileWriter(file2);
//Writer out = new FileWriter(file2, true); // 追加
String str2 = "KarlRixon";
out.write(str2);
out.close();

字符输入流:Reader

注意四个方法:int read()char read()int read(char[] cbuf)long skip(long n),分别代表读取单个数据、读取单个字符、读取数据到字符数组、跳过字节长度。

FileReader类

1
2
3
4
5
6
7
8
// 字符文件内容读取
if(file2.exists()) {
Reader in = new FileReader(file2);
char instr[] = new char[1024];
int len2 = in.read(instr);
in.close();
System.out.println(new String(instr, 0, len2));
}

转换流

虽然字节流和字符流表示两种不同的数据操作,但是这两种流彼此之间是可以实现相互转换的,可以通过InputStreamReader(InputStream in)OutputStreamWriter(OutputStream out)实现。

1
2
3
4
5
6
7
8
9
10
File file = new File("C:\\Users\\24346\\Documents\\Java\\HelloWorld\\src\\JavaIO\\test2.txt");
if(!file.getParentFile().exists()) {
file.getParentFile().mkdirs();
}
OutputStream output = new FileOutputStream(file);
// 将OutputStream类对象传递给OutputStreamWriter类的构造方法,然后向上转型
Writer out = new OutputStreamWriter(output);
out.write("hi\n");
out.flush();
out.close();

内存流

在流的操作中除了进行文件的输入输出操作之外,还可以对内存进行同样的操作。
Java提供了一下两组内存操作类:
字节内存流:ByteArrayInputStream(内存字节输入流)、ByteArrayOutputStream(内存字节输出流)
字符内存流:CharArrayReader(内存字符输入流)、CharArrayWriter(内存字符输出流)

内存流示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
// 实现小写字母转大写字母
String str = "Hello KarlRixon 233";
// 通过内存操作流实现转换,先将数据保存在内存流,再从里面取出每一个数据
// 将所有要读取数据设置到内存输入流,本次利用向上转型为InputStream类型实例化
InputStream input = new ByteArrayInputStream(str.getBytes());
// 使用ByteArrayOutputStream将所有内存流数据取出
OutputStream output = new ByteArrayOutputStream();
int temp = 0;
// 经过此次循环后,所有数据都将保存在内存输出流对象中
while((temp = input.read()) != -1) {
// 将读取进来的数据转化为大写字母,利用Character.toUpperCase()可以保证只换字母
output.write(Character.toUpperCase(temp));
}
System.out.println(output); // 调用toString方法
input.close();
output.close();

// 实现文件合并读取
File fileA = new File("C:\\Users\\24346\\Documents\\Java\\HelloWorld\\src\\JavaIO\\test.txt");
File fileB = new File("C:\\Users\\24346\\Documents\\Java\\HelloWorld\\src\\JavaIO\\test2.txt");
InputStream inputA = new FileInputStream(fileA);
InputStream inputB = new FileInputStream(fileB);
ByteArrayOutputStream output2 = new ByteArrayOutputStream();
while((temp = inputA.read()) != -1) {
output2.write(temp);
}
while((temp = inputB.read()) != -1) {
output2.write(temp);
}
// 现在所有内容都保存在了内存输出流,所有内容变为字节数组取出
byte data[] = output2.toByteArray();
output2.close();
inputA.close();
inputB.close();
System.out.println(new String(data)); // 字节转换为字符串输出

缓冲区之BufferedReader

为了可以进行完整的数据输入操作,最好的做法是采用缓冲区的方式对输入数据进行暂存,而后可以利用输入流一次性读取内容,这样就可以避免输入中文时读取错乱问题,其他由于数据没有一次性读取而产生的问题也可以解决。

java.io提供了以下两种缓冲区操作流:
字符缓冲区流:BufferedReaderBufferedWriter
字节缓冲区流:BufferedInputStreamBufferedOutputStream

以上4个操作类中,最重要的就是BufferedReader类,处理中文数据最方便。
如果要使用BufferedReader类来处理System.in的操作就会出现一个问题,BufferedReaderReader的子类,且构造方法也要接受Reader类型,而System.inInputStream类型,所以此处必须将InputStream转换为Reader类型,可以用InoutStreamReader类实现这一转换。

BufferedReader示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
// 判断输入内容
BufferedReader buf = new BufferedReader(new InputStreamReader(System.in));
boolean flag = true;
while(flag) {
System.out.println("输入年龄:");
String str = buf.readLine();
if(str.matches("\\d{1,3}")) {
System.out.println("年龄是:"+Integer.parseInt(str));
flag = false;
}else {
System.out.println("输入年龄错误,年龄必须是数组组成");
}
}

// 读取文件
File file = new File("C:\\Users\\24346\\Documents\\Java\\HelloWorld\\src\\JavaIO\\test3.txt");
// 使用文件输入流实例化BufferedReader类对象
BufferedReader buf2 = new BufferedReader(new FileReader(file));
String str2 = null;
while((str2 = buf2.readLine()) != null) {
System.out.println(str2);
}
buf2.close();

扫描流

JDK 1.5开始增加了新的Scanner类,相较于BufferedReader,Scanner类对于键盘数据输入的实现也会变得更加简单。

Scanner示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
// 使用Scanner实现键盘数据输入
Scanner scan = new Scanner(System.in);
System.out.print("输入内容:");
if(scan.hasNext()) {
System.out.println("输入内容为:"+scan.next());
}

// 输入数字
System.out.println("输入成绩:");
if(scan.hasNext()) {
double score = scan.nextDouble();
System.out.println("成绩为:"+score);
}else {
System.out.println("输入错误");
}

// 正则验证
System.out.println("输入生日:");
if(scan.hasNext("\\d{4}-\\d{2}-\\d{2}")) {
String bir = scan.next("\\d{4}-\\d{2}-\\d{2}");
System.out.println("生日为:"+bir);
}else {
System.out.println("输入错误");
}

scan.close();

// 读取文件
Scanner scan2 = new Scanner(new FileInputStream(new File("C:\\Users\\24346\\Documents\\Java\\HelloWorld\\src\\JavaIO\\test.txt")));
scan2.useDelimiter("\n"); // 设置读取分隔符
while(scan2.hasNext()) { // 循环读取
System.out.println(scan2.next());
}
scan2.close();

×

纯属好玩

扫码支持
扫码打赏,你说多少就多少

打开支付宝扫一扫,即可进行扫码打赏哦

文章目录
  1. 1. 字节输出流:OutputStream
    1. 1.1. FileOutputStream类
  2. 2. 字节输入流:InputStream
    1. 2.1. FileInputStream类
  3. 3. 字节输出流:Writer
    1. 3.1. FileWriter类
  4. 4. 字符输入流:Reader
    1. 4.1. FileReader类
  5. 5. 转换流
  6. 6. 内存流
    1. 6.1. 内存流示例
  7. 7. 缓冲区之BufferedReader
    1. 7.1. BufferedReader示例
  8. 8. 扫描流
  9. 9. Scanner示例