菜鸟学习笔记:Java IO流1——IO流的概念、字节流、字符流、缓冲流、转换流
- IO流的原理及概念
- 节点流
-
- 字节流
-
- 文件读取
- 文件写出
- 文件拷贝
- 文件夹拷贝
- 字符流
-
- 文件读取
- 文件写出
- 处理流
-
- 缓冲流
- 转换流
-
- 编码和解码
- 转换流实现
IO流的原理及概念
在基础篇的讲解中大家已经了解了File对象以及它的一些方法(复习链接)。但是File的主要作用仅仅是建立文件和程序的联系,File对象的方法只能用于文件和目录的创建、文件的查找和文件的删除等功能,但无法对文件的内容做具体的操作。所以引入了现在所要讲的IO流知识点。
在Java程序中,对数据的输入输出操作以“流”的方式进行:
如果大家觉得不好理解可以这样想,我们要读取的文件好比一个装满水的桶,文件内容就是里面的水,我们的流相当于一根管道,一端接文件,一端接程序,读取文件就相当于把水抽过来,,放到程序里。不过不一样的一点是读取文件后原文件内容不变,也就是抽水过程不影响文件桶里的水,抽的水凭空产生。
对于IO流按不同的角度可以分成不同的类:
- 如果以数据流向可以分为输入流(程序从文件抽水)和输出流(文件从程序抽水)。
- 如果以读取数据的基本单元来划分可以分为字节流(读取基本单位是字节)和字符流(读取基本单位是字符)。这里稍微讲解一下字节和字符,字节是计算机存储的一个基本单位,它由8个2进制位组成。而一个字符表示一个文字所占的大小,为2个字节。一般情况下字节流可以用来读取一切文件,而字符流只能读取文本文件。
- 如果按照功能来划分又可以把IO流划分为节点流(基本读取用)和处理流(包裹节点流,提高性能)。
节点流
字节流
由于读取的基本单位是字节,字节是文件的基本构成单位,所以字节流可以处理一切图片、视频音频、文本文件。InputStream和OutputStream类是Java中字节流的实现,通过这两个类所提供的方法,可以实现用字节流文件读写。
文件读取
对于流的操作我们都用代码来呈现,为方便读取代码,首先讲解几个点:
- FileInputStream是InputStream的子类,可以用来建立文件与程序间的流
- FileInputStream类的read(byte[] b)方法用来读取文件,b表示一个读取用的容器,每次读取的内容会存放到b中,该方法会返回每次读取文件的长度,如果读取内容为空会返回-1,也就是读取完成。
- jdk1.7之前读取完成需要手动调用close()方法关闭流。
有了这些知识基础我们就可以看实例代码了,这段代码的功能就是从文件中读取内容并输出,首先看我们的文件:
public static void main(String[] args) {
//1、建立联系 File对象
File src =new File("D:/testFile/haha.txt");
//2、选择流(输入字节流)
//注意流需要在读取结束后关闭,必须放在trycatch之外。jdk1.7之后可不用这样
InputStream is =null;
//首先IO操作会产生FileNotFoundException和IOException两个异常,需要捕获出路
try {
//建立程序和文件之间的流(之前例子里的水管)
is =new FileInputStream(src);
//3、操作 不断读取 缓冲数组
byte[] car =new byte[1024];
int len =0; //接收 实际读取大小
//循环读取
StringBuilder sb =new StringBuilder();
while(-1!=(len=is.read(car))){
//输出 字节数组转成字符串
String info =new String(car,0,len);
sb.append(info);
}
System.out.println(sb.toString());//打印haha
} catch (FileNotFoundException e) {
e.printStackTrace();
System.out.println("文件不存在");
} catch (IOException e) {
e.printStackTrace();
System.out.println("读取文件失败");
}finally{
try {
//4、释放资源
if (null != is) {
is.close();
}
} catch (Exception e2) {
System.out.println("关闭文件输入流失败");
}
}
}
程序效果
文件写出
和读入对应
- 写出文件所用的是FileOutputStream(dest,true),构造方法中第一个参数表示要写入的文件对象。第二个参数为true表示如果文件存在则追加内容 ,false表示覆盖文件内容。
- 写入文件用的是write(data,start,end)方法,data必须为byte[]类型,可以指定开始和结束位置。
-
flush()方法的作用是将存放在流中的数据强行写道文件中防止写出文件不完整的情况。
输出流的代码如下:
public static void main(String[] args) {
//1、建立联系 File对象 目的地
File dest =new File("D:/testFile/houhou.txt");
//2、选择流 文件输出流 OutputStream FileOutputStream
OutputStream os =null;
//以追加形式 写出文件 必须为true 否则为覆盖
try {
os =new FileOutputStream(dest,true);
//3、操作
String str="very good \r\n";
//字符串转字节数组
byte[] data =str.getBytes();
os.write(data,0,data.length);
os.flush(); //强制刷新出去
} catch (FileNotFoundException e) {
e.printStackTrace();
System.out.println("文件未找到");
} catch (IOException e) {
e.printStackTrace();
System.out.println("文件写出失败");
}finally{
//4、释放资源 :关闭
try {
if (null != os) {
os.close();
}
} catch (Exception e2) {
System.out.println("关闭输出流失败");
}
}
}
程序结果:
文件拷贝
将以上两个流结合就可以得到文件拷贝的代码:
public class FileUtil {
public static void copyFile(String srcPath,String destPath) throws FileNotFoundException,IOException {
//1、建立联系 源(存在且为文件) +目的地(文件可以不存在)
File src =new File(srcPath);
File dest =new File(destPath);
if(! src.isFile()){ //不是文件或者为null
System.out.println("只能拷贝文件");
throw new IOException("只能拷贝文件");
}
//2、选择流
InputStream is =new FileInputStream(src);
OutputStream os =new FileOutputStream(dest);
//3、文件拷贝 循环+读取+写出
byte[] flush =new byte[1024];
int len =0;
//读取
while(-1!=(len=is.read(flush))){
//写出
os.write(flush, 0, len);
}
os.flush(); //强制刷出
//关闭流
os.close();
is.close();
}
}
文件夹拷贝
依据上面文件拷贝的案例可以扩展成文件夹的拷贝:
public static void copyDirDetail(File src,File dest) throws FileNotFoundException,IOException{
if(src.isFile()){ //文件
try {
FileUtil.copyFile(src, dest);
} catch (FileNotFoundException e) {
//e.printStackTrace();
throw e;
} catch (IOException e) {
//e.printStackTrace();
throw e;
}
}else if(src.isDirectory()){ //文件夹
//确保目标文件夹存在
dest.mkdirs();
//获取下一级目录|文件
for(File sub:src.listFiles()){
copyDirDetail(sub,new File(dest,sub.getName()));
}
}
}
字符流
与字节流不同,由于只有文本文件的基本组成单位是字符,所以字符流只能读写文档文件。Reader和Writer是类是Java中字节流的实现。
文件读取
字符流需要注意的有几个点:
- 字符流需要创建FileReader类。
- 读取过程建立的不是byte[]而是char[]。
写法和思路与字节流读取基本相同,下面直接看例子。
public static void main(String[] args) {
//创建源
File src =new File("E:/xp/test/a.txt");
//选择流
Reader reader =null;
try {
reader =new FileReader(src);
//读取操作
char[] flush =new char[1024];
int len =0;
while(-1!=(len=reader.read(flush))){
//字符数组转成 字符串
String str =new String(flush,0,len);
System.out.println(str);
}
} catch (FileNotFoundException e) {
e.printStackTrace();
System.out.println("源文件不存在");
} catch (IOException e) {
e.printStackTrace();
System.out.println("文件读取失败");
}finally{
try {
if (null != reader) {
reader.close();
}
} catch (Exception e2) {
}
}
}
文件写出
public static void main(String[] args) {
//创建源
File dest =new File("e:/xp/test/char.txt");
//选择流
Writer wr =null;
try {
//追加文件,而不是覆盖文件
wr =new FileWriter(dest);
//写出
String msg ="追加.....锄禾日当午\r\n码农真辛苦\r\n一本小破书\r\n一读一上午";
wr.write(msg);
//文件追加内容
wr.append("倒萨发了看电视剧 ");
wr.flush();
} catch (FileNotFoundException e) {
e.printStackTrace();
}catch (IOException e) {
e.printStackTrace();
}finally{
try {
if (null != wr) {
wr.close();
}
} catch (Exception e2) {
}
}
}
拷贝与字节流相同,这里不再举例。由于字符流不能处理非文本文件,所以不能用来拷贝文件夹。
处理流
以上所讲述的字节流和字符流都属于节点流,是文件传输的基本流。处理流和节点流不同,它不能用来直接传输数据,而是用来加强节点流的功能,进而提升传输性能。就之前抽水的例子,你可以把想象处理流为在水管上加一个电动抽水器,加快抽水速度。
缓冲流
缓冲流是最常用的处理流,Java中的缓冲流类为BufferedReader和BufferedWriter,它的使用非常简单,直接在构造函数中传入流对象就可以实现,使用方法如下:
reader =new BufferedReader(new FileReader(new File(地址)));
wr =new BufferedWriter(new FileWriter(new File(地址)));
对于缓冲流最常用的操作就是字符流中的readLine方法,它大大简化了读取文件的操作,下面就是一个缓冲流的使用案例:
public static void main(String[] args) {
//创建源 仅限于 字符的纯文本
File src =new File("E:/xp/test/Demo03.java");
File dest =new File("e:/xp/test/char.txt");
//选择流
BufferedReader reader =null;
BufferedWriter wr =null;
try {
reader =new BufferedReader(new FileReader(src));
wr =new BufferedWriter(new FileWriter(dest));
//新增方法的操作
String line =null;
//readLine方法可以按行进行读取,不用在建立字节数组进行接收,大大简化了操作
while(null!=(line=reader.readLine())){
wr.write(line);
//wr.append("\r\n");
wr.newLine(); //换行符号
}
wr.flush();//强制刷出
} catch (FileNotFoundException e) {
e.printStackTrace();
System.out.println("源文件不存在");
} catch (IOException e) {
e.printStackTrace();
System.out.println("文件读取失败");
}finally{
try {
if (null != wr) {
wr.close();
}
} catch (Exception e2) {
}
try {
if (null != reader) {
reader.close();
}
} catch (Exception e2) {
}
}
}
转换流
编码和解码
我们都知道计算机处理的文件称之为二进制文件,但二进制文件不能被我们直接阅读,所以需要将二进制数据翻译为文本数据,这个翻译过程就称为解码,同样我们输入电脑的文本数据也需要翻译成二进制数据读入电脑,这个翻译过程被称为编码。
字符集就是编码和解码过程中用来翻译的工具,但不同的字符集会带来乱码问题乱码问题(在基础篇第一节就有讲解,链接),在文件操作中一不注意就会产生字符集不同的情况。比如我们ecilpse中设置的字符集如下:
如果用GBK的方式打开之前的文件不会有问题(ANSI就是GBK)
我们之前写出的文件的编码设置为UTF-8,就会出现乱码的情况:
为避免这种情况我们需要手动设定编码集。
Java中可以通过String类getBytes()对数据进行编码
public static void test1() throws UnsupportedEncodingException{
//解码 byte -->char
String str ="中国"; //gbk
//编码 char -->byte
byte[] data =str.getBytes();
//编码与解码字符集同一
System.out.println(new String(data));
data =str.getBytes("utf-8"); //设定编码字符集
//不同一出现乱码
System.out.println(new String(data));
//编码
byte[] data2 = "中国".getBytes("utf-8");
//解码
str=new String(data2,"utf-8");
System.out.println(str);
}
转换流就是将字节流转换为字符流的操作,它的主要作用是处理复制文件过程中的乱码问题。
转换流实现
Java中转换流对应的类为OutputStreamWriter和InputStreamReader。注意转换流只能将字节流转化为字符流,没有逆向操作。
指定编码和解码字符集:
public static void main(String[] args) throws IOException {
//指定解码字符集
BufferedReader br =new BufferedReader(//缓冲流
new InputStreamReader(//转换流
new BufferedInputStream(//缓冲流
new FileInputStream( //节点流
new File("E:/xp/test/Demo03.java"))),"UTF-8")//通过转换流指定字符集
);
//写出文件 编码
BufferedWriter bw =new BufferedWriter(//缓冲流
new OutputStreamWriter(//转换流
new BufferedOutputStream( //缓冲流
new FileOutputStream(new //节点流File("E:/xp/test/encode.java")))));
String info =null;
while(null!=(info=br.readLine())){
//System.out.println(info);
bw.write(info);
bw.newLine();
}
bw.flush();
bw.close();
br.close();
}
}
上一篇:菜鸟学习笔记:Java提升篇4(容器4——Collections工具类、其他容器)
下一篇:菜鸟学习笔记:Java提升篇6(IO流2——数据类型处理流、打印流、随机流)