java 基础之 I/O流 其三 (文本输入输出)

时间:2023-02-15 00:07:23

文本输入输出

上一小节讲了字节的使用,但是使用字节的操作不适合我们在日常中对文件的文本进行使用和理解,所以这节讲讲字符流的操作。

保存数据时,我们可以选择文本格式PrintWrite 或 二进制格式DataOutputStream。

一 . 文本格式保存:在存储文本字符串时,需要考虑字符编码。理由如下:

编码格式 内容 编码
UTF-16 “1234” 00 31 00 32 00 33 00 34
IOS 8859-1 “1234” 31 32 33 34

(当然我们日常中会造成乱码的原因是 编码 和解码使用的编码集不同)

InputStreamReader 类 将包含字节的输入流 换成可以产生 Unicode 码元的读入器

InputStreamReader in  = new InputStreamReader(new fFileInputStream("changeTom.dat"),"ISO8859-1");

OutputStreamReader 类 将选定字符编码方式,将unicode字符流转成字节流

1. 输出文本 - - PrintWriter 类

PrintWriter out = new PrintWriter("changeTom.dat");
//等同于
PrintWriter out = new PrintWriter(new FileWrite("changeTom.dat"),true);
//后面的true是启用冲刷机制,只要println被调用,就会被送到目的地;(默认情况是关闭,要等到关闭流才会送到目的地)

String name = "nihao";
out.print("asd");
out.print(name);
//最终会转换成字节并写入changeTom.dat 文件中

2. 输入文本 - - BufferedReader 和 Scanner
(BufferedReader 1.5 之前 而1.6之后 可以使用 Scanner )

BufferedReader :要和 输入源组合起来
(补充:这里BufferedReader 具有缓冲的功能 。之所以要使用缓冲,是因为在操作读写的时候,程序一边读一边写是非常伤硬盘,速度也不快,而建立缓冲区可以先读取固定长度的内容,放到内存,再从内存写到硬盘。)

//从文件输入
try(BufferedReader bufin = new BufferedReader(
new InputStreamReader(new FileInputStream("emplyes.dat"),"UTF-8"))) {

String line ;
while((line = bufin.readLine())!=null){
System.out.println(line);
}

} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}

//从控制台输入
BufferedReader bufin = new BufferedReader(new InputStreamReader(System.in))

Scanner 的使用方式

Scanner in = new Scanner(System.in);
String str = in.next();
// 输入整型
int a = in.nextInt();
System.out.println(str);
System.out.println(a);

Scanner 和 BufferedReader 两哥们的区别
BufferReader 只能读取字符类型,识别基本类型
Scanner 是用正则表达式处理过了的,所以可以识别基本类型

3. 输入文本和输出文本组合使用

//控制台输入
try(BufferedReader bufin = new BufferedReader(
new InputStreamReader(System.in));
PrintWriter out = new PrintWriter(
new FileOutputStream("emplyes.dat"))) {

String line ;
line = bufin.readLine();

System.out.println(line);
out.println(line);


} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}

二、二进制数据读写 继承于字节流 DataOutputStream 和 DataInputStream
使用的方式和其他流无益,这里只贴出代码

try(BufferedReader bufin = new BufferedReader(
new InputStreamReader(System.in));
//使用二进制流输出到文件
DataOutputStream out = new DataOutputStream(
new FileOutputStream("emplyes.dat"))) {

String line ;
line = bufin.readLine();
System.out.println(line);

//输出缓冲区读入的字节流
out.writeBytes(line);

} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}

三、随机访问文件 RandomAccessFile : 实现了DataOutput 和 DataInput 接口

特性:

  • 强大的地方:可以在文件中的任何位置查找或者写入数据
  • 对于磁盘的文件都是随机访问的,而从网络来的数据流却不是
  • 当有文件被 RandomAccessFile 打开时,这个文件不会被删除 通过

RandomAccessFile的第二个构造参数进行读写控制

“r” :只读 。如果试图对该RandomAccessFile指定的文件执行写入方法则会抛出IOException

“rw” :读写 。如果文件不存在,会尝试创建文件

“rwd” :读写 。有rw功能,还要求对文件的内容的每个更新都要同步到存储设备上。这对确保在系统崩溃时不会丢失重要信息特别有用。

“rws”:读写。有rwd 的功能,还要求对元数据的每个更新都要同步到存储设备上。

RandomAccessFile 主要方法

  • getFilePointer() 返回文件记录指针的当前位置
  • seek(long pos) 将文件记录指针定位到pos的位置
  • 由于实现了 DataOutput 和 DataInput 接口,所以读写操作和这两个接口无异 下面是实现的代码

读取

//RandomAccessFile 读取
try(RandomAccessFile raf = new RandomAccessFile("emplyes.dat","r")){
//获取RandomAccessFile对象文件指针的位置,初始位置是0
System.out.println("RandomAccessFile文件指针的初始位置:"+raf.getFilePointer());
//移动指针
raf.seek(3);//移动文件指针位置

byte[] buff = new byte[1024];
int le = 0;
while((le = raf.read(buff))!=-1){
System.out.println(new String(buff,0,le));
}

} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}

写入
这里很简单,只需要移动指针,再写入数据就行

//写出文件
try(RandomAccessFile raf = new RandomAccessFile("emplyes.dat","rw")){
//移动指针到文件最后
raf.seek(raf.length());

raf.write("这是追加在文件最后的内容".getBytes());

} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}

读写,
在文件中间插入数据的操作

try(RandomAccessFile raf = new RandomAccessFile("emplyes.dat","rw")){
//建立一个临时的文件
File tmp=File.createTempFile("tmp", null);
//退出时删除该文件
tmp.deleteOnExit();

try(FileInputStream fis = new FileInputStream(tmp);
FileOutputStream fos = new FileOutputStream(tmp)){
//移动指针
raf.seek(4);

byte[] buff = new byte[1024];

int hasRead = 0 ;

while((hasRead = raf.read(buff))>0){

//读到的内容放到临时文件中
fos.write(buff,0,hasRead);
}
//指针再返回原来的地方
raf.seek(4);

raf.write("这是在读写操作中插入的数据".getBytes());

//将临时文件的内容插回去
while((hasRead = fis.read(buff))>0){
raf.write(buff,0,hasRead);
}


}
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}

可以使用
RandomAccessFile这个类,来实现一个多线程断点下载的功能