第十章 IO
- 1 java.io.File类的使用(计算机操作系统中的文件和文件夹)
- ✔ 练习:遍历目录下所有文件和目录
- 2 IO原理及流的分类(IO指input和output)
- 3 文件流
- ✔ 方法.read(b);//b在字符流中为char数组,字节流中为byte数组
- ✔ 方法.write(str.getBytes()); .write(str); .write(ch,0,len);//str表示要输出的文本为String类型,ch为char数组。其中字节流输出时必须为byte数组,字符流可以是char数组和String
- ✔ 练习:编写程序,把一个文件复制到指定的文件夹下
- 4 处理流1:缓冲流
- 5 处理流2:转换流(InputStreamReasder/OutputStreamWriter)
- 6 处理流3:标准输入/输出流
- ✔ 方法 .readLine();//是字符流的方法
- ✔ 练习:1.把控制台输入的内容写到指定的txt文件中,当接收到字符串over,就结束程序的运行。
- ✔ 练习 2.在一个txt文件中,写一组用户名和密码,通过控制台输入用户名和密码,与txt文件中的用户名和密码做对比,如果一样就在控制台打印登陆成功,如果不一致就打印用户名或密码错误。
- 7 处理流4:打印流(了解)
- 8 处理流5:数据流(了解)
- 9 处理流6:对象流---涉及序列化、反序列化
- 10 随机存取文件流
- 流小节
1 java.io.File类的使用(计算机操作系统中的文件和文件夹)
1.File类概述
File类只能操作文件本身,不能操作文件内容。类似于你可以把一个日记本放在各种地方,但不能在日记本中写日记
2.使用举例
- 创建要操作的目录和文件
在D盘D:\MyAll\study\WorkSpace的test文件夹里建立一个abc文件夹,在里面建立一个tt.txt
-
- 访问文件名
import java.io.File;
/**
* Created with IntelliJ IDEA.
*
* @Author: xuexuezi
* @Date: 2022/11/27/0:22
* @Description:
*/
public class FileTest {
public static void main(String[] args) {
//=======================================创建文件或目录对象============================================
File f = new File("D:\\MyAll\\study\\WorkSpace\\test\\abc\\tt.txt");//这个对象f就是tt.txt了。一个参数路径的写法
File f1 = new File("D:\\MyAll\\study\\WorkSpace","test\\abc\\tt.txt");//这个也是tt.txt。这种两个参数路径的写法使用较少,
File f2 = new File("D:/MyAll/study/WorkSpace/test/abc/tt.txt");
File f3 = new File("D:/MyAll\\study/WorkSpace"+File.separator+"test/abc/tt.txt");//也可以用File.separator分隔;这里试了一下,双斜杠和反斜杠混着用也没问题
//注意,这个\斜杠在文件中是一个路径的分隔符,但是在java编程中,一个\代表转义符,在java中是用双斜杠\\或者一个/反斜杠分隔目录
File ff = new File("D:\\MyAll\\study\\WorkSpace\\test\\abc");//一个目录
File fs = new File("src/qxcto/chapter10/Test.java");//使用相对路径来创建File对象
//====================================访问文件名=================================
//--------------------------------getName()获取当前文件名或目录名称----------------------------------
System.out.println(f.getName());//tt.txt
System.out.println(fs.getName());//Test.java
System.out.println(ff.getName());//abc
//----------------------------------getPath()获取文件或目录的路径----------------------------------
//获取的是new file时写的路径
System.out.println(f.getPath());//D:\MyAll\study\WorkSpace\test\abc\tt.txt
System.out.println(fs.getPath());//src\Test.java
System.out.println(ff.getPath());//D:\MyAll\study\WorkSpace\test\abc
//返回一个用当前文件的绝对路径构建的file对象
//获取当前文件的绝对路径,若不是则补齐
System.out.println(f.getAbsoluteFile());//D:\MyAll\study\WorkSpace\test\abc\tt.txt
System.out.println(fs.getAbsolutePath());//D:\MyAll\study\WorkSpace\IDEA_Project\22-11-17(十、IO)\src\Test.java
System.out.println(ff.getAbsoluteFile());//D:\MyAll\study\WorkSpace\test\abc
System.out.println(fs);//src\Test.java;与fs.getAbsoluteFile()是一个东西,只不过一个是相对路径一个绝对路径
//返回当前文件或文件夹的父级路径
System.out.println(f.getParent());//D:\MyAll\study\WorkSpace\test\abc
System.out.println(fs.getParent());//src\qx\chapter10
System.out.println(ff.getParent());//D:\MyAll\study\WorkSpace\test
//-------------------------------重命名文件名或目录名-------------------------------
//重命名成功返回true,失败返回false
//失败一般是因为原文件(夹)不存在,或新文件已存在。如果不在一个目录下重命名,要保证父级路径存在
//把f对应的tt.txt文件重命名为tt1.txt
System.out.println(f.renameTo(new File("D:\\MyAll\\study\\WorkSpace\\test\\abc\\tt1.txt")));//true
//把fs对应的Test.java文件重命名为TestFile.java;不知道是不是因为本类与Test类在同一个包下
//System.out.println(fs.renameTo(new File("src\\qxcto\\chaptet10\\Test1.java")));//false修改失败
System.out.println(fs.renameTo(new File("D:\\MyAll\\study\\WorkSpace\\IDEA_Project\\ten_IO\\src\\qxcto\\chapter10\\Test2.java")));//true修改成功
//把ff对应的abc路径重命名为abcabc
System.out.println(ff.renameTo(new File("D:\\MyAll\\study\\WorkSpace\\test\\abcabc")));//true
}
}
renameTo (重命名成功返回true,失败返回false)
参考Java file类中的renameTo方法
修改成功
- 问题1:对于fs这个相对路径的文件,且与当前main方法所在类在同一个包下的类文件却无法重命名成功,重命名的新名字得是绝对路径,重命名该类名成功后,内容中的
class Test{
依旧未改,需要手动改
很坑… - 问题2:
再次重命名目录时失败,但之前一直是成功?(原因,要重命名的目录下文件关掉后有进程残余,很奇怪该文件夹中只有一个txt文件和一张png图片,手动右键+M也是改不掉。重启电脑后再重命名就成功了)
-
- 文件检测
//=======================================文件检测=======================================
//重命名后原File对象不存在了
System.out.println(f.exists());//false
System.out.println(fs.exists());//true
System.out.println(ff.exists());//false
//判断文件是否存在,存在返回true,不存在返回false
System.out.println(f.exists());//true
System.out.println(fs.exists());
System.out.println(ff.exists());
//判断文件是否可读,是否可写;可以返回true,不可返回false
System.out.println(f.canWrite());//true
System.out.println(f.canRead());
//判断File对象是否是文件;是否是文件夹
System.out.println(f.isFile());//true
System.out.println(ff.isDirectory());
System.out.println(f.lastModified());//获取文件的最后修改时间,返回的是一个毫秒数
System.out.println(f.length());//返回文件的长度,单位是字节数
-
- 文件相关操作
File f4 = new File("D:\\MyAll\\study\\WorkSpace\\test\\abc\\tt2.txt");
System.out.println(f4.exists());
// if(!f4.exists()) {//判断如果f4不存在
// try {
// f4.createNewFile();//创建新的文件
// } catch (IOException e) {
// e.printStackTrace();
// }
// }
f4.delete();//删除文件
-
- 目录相关操作
//====================================目录相关操作========================================
//-------------------------------------创建目录-------------------------------------
File f5 = new File("D:\\MyAll\\study\\WorkSpace\\test\\cc");
f5.mkdir();//创建单层目录,如果使用这样的方法来创建多层目录,就得一层一层的执行mkdir方法
File f6 = new File("D:\\MyAll\\study\\WorkSpace\\test\\cc\\dd");
f6.mkdir();
File f7 = new File("D:\\MyALL\\study\\WorkSpace\\test\\a\\b\\c");//aa\bb\cc不存在
f7.mkdirs();//创建多层目录
//-----------------------------------返回目录的子集-----------------------------------------
File f8 = new File("D:\\MyALL\\study\\WorkSpace\\test");
System.out.println(f8.list());//null//返回值是一个String字符串
String[] f8s = f8.list();//返回的是当前文件夹的子集的名称,包括目录和文件
for(String s: f8s){
System.out.println(s);
}
File[] f8ss = f8.listFiles();//返回当前文件夹子集的file对象,包括目录和文件
for(File fss: f8ss){
System.out.println(fss);
}
创建多层目录
f8.list()结果,返回的是子文件或目录名称,为String类
f8.listFiles()结果,返回的是子文件或目录,为File类
✔ 练习:遍历目录下所有文件和目录
遍历D盘下的test文件,把test文件夹下所有的目录与文件全部遍历出来,不论层级有多深,要全部遍历出来
思路:类似于递归思路遍历。每遍历到一个目录就判断其下是否为空,不为空则进入目录遍历。为空则继续遍历下一项
import java.io.File;
/**
* Created with IntelliJ IDEA.
* @Author: xuexuezi
* @Date: 2022/11/27/0:22
* @Description: 小测试,遍历test下的所有目录与文件。用到递归思路
*/
public class Test {
public static void main(String[] args) {
new Test().test(new File("D:\\MyAll\\study\\WorkSpace\\test"));
}
/**
* @Description: 递归遍历文件
* @Param: [file]
* @return: void
*/
public void test(File file){//处理一个file对象,如果是文件直接输出,如果是目录则遍历目录下子文件和子目录
//先判断这个File对象是文件还是目录
if(file.isFile()){//是文件直接输出名称
System.out.println(file.getAbsoluteFile()+"是文件");
}else{//是目录则遍历目录下的file对象,并调用自身test方法再次处理子file对象
System.out.println(file.getAbsoluteFile()+"是目录");
//如果是文件夹(目录),内部可能有子文件夹或者文件
File[] fstr = file.listFiles();
//优化一下,当目录下为空(如果file是文件则file.listFiles返回null),或长度为0时(file为空目录则返回空数组,长度为0)不处理(文件夹的长度会比其下文件长度长一点点)
if(fstr != null && fstr.length>0){
for(File f:fstr){
test(f);//递归,调用方法自身去处理该目录下的子文件or子目录
}
}
}
}
}
2 IO原理及流的分类(IO指input和output)
流是什么?IO流原理
例如通过程序把一个图放到某一个文件夹,要怎么完成,把图片转化成一个数据集(例如二进制),把这些数据一点一点传到文件件。这个传递的过程很类似于水的流动,我们就可以称这个整体的数据集是一个数据流。
流的分类分为两种,一个是文件流一个是缓冲流。
- 文件流都是File开头,数据流的读写都是基于文件的操作。
- 缓冲流Buffered开头,数据流的读写是基于内存的操作。
输入流和输出流
- 输入流必须保证,要读取的文件存在,否则出异常
- 输出流,在写入一个文件时,如果目录下有同名文件将被覆盖
字符流和字节流
- 字节流以字节(1 byte字节 == 8bit比特)为单位,字符流以字符为单位,根据码表映射字符,一次可能读多个字节。ASCII 码中,一个英文字母字符(不分大小写)为一个字节,一个中文汉字字符为两个字节。
- 字节流能处理所有类型的数据(如图片、avi等),而字符流只能处理字符类型的数据。
总结输入输出的数据类型:
字节输入时,按byte数组接收
byte[] b = new byte[1024];
len = bfin.read(b)
字节输出时,按byte数组输出
String str = "哈哈,hello,barbie,把这段话输出";
bfout.write(str.getBytes());
字符输入时,用char数组接收
char[] ch = new char[20];
len = fr.read(ch)
字符输出时,可以用String类输出;也可以是char数组, 3个参数的形式
//参数String text
fw.write(text);
char[] ch = new char[200];
len = fr.read(ch)//先读
fw.write(ch, 0, len);//再写
3 文件流
FileInputStream/FileOutputStream
- 输入流用到的方法
FileInputStream in = new FileInputStream("D:\\MyAll\\study\\WorkSpace\\test\\hah.txt");
✔ 方法.read(b);//b在字符流中为char数组,字节流中为byte数组
in.read(b);
//b为btye数组,负责接收in的数据,若读完则返回-1。测试发现如果b数组长度大于数据长度,该方法都是一次读完数据,返回所读数据的长度。如果b长度小于数据长度,需要循环读取会一直刷新b数组的内容,一段一段的读,每次都读够数组长度,如果是中文则会乱码,因为一个中文汉字由两个字节构成;而一个英文字母不论大小写都由一个字节构成。
System.out.println(new String(b, 0, len));
//读到数组b内输出,new String(b)j将b数组转化为String字符串,并且0为开始转化位置,len为结束转化位置
in.close();
//流在使用完毕后,一定要关闭
- 输出流用到的方法
//outFile开始时不存在,输出后自动生成;如果开始时outFile存在,则刷新文件内容
FileOutputStream out = new FileOutputStream("D:\\MyAll\\study\\WorkSpace\\test\\outFile.txt");
✔ 方法.write(str.getBytes()); .write(str); .write(ch,0,len);//str表示要输出的文本为String类型,ch为char数组。其中字节流输出时必须为byte数组,字符流可以是char数组和String
out.write(str.getBytes());
//把数据写到内存,可以看到文件内容变化
//str.getBytes()将String字符串str转化为byte类型的数组并返回
out.flush();
//把内存中的数据刷写到硬盘。目前不会观测效果
out.close();
//关闭流
import java.io.FileInputStream;
import java.io.FileOutputStream;
/**
* Created with IntelliJ IDEA.
*
* @Author: xuexuezi
* @Date: 2022/12/02/20:35
* @Description: 文件流
*/
public class FileStreamTest {
public static void main(String[] args) {
FileStreamTest.testFileInputStream();
FileStreamTest.testFileOutputStream();
}
/**
* @Description: 文件字节输入流
* @Param: []
* @return: void
*/
public static void testFileInputStream() {
//==========================================FileInputStream========================================
//io操作都是有异常的,需要捕获或者抛出
try {
FileInputStream in = new FileInputStream("D:\\MyAll\\study\\WorkSpace\\test\\hah.txt");//必须要是存在的文件
//InputStream为字节流,做一个比特数组用来接收数据
byte[] b = new byte[10];//设置一个byte数组,接收读取的文件的内容
int len = 0;//设置一个读取数据的长度
/*该方法会有一个返回值,返回值是读取的数据的长度,如果读取到最后一个数据,还会向后读一个,这个时候返回值就是-1
也就意味着当in.read(b)的返回值是-1的时候,整个文件就读取完了
in.read(b);//b数组接收in的输入字节流 */
//注意:new String三个参数的用法(要转化为String类的数组名,开始转化位置,转化结束位置)
while ((len = in.read(b)) != -1) {//说明没读完
System.out.println(len);//0//因为疑惑in.read的返回值,发现此处while和if没有区别。第一次返回值即为数据长度(原因,因为数据太短)
//只读取刚好完即可,不会多读
System.out.println(new String(b, 0, len));
/*?一个疑惑:循环输出与单句输出的内容完全一致,解决:是因为数据太短,
当数据比较长时,每次read都会新读进来一段存到b然后根据[0,len]输出。而且读满的情况下len是固定的长度10
//例1输出:
5
qjhfu
例2输出:
10
把这个�
10
��件的�
10
�容输入
10
到计算�
2
��
*/
}
//优化前,固定b数组长度,可能会比要读取的文件内容长。
//new String一个参数的用法(要转化为String的数组名)
//System.out.println(new String(b));//输出b数组,并转为String类型
in.close();//注意:流在使用完毕之后一定要关闭
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* @Description: 文件字节输出流
* @Param: []
* @return: void
*/
public static void testFileOutputStream() {
//=========================FileOutputStream============================================
try{
//outFile开始时不存在,输出后自动生成;如果开始时outFile存在,则刷新文件内容
FileOutputStream out = new FileOutputStream("D:\\MyAll\\study\\WorkSpace\\test\\outFile.txt");
String str = "2把这段文字输出到outFile文件";
out.write(str.getBytes());//把数据写到内存,可以看到文件内容变化
out.flush();//把内存中的数据刷写到硬盘。目前不会观测效果
out.close();//关闭流
}catch(Exception e){
e.printStackTrace();
}
}
}
✔ 练习:编写程序,把一个文件复制到指定的文件夹下
- 注意:文件字节流非常通用,可以用来操作字符的文档,还可以操作任何的其他类型文件(图片、压缩包等 ),因为字节流直接使用二进制
public static void main(String[] args) {
//复制文件到一个文件夹下
File file1 = new File("D:\\MyAll\\study\\WorkSpace\\test\\abc\\img.png");
File file2 = new File("D:\\MyAll\\study\\WorkSpace\\test\\cc");
new Test().copyFile(file1,file2);
}
/**
* @Description: 把一个文件复制到指定的文件夹下,file1为该文件,file2为该目录
* @Param: [file1, file2]
* @return: void
*/
public void copyFile(File file1, File file2){
if (file1.isFile() && file2.isDirectory()) {
//通过文件字节流复制其内容
//思路,将文件内容读取到内存,用一个byte数组接收,再将其输出到指定路径下,将自动创建新文件
try {
FileInputStream in = new FileInputStream(file1);//把file1的内容输入进来,读的源文件
//输出到新路径下
FileOutputStream out = new FileOutputStream(file2 + File.separator + file1.getName());//复制到哪里
byte[] b = new byte[200];//做大一点,因为b作为接收对象必须接收所有数据,不可以比文件中数据长度短
int len = 0;
while ((len = in.read(b)) != -1) {
System.out.println(new String(b, 0, len));//输出一下要复制的数据
out.write(b);//如果b长度小于数据长度,可能会读不全。直接在循环里交互,即使短也能按段复制
}
out.flush();//把写到内存的数据刷到硬盘
out.close();//后开的先关
in.close();
} catch (Exception e) {
e.printStackTrace();
}
}
else{
System.out.println("输入错误,请重新输入。请保证file1为文件,file2为目录。该方法用来将file1复制到file2下");
return;
}
}
FileReader/FileWriter
- 字符流只适合操作内容是字符的文件例如txt文档
import java.io.FileReader;
import java.io.FileWriter;
/**
* Created with IntelliJ IDEA.
*
* @Author: xuexuezi
* @Date: 2022/12/03/23:10
* @Description: 文件字符流
*/
public class FileRWTest {
public static void main(String[] args){
FileRWTest.testFileReader("D:\\MyAll\\study\\WorkSpace\\test\\FileReader.txt");//FileReader.txt开始时存在
FileRWTest.testFileWriter("把这段字符输出到Filewriter。txt","D:\\MyAll\\study\\WorkSpace\\test\\FileWriter.txt");//FileWriter.txt开始时不存在
}
public static void testFileReader(String inPath){
try{
FileReader fr = new FileReader(inPath);
char[] ch = new char[20];
int len = 0;
while((len = fr.read(ch)) != -1){
System.out.println(new String(ch, 0, len));
}
fr.close();
}catch(Exception e){
}
}
public static void testFileWriter(String text, String outPath){
try{
FileWriter fw = new FileWriter(outPath);
fw.write(text);//写到内存中
fw.flush();//把内存中数据刷写到硬盘
fw.close();//关闭流
}catch(Exception e){
e.printStackTrace();
}
}
}
- 拷贝文件,顺便可以重命名
public static void main(String[] args){
FileRWTest.copyFile("D:\\MyAll\\study\\WorkSpace\\test\\cc\\dd\\文件.txt","D:\\MyAll\\study\\WorkSpace\\test\\abca\\新文件.txt");
}
public static void copyFile(String oldFile, String newFile){
try{
FileReader fr = new FileReader(oldFile);
FileWriter fw = new FileWriter(newFile);
char[] ch = new char[200];//字符数组接收读到的数据
int len = 0;
while((len = fr.read(ch)) != -1){
fw.write(ch, 0, len);//把读到的数据按照字符格式输出
}
fw.flush();
fw.close();
fr.close();
}catch(Exception e){
e.printStackTrace();
}
}
注意:
定义文件路径时,注意:可以用“/”或者“\”。
在写入一个文件时,如果目录下有同名文件将被覆盖。
在读取文件时,必须保证文件已存在,否则出异常。
4 处理流1:缓冲流
BufferedInputStream/BufferedOutputStream
- 缓冲字节输入/输出流
package qxcto.chapter10;
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.FileInputStream;
import java.io.FileOutputStream;
/**
* Created with IntelliJ IDEA.
*
* @Author: xuexuezi
* @Date: 2022/12/04/0:42
* @Description: 缓冲字节流
*/
public class BufferedStreamTest {
public static void main(String[] args){
//因为两个方法都把可能的异常抛出了,调用的时候需要接收一下
try{
BufferedStreamTest.testBufferedInputStream();
BufferedStreamTest.testBufferedOutputStream();
}catch(Exception e){
e.printStackTrace();
}
}
/**
* @Description: 缓冲字节输入流
* @Param: []
* @return: void
*/
public static void testBufferedInputStream() throws Exception {
//文件字节输入流对象
FileInputStream fin = new FileInputStream("D:\\MyAll\\study\\WorkSpace\\IDEA_Project\\ten_IO\\src\\qxcto\\chapter10\\inTt.txt");
//缓冲字节输入流对象
//把这个文件字节流放入到这个缓冲输入流当中
BufferedInputStream bfin = new BufferedInputStream(fin);//参数需要一个InputStream的或者其子类的对象
byte[] b = new byte[1024];
int len = 0;
while((len = bfin.read(b)) != -1){
System.out.println(new String(b, 0 ,len));
}
//关闭流的时候,本着一个最晚开的最早关,依次关
bfin.close();//后开的先关,一个嵌套样式
fin.close();
}
/**
* @Description: 缓冲字节输出流
* @Param: []
* @return: void
*/
public static void testBufferedOutputStream() throws Exception{
//创建字节输出流对象
FileOutputStream fout = new FileOutputStream("D:\\MyAll\\study\\WorkSpace\\IDEA_Project\\ten_IO\\src\\qxcto\\chapter10\\outTt.txt");
//创建字节输入流对象
BufferedOutputStream bfout = new BufferedOutputStream(fout);
String str = "哈哈,hello,barbie,把这段话输出";
bfout.write(str.getBytes());//写到内存中
bfout.flush();//刷到硬盘上
bfout.close();
fout.close();
}
}
- 复制文件到新路径下
public static void main(String[] args) {
BufferedStreamTest.bfSCopyFile("D:\\MyAll\\study\\WorkSpace\\test\\abca.zip","D:\\MyAll\\study\\WorkSpace\\test\\haha\\wex\\abcaCopy.zip");
}
/**
* @Description: 缓冲字节流复制文件
* @Param: oldFile 原文件的原路径;输入的内容
* @Param newFile 要复制到的新路径,还可以顺便改名字;输出的内容
* @return: void
*/
public static void bfSCopyFile(String oldFile, String newFile){
try{
BufferedInputStream bfin = new BufferedInputStream(new FileInputStream(oldFile));
BufferedOutputStream bfout = new BufferedOutputStream(new FileOutputStream(newFile));
byte[] by = new byte[1024];
int len = 0;
System.out.println("打印一下读到的内容");
while((len = bfin.read(by)) != -1){
System.out.println(by);
bfout.write(by, 0 , len);//写到内存
}
bfout.flush();//刷到硬盘
bfout.close();
bfin.close();
}catch(Exception e){
e.printStackTrace();
}
}
}
BufferedReader/BufferedWriter
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.FileReader;
import java.io.FileWriter;
/**
* Created with IntelliJ IDEA.
*
* @Author: xuexuezi
* @Date: 2022/12/04/0:43
* @Description: 缓冲字符流
*/
public class BufferedRWTest {
public static void main(String[] args){
try {
BufferedRWTest.testBufferedReader("D:\\MyAll\\study\\WorkSpace\\test\\FileReader.txt");
BufferedRWTest.testBufferedWriter("缓冲字符流进行输出", "D:\\MyAll\\study\\WorkSpace\\test\\FileWriter.txt");
}catch(Exception e){
e.printStackTrace();
}
}
/**
* @Description: 缓冲字符输入流
* @Param: inPath
* @return: void
*/
public static void testBufferedReader (String inPath)throws Exception{
BufferedReader bfR = new BufferedReader(new FileReader(inPath));
char[] ch = new char[1024];
int len = 0;
while((len = bfR.read(ch)) != -1){
System.out.println(new String(ch, 0, len));
}
bfR.close();
}
/**
* @Description: 缓冲字符输出流
* @Param: text
* @Param outPath
* @return: void
*/
public static void testBufferedWriter(String text, String outPath)throws Exception{
BufferedWriter bfW = new BufferedWriter(new FileWriter(outPath));
bfW.write(text);
bfW.flush();
bfW.close();
}
}
- 缓冲字符流复制文件
public static void main(String[] args){
try {
BufferedRWTest.copyBfRW("D:\\MyAll\\study\\WorkSpace\\test\\cc\\dd\\文件.txt","D:\\MyAll\\study\\WorkSpace\\test\\abcabc\\新文件.txt");
}catch(Exception e){
e.printStackTrace();
}
}
/**
* @Description: 缓冲字符流复制文件
* @Param: oldFile
* @Param newFile
* @return: void
*/
public static void copyBfRW(String oldFile, String newFile) throws Exception{
BufferedReader bfR = new BufferedReader(new FileReader(oldFile));
BufferedWriter bfW = new BufferedWriter(new FileWriter(newFile));
char[] ch = new char[1024];
int len = 0;
while((len = bfR.read(ch)) != -1){
bfW.write(ch, 0 ,len);
}
bfW.flush();
bfW.close();
bfR.close();
}
}
5 处理流2:转换流(InputStreamReasder/OutputStreamWriter)
可以把字节流转换成字符流。
当字节流中的数据都是字符的时候,使用转换流转为字符流处理
-
查看项目编码
-
查看类编码格式
-
转换输入流
public static void main(String[] args){
//所有的文件都是又编码格式的
//对于我们来说,txt和java文件一般来讲有三种编码
//ISO8859-1,西欧编码,是纯粹英文编码,不适用汉字
//GBK和UTF-8,这两编码是适用于中文和英文
//我们一般使用UTF-8这种编码
try{
StreamRWTest.testInputStreamReader();
}catch(Exception e){
e.printStackTrace();
}
}
/**
* 转换输入流
* 把字节流转换成字符流
*/
public static void testInputStreamReader() throws Exception{
FileInputStream fin = new FileInputStream("D:\\MyAll\\study\\WorkSpace\\IDEA_Project\\ten_IO\\src\\qxcto\\chapter10\\tt5.txt");
//将字节流转换成字符流
InputStreamReader inR= new InputStreamReader(fin,"UTF-8");//参数1是字节流,参数2是编码
char[] ch = new char[100];//因为转换成字符流,用char数组接收
int len = 0;
while((len = inR.read(ch)) != -1){
System.out.println(new String(ch, 0, len));
}
inR.close();
fin.close();
}
-
- 正确输出结果
-
- 错误输出结果
如果转换时参数2的编码不对应文件的编码格式
- 转换输出流
public static void main(String[] args){
//所有的文件都是又编码格式的
//对于我们来说,txt和java文件一般来讲有三种编码
//ISO8859-1,西欧编码,是纯粹英文编码,不适用汉字
//GBK和UTF-8,这两编码是适用于中文和英文
//我们一般使用UTF-8这种编码
try{
StreamRWTest.testOutputStreamWriter();
}catch(Exception e){
e.printStackTrace();
}
}
/*
*转换输出流
* 把字节流转换为字符流
*/
public static void testOutputStreamWriter() throws Exception{
//该路径当前不存在,第一次输出自动创建,因为项目设置编码格式为UTF-8,能确定创建tt6.txt后也为UTF-8编码格式
FileOutputStream fout = new FileOutputStream("D:\\MyAll\\study\\WorkSpace\\IDEA_Project\\ten_IO\\src\\qxcto\\chapter10\\tt6.txt");
OutputStreamWriter outW = new OutputStreamWriter(fout,"GBK");
String str = "输出到tt6.txt";
outW.write(str);
outW.flush();
outW.close();
fout.close();
}
-
- 正确输出结果
-
- 错误输出
文件本身为UTF-8的编码格式,转换时参数2提供为GBK格式,则输出乱码
- 错误输出
此时再讲tt6.txt改为GBK
刚改完输出就正常,无需运行
6 处理流3:标准输入/输出流
✔ 方法 .readLine();//是字符流的方法
该方法是字符流的方法,
与.read(b)的区别在于:
1.使用方
- 字符流和字节流都可以使用.read();
- 目前只见过BufferedReader的对象使用readLine(),且缓冲字符输入流接收了标准输入流
2.输入数据的过程
- 字符流.read(char数组);字节流.read(byte数组);//是将流里的数据输入到数组中,一个字符(字节)的读
- str = BufferedReader.read();是将缓冲流中的数据传入str,一行一行的读
3.返回值
- .read(b)的返回值是一个int长度,表示读取到的数据长度。读到最后一个数据的后一位返回-1表示读取结束。受制于接收的数组长度,如果数组长度大于数据长度,读一次就可读完,但如果数组长度比较短,就需要循环读,且每次读到的数据长度(除过最后一次)都等于数组长度。为了防止读多,一般接收这个返回值len作为输出时的长度,将用new String(b, 0, len)来输出数组中的数据。
- .readLine()的返回值是一个String字 符串为读到的数据,一行一行读,每次都用str接收一下,也是循环接收。直到读到最后一个最后一个字符之后的下一个,就是空null。
例1:bfR为接收了标准输入的缓冲字符输入流
String str = "";//定义一个临时接收数据的字符串
//.readLine返回的是一个字符串,如果要是读到最后一个最后一个字符之后的下一个,就是空null
while((str = bfR.readLine()) != null){
System.out.println(str);
}
✔ 练习:1.把控制台输入的内容写到指定的txt文件中,当接收到字符串over,就结束程序的运行。
复习equals == :
- ==对于基础数据比较内容,对于引用类只有同一个对象才true
- equals只能比较对象,只有对于File,String,Data和包装类是比较内容,不要求是同一个对象。其他类都是必须为同一个对象才相等true
public static void main(String[] args){
try{
//控制台输入的数据输出到该txt文件,开始时不存在
Test3.testSystemIn("D:\\MyAll\\study\\WorkSpace\\IDEA_Project\\ten_IO\\src\\qxcto\\chapter10\\systemIn.txt");
}catch(Exception e){
e.printStackTrace();
}
}
/*控制台输入的内容写到指定的txt文件中,当接收到字符串over,就结束程序的运行
*
*/
//我的写法:理解怎么使用标准输入流来接收控制台输入的内容,开始想使用字节流但发现readLine方法只能是字符流用
//全篇使用字符流(为什么呢,因为readLine是字符流的方法),用缓冲字节输出流接收新建一个文件字节输出流来确认输出txt的路径,再用缓冲字节输入流接收标准输入流
//定义String空字符串作为一个临时接收字符串的变量
//while读,读到null说明读完,循环体内写
public static void testSystemIn(String txtPath)throws Exception{
//创建一个输入转换流,把标准输入转为字符流
InputStreamReader inR = new InputStreamReader(System.in);
//创建一个缓冲字节输入流,把输入流放入缓冲流
BufferedReader bfR = new BufferedReader(inR);
创建一个缓冲字节输出流,用来接收到数据后输出到txt文件,缓冲流接收一下文件字节输出流会快一点
BufferedWriter bfOut = new BufferedWriter(new FileWriter(txtPath));
String line ="";//定义一个临时接收字符的字符串,而且是一行一行的读
//这里需要考虑实现读到over结束字符
//复习:== 对于基础数据比较内容,对于引用类只有同一个对象才true
//equals只能比较对象,只有对于File,String,Data和包装类是比较内容,不要求是同一个对象。其他类都是必须为同一个对象才相等true
while((line = bfR.readLine()) != null){
if(line.equals("over")){
//中断逻辑
break;
}
//读取的每一行都写到指定的txt文件中
bfOut.write(line);
}
bfOut.flush();
bfOut.close();
bfR.close();
inR.close();
}
✔ 练习 2.在一个txt文件中,写一组用户名和密码,通过控制台输入用户名和密码,与txt文件中的用户名和密码做对比,如果一样就在控制台打印登陆成功,如果不一致就打印用户名或密码错误。
/*练习2:在一个txt文件中,写一组用户名和密码,通过控制台输入用户名和密码,
与txt文件中的用户名和密码做对比,如果一样就在控制台打印登陆成功,如果不一致就打印用户名或密码错误。*/
//
public static void testPass(String txtPath) throws Exception{
//创建一个转换流,转换标准输入为字符流
InputStreamReader inR = new InputStreamReader(System.in);
//创建一个缓冲输入流,接收控制台的输入
BufferedReader bfR = new BufferedReader(inR);
//接收txt文件的输入
BufferedReader bfRtxt = new BufferedReader(new FileReader(txtPath));
String line = "";//一个字符串临时接收一下控制台输入的每行的字符串
String linetxt = "";//一个字符串临时接收一下从txt输入的每行的字符串
//循环两次,接收两行数据
boolean bf = true;//定义一个判断是否登录成功的标志,因为我们不能让他用户名一错误就break,这样没机会输入密码
System.out.println("请输入用户名和密码,回车隔开");
for(int i=0; i<2; i++){
line = bfR.readLine();
linetxt = bfRtxt.readLine();
if(! line.equals(linetxt)){
bf = false;
}
}
if(bf == true){
System.out.println("登陆成功");
}else{
System.out.println("用户名或密码错误");
}
bfRtxt.close();
bfR.close();
inR.close();
}
7 处理流4:打印流(了解)
PrintStream/PrintWriter(System.out.println)
8 处理流5:数据流(了解)
DataInputStream/DataOutputStream
- 输出流
用数据输出流写到文件中的基本数据类型是乱码的,不能直接辨认。需要数据层的输入流来读取
public static void main(String[] args) {
try{
DataInOutTest.testDataOutputStream();
}catch(Exception e){
e.printStackTrace();
}
}
/**
* @return: void
* @Param:
* @Description: 数据输出流
* 用数据输出流写到文件中的基本数据类型是乱码的,不能直接辨认。
* 需要数据层的输入流来读取
* DataOutputStream
*/
public static void testDataOutputStream()throws Exception{
DataOutputStream dout = new DataOutputStream(new FileOutputStream("D:\\MyAll\\study\\WorkSpace\\IDEA_Project\\ten_IO\\src\\qxcto\\chapter10\\dataInOut.txt"));//需要一个OutputStream类型的参数,子类都可以
//dout.writeBoolean(true);
dout.writeDouble(7.56d);
//dout.writeInt(345);
dout.flush();
dout.close();
}
- 输入流
-
- 用数据输出流写到文件中的基本数据类型是乱码的,不能直接辨认。需要数据层的输入流来读取
-
- 用数据输入流读取数据输出流写到文件中的数据时,要保证使用和当时写的数据类型一致的类型来读取
-
- 也就是说,如果写的时候是writeDouble,那么读的时候就得是readDouble
-
public static void main(String[] args) {
try{
DataInOutTest.testDataOutputStream();
DataInOutTest.testDataInputStream();
}catch(Exception e){
e.printStackTrace();
}
}
/**
* @return: void
* @Param:
* @Description: 数据输出流
* 用数据输出流写到文件中的基本数据类型是乱码的,不能直接辨认。
* 需要数据层的输入流来读取
* DataOutputStream
*/
public static void testDataOutputStream()throws Exception{
DataOutputStream dout = new DataOutputStream(new FileOutputStream("D:\\MyAll\\study\\WorkSpace\\IDEA_Project\\ten_IO\\src\\qxcto\\chapter10\\dataInOut.txt"));//需要一个OutputStream类型的参数,子类都可以
//dout.writeBoolean(true);
dout.writeDouble(7.56d);
//dout.writeInt(345);
dout.flush();
dout.close();
}
/**
* @return: void
* @Param:
* @Description: 数据的输入流,
* 用数据输出流写到文件中的基本数据类型是乱码的,不能直接辨认。需要数据层的输入流来读取
* 用数据输入流读取数据输出流写到文件中的数据时,要保证使用和当时写的数据类型一致的类型来读取
* 也就是说,如果写的时候是writeDouble,那么读的时候就得是readDouble
*/
public static void testDataInputStream()throws Exception{
//把刚输入的那个文件再输入一下,为了识别输出的数据,因为无法手动识别
DataInputStream din = new DataInputStream(new FileInputStream("D:\\\\MyAll\\\\study\\\\WorkSpace\\\\IDEA_Project\\\\ten_IO\\\\src\\\\qxcto\\\\chapter10\\\\dataInOut.txt"));
System.out.println(din.readDouble());
din.close();
}
写用writeDouble,读用readInt。则读出来不是本身的值
写用writeDouble,读用readBolean。则读出来不是本身的值
9 处理流6:对象流—涉及序列化、反序列化
ObjectInputStream/ObjectOutputStream(把一个对象转化为一个数据流进行读写)
对象的序列与反序列化使用的类要严格一致,包名,类名,类结构等等所有都要一致。所有东西都要一致
- 对象的序列化
public static void main(String[] args) {
try{
Test4.testSerialize();
}catch(Exception e){
e.printStackTrace();
}
}
/**
* 对象的序列化
*/
public static void testSerialize()throws Exception{
//定义对象的输出流,把对象序列化之后的流放到指定的文件中
ObjectOutputStream oout = new ObjectOutputStream(new FileOutputStream("D:\\MyAll\\study\\WorkSpace\\IDEA_Project\\ten_IO\\src\\qxcto\\chapter10\\oout.txt"));
Person p1 = new Person();
p1.name = "张三";
p1.age = 17;
oout.writeObject(p1);
oout.flush();//刷写数据到硬盘
oout.close();
}
- 反序列化
public static void main(String[] args) {
try{
Test4.testSerialize();
Test4.testDeserialize();
}catch(Exception e){
e.printStackTrace();
}
}
/**
* 对象的序列化
*/
public static void testSerialize()throws Exception{
//定义对象的输出流,把对象序列化之后的流放到指定的文件中
ObjectOutputStream oout = new ObjectOutputStream(new FileOutputStream("D:\\MyAll\\study\\WorkSpace\\IDEA_Project\\ten_IO\\src\\qxcto\\chapter10\\oout.txt"));
Person p1 = new Person();
p1.name = "张三";
p1.age = 17;
oout.writeObject(p1);
oout.flush();//刷写数据到硬盘
oout.close();
}
/**
* 对象的反序列化
* @throws Exception
*/
public static void testDeserialize()throws Exception{
//把对象流读回来
//创建对象输入流,从指定的文件中把对象的序列化后的流读取出来
ObjectInputStream oin = new ObjectInputStream(new FileInputStream("D:\\MyAll\\study\\WorkSpace\\IDEA_Project\\ten_IO\\src\\qxcto\\chapter10\\oout.txt"));
Object obj = oin.readObject();
Person p = (Person)obj;
//Person p = (Person)obj;
//这个时候序列化与反序列化使用的类不是一个类,分别是qxcto.chapter10.Person和qxcto.chapter10.object.Person
//这是反序列化就有异常qxcto.chapter10.Person cannot be cast to qxcto.chapter10.object.Person
//类型转换异常,哪怕两个类的结构一致
//qxcto.chapter10.object.Person p = (qxcto.chapter10.object.Person)obj;
System.out.println(p.name);
System.out.println(p.age);
oin.close();
}
- 这个时候序列化与反序列化使用的类不是一个类,错误
-
- 两个类的结构完全一致,但不是一个类
10 随机存取文件流
RandomAccessFile(例如一个txt文件,其中有100行数据,可以直接读取第50行的数据,也可以在第89行插入数据)
- 随机读
public static void main(String[] args) {
try{
RandomTest.testRandomRead();
}catch(Exception e){
e.printStackTrace();
}
}
public static void testRandomRead() throws Exception{
// r: 以只读方式打开
// rw:打开以便读取和写入
// rwd:打开以便读取和写入;同步文件内容的更新
// rws:打开以便读取和写入;同步文件内容和元数据的更新
//最常用的是r和rw
//RandomAccessFile的构造有两个参数,参数1是读写的文件的路径
//参数2是指定RandomAccessFile的访问方式
RandomAccessFile ra = new RandomAccessFile("D:\\MyAll\\study\\WorkSpace\\IDEA_Project\\ten_IO\\src\\qxcto\\chapter10\\RandomTest.txt","r");
//通过设置读取文件内容的起始点,来达到从文件的任意位置读取
ra.seek(13);//设置读文件内容的起始点
byte[] b = new byte[1024];//可见随机存取流也是字节流
int len = 0;
while((len = ra.read(b)) != -1){
System.out.println(new String(b, 0, len));
}
}
- 随机写
-
- 如果是在开头或者中间的某个位置开始写的话,就会用写的内容覆盖掉等长度的原内容
public static void main(String[] args) {
try{
//RandomTest.testRandomRead();
RandomTest.testRandomWriter();
}catch(Exception e){
e.printStackTrace();
}
}
//随机写
public static void testRandomWriter() throws Exception{
//因为要写入,记得设置为rw可读写,如果是r只读就错了
RandomAccessFile ra = new RandomAccessFile("D:\\MyAll\\study\\WorkSpace\\IDEA_Project\\ten_IO\\src\\qxcto\\chapter10\\RandomTest.txt","rw");
//ra.seek(6);//设置写的起始点,0代表从头开始写
//注意:如果是在开头或者中间的某个位置开始写的话,就会用写的内容覆盖掉等长度的原内容
ra.seek(ra.length());//设置写的起点,ra.length()代表从文件的最后结尾写,也就是文件的追加
ra.write("咋们在尾部追加,以免覆盖".getBytes());
ra.close();//不用flush
}
流小节