IO流随笔
在java程序中,数据的输入、输出操作以“流”的方式进行
数据源:data source 提供原始数据的原始媒介。常见的:数据库、文件、其他程序、内存、网络连接、IO设备。
流的分类:
流的方向:
-
输入流:数据源到程序(InputStream、Reader读进来)
-
输出流:程序到目的地(OutputStream、Writer写出去)
处理数据单元:
-
字节流:按照字节读取数据(InputStream、OutputStream)
-
字符流:按照字符读取数据(Reader、Writer)
功能不同:
-
节点流:可以直接从数据源或目的地读写数据。
-
处理流(包装流):不直接连接到数据源或目的地,是其他流进行封装。目的主要是简化操作和提高性能。
节点流和处理流的关系:
-
节点流处于IO操作的第一线,所有操作必须通过他们进行
-
处理流可以对其他流进行处理(提高效率或操作灵活性)
字节流:InputStream 字节输出流的顶级类 OutputStream 字节输出流的顶级类
字符流: Writer 字符输入流的顶级类 Reader 字符输出流的顶级类
复制文件:
/**
* 功能:文件复制
* FileInputStrem FileOutputStrem
*
* 此法缺点:
* 中转站太小,此法复制大文件时,效率比较低下,
*/
public class TestCopy1 {
public static void main(String[] args) throws IOException {
//创建一个输入和输入流
// File file = new File("d:/BugReport.txt");
InputStream is = new FileInputStream(new File("d:/BugReport.txt"));
OutputStream os = new FileOutputStream(new File("d:\\BugReport2.txt"));
//使用输入流和输出流完成文件复制
int n; //中转站 ,比较小
//读一个字节
// n = is.read(); //从输入流读取一个字节赋值给n
while ((n= is.read()) != -1){ //没有读完
//写一个字节
os.write(n);
//接着读下一个字节
// n = is.read();
}
//关闭流
is.close();
os.close();
}
}
此方法缺点:当复制大文件时,复制效率低
改进:
public class TestCopy2 {
public static void main(String[] args) throws IOException {
//创建一个输入和输出流
InputStream fis = new FileInputStream("d:/JDK1.6 API帮助文档.CHM");
OutputStream fos = new FileOutputStream("d:/JDK1.6 API帮助文档2.CHM");
//使用输入流和输出流复制文件
//设置中转站
byte[] bytes = new byte[1024];
//读一次字节
int len = 0; //将源文件的内容读取到bytes中,返回真实读取到的字节数
while ((len=fis.read(bytes)) != -1){ //判断没有读完 -1表示已经读取完毕
//写一次字节
// fos.write(bytes); //写出1024字节流的内容
fos.write(bytes,0,len); // 写出0到len字节的内容
}
//关闭流
fis.close();
fos.close();
}
}
字符流复制文件:
public class TestCopyChar1 {
public static void main(String[] args) throws IOException {
//创建一个输入和输入流
Reader fr = new FileReader(new File("d:\\BugReport.txt"));
Writer fw = new FileWriter(new File("d:\\BugRepor2.txt"));
//定义一个中转站
char[] chars = new char[1024];
int len = 0;
while ((len = fr.read(chars)) != -1){ ////判断有没有读取完,,返回-1表示已经读取完毕
//写一次字符
fw.write(chars,0,len);
}
//关闭流
fr.close();
fw.close();
}
}
上面几个示例中都是将异常抛出,接下来修改一下例子,将异常捕获并进行处理:
public class TestCopyChar2 {
public static void main(String[] args){
//创建字符输入流和输出流
Reader fr = null;
Writer fw = null;
try {
fr = new FileReader(new File("d:/JDK1.6 API帮助文档.CHM"));
fw = new FileWriter(new File("d:/JDK1.6 API帮助文档2.CHM"));
//使用输入流和输出流复制文件
//定义一个中转站
char[] chars = new char[1024];
int len;
while ((len = fr.read(chars))!=-1){ //判断有没有读取完,,返回-1表示已经读取完毕
//写出一次字符
fw.write(chars,0,len);
}
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}finally {
//关闭流
try {
if (fr != null ){ //判断当输入流不为空时,将其关闭
fr.close();
}
} catch (IOException e) {
e.printStackTrace();
}
try {
if (fw != null){ //判断当输出流不为空时,将其关闭
fw.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
字符流复制文件时,出现乱码问题。解决办法,先将源文件调整为和Eclpise一样的编码
注意:
-
字节流可以读写任何文件(文本文件、二进制文件(音频视频文件 chm)),字符流只可以读写文本文件(word不是文本文件),但是字符流处理非英文字符文本非常方便。
-
其实只有字节流,没有字符流,字符流底层使用的还是字节流,Java在字节流基础上提供了字符流,给编程带来了便利。
-
字符流如何识别是英文字符还是中文字符 。
-
英文占一个字节 最高位是0 0111 0011
-
中文占两个字节 最高位是1 1011 1011 1001 1101
-
使用缓冲字节流复制文件
使用缓冲流之前,中转站小,只有一个字节,所以每读一个字节,都要访问硬盘一次;每写一个字节,都要访问硬盘一次。
public class TestBuffCopy {
public static void main(String[] args) {
//创建一个输入和输出流
BufferedInputStream bis = null;
BufferedOutputStream bos = null;
try {
bis = new BufferedInputStream(new FileInputStream(new File("d:/JDK1.6 API帮助文档.CHM"))); // 输入缓冲区默认8192字节
bos = new BufferedOutputStream(new FileOutputStream(new File("d:/JDK1.6 API帮助文档2.CHM"))); // 输出缓冲区默认8192字节
//读取字节
int n = 0;
while ((n = bis.read()) != -1){
//写出字节
bos.write(n);
}
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}finally {
try {
if (bis != null){
bis.close();
}
} catch (IOException e) {
e.printStackTrace();
}
try {
if (bos != null){
bos.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
使用缓冲字符流按行复制文件:
/**
* 只有文本文件才有行的概念
*/
public class TestBuffCopy2 {
public static void main(String[] args) throws IOException {
//创建输入流和输出流
BufferedReader br = new BufferedReader(new FileReader(new File("d:/BugReport.txt")));
BufferedWriter bw = new BufferedWriter(new FileWriter(new File("d:/BugReport2.txt")));
//读一行
String str = null;
while ((str = br.readLine()) != null){
//写一行
bw.write(str);
//写完一行就换行
bw.newLine();
}
//关闭流
br.close();
bw.close();
}
}
使用处理流:
-
提高性能
-
简化操作
使用转换流将键盘数据按行复制到文件:
public class TestBuffCopy3 {
public static void main(String[] args) throws IOException {
//键盘输入数据
InputStream inputStream = System.in;
//使用转换流,将字节流转换成字符流
Reader reader = new InputStreamReader(inputStream);
//字符输入流
BufferedReader br = new BufferedReader(reader);
//字符输出流
BufferedWriter bw = new BufferedWriter(new FileWriter(new File("d:/java.txt")));
//读一行字符
String str = br.readLine();
while (!"bye".equals(str)){ //判断当读取到bye时结束
//写一行字符
bw.write(str);
//写完一行就换行
bw.newLine();
//读取下一行
str = br.readLine();
}
//关闭流
br.close();
bw.close();
}
}
使用打印流按行写入文件:
/**
* 打印流,只有输出流
*/
public class TestPrintWriter {
public static void main(String[] args) throws FileNotFoundException {
//打印输出流
PrintWriter pw = new PrintWriter("d:/BugReport.txt");
pw.println(23);
pw.println('a');
pw.println(3.14);
pw.println(true);
pw.println("dasd");
//关闭流
pw.close();
}
}
System.out是PrintStream的一个实例:
public class TestPrintStream {
public static void main(String[] args) throws FileNotFoundException {
//System.out是PrintStream的一个实例
PrintStream ps = System.out;
ps.println(2);
ps.println('a');
ps.println(3.14);
ps.println(true);
ps.println("skjdh");
//关闭流
ps.close();
}
}
执行main方法,在控制台打印出对应数据。
PrintStream类的Println()方法功能强大,可以将各种数据类型(基本数据类型、引用数据类型)直接写入到文件中,并且换行。不管什么类型,写入到文件中全部变成字符串。
缺点:
-
需要使用特殊的字符来区分各个内容,防止混淆 2#a#3.14#true#skjdh
-
读出来之后都是字符串,还需要将字符串转换成真实类型 "2" "a" "3.14" "true"
解决方案,使用数据流和对象流:
-
DataInputStream 和 DataOutputStream 数据流(可以对各种基本数据类型和字符串直接读写)
-
ObjectInputStream 和 ObjectOutputStream 对象流(可以对各种基本数据类型和引用数据类型直接读写)
使用数据流读写文件:
public class TestDataStream {
public static void main(String[] args) throws IOException {
writer();
// read();
}
/**
* 写入的是二进制数据,用户不可直接读
*/
public static void writer() throws IOException {
//创建输出流
OutputStream os = new FileOutputStream("d:/BugReport3.txt");
BufferedOutputStream bos = new BufferedOutputStream(os);
DataOutputStream dos = new DataOutputStream(bos);
//写数据
dos.writeInt(22);
dos.writeChar('B');
dos.writeBoolean(true);
dos.writeDouble(3.14);
dos.writeUTF("lrc");
dos.writeUTF("hqn");
//关闭流
dos.close();
bos.close();
os.close();
}
/**
* 用DataInputStream读数据
*/
public static void read() throws IOException {
//创建输出流
InputStream is = new FileInputStream("d:/BugReport3.txt");
BufferedInputStream bis = new BufferedInputStream(is);
DataInputStream dis = new DataInputStream(bis);
//写数据
System.out.println(dis.readInt());
System.out.println(dis.readChar());
System.out.println(dis.readDouble());
System.out.println(dis.readBoolean());
System.out.println(dis.readUTF());
//关闭流
dis.close();
bis.close();
is.close();
}
}
当需要对文件读写对象时,需要用对象流来进行操作。
使用对象流读写文件:
public class TestObject {
public static void main(String[] args) throws IOException, ClassNotFoundException {
writer();
// read();
}
/**
* 写入的是二进制数据,用户不可直接读
* @throws IOException
*/
public static void writer() throws IOException {
//创建输出流
OutputStream os = new FileOutputStream("d:/BugReport3.txt");
BufferedOutputStream bos = new BufferedOutputStream(os);
ObjectOutputStream oos = new ObjectOutputStream(bos);
//写数据
oos.writeInt(22);
oos.writeChar('B');
oos.writeBoolean(true);
oos.writeDouble(3.14);
oos.writeUTF("lrc");
oos.writeUTF("hqn");
oos.writeObject(new Date()); //写一个Date对象
//关闭流
oos.close();
bos.close();
os.close();
}
/**
* 用ObjectInputStream读数据
*/
public static void read() throws IOException, ClassNotFoundException {
//创建输出流
InputStream is = new FileInputStream("d:/BugReport3.txt");
BufferedInputStream bis = new BufferedInputStream(is);
ObjectInputStream ois = new ObjectInputStream(bis);
//写数据
System.out.println(ois.readInt());
System.out.println(ois.readChar());
System.out.println(ois.readDouble());
System.out.println(ois.readBoolean());
System.out.println(ois.readUTF());
System.out.println(ois.readUTF());
Date date = (Date) ois.readObject(); //读出对象
System.out.println(date);
//关闭流
ois.close();
bis.close();
is.close();
}
}
复制文件夹
public class TestCopyDir {
public static void main(String[] args) {
//调用方法
copyDir("e:/test","e:/test2");
}
/**
* 复制文件夹
*/
public static void copyDir(String sourceFile,String targetFile){
//源文件必须存在
File file = new File(sourceFile);
if (!file.exists()){
System.out.println("源文件不存在");
return;
}
//创建目标文件
File dir = new File(targetFile);
if (!dir.exists()){
//如果目标文件不存在,就创建
dir.mkdirs();
}
//复制源文件的子文件夹到目标文件(不包含子文件)
File[] files = file.listFiles(); //获得源文件下的所有文件和文件夹
for (File f: files) {
//如果是文件
if (f.isFile()){
//f.getName()获取该文件的名称
copyFile(sourceFile+"/"+f.getName(),targetFile+"/"+f.getName());
}
//如果是文件夹
if (f.isDirectory()){
//递归
copyDir(sourceFile+"/"+f.getName(),targetFile+"/"+f.getName());
}
}
}
/**
* 复制文件
* @param sourceFile 源文件
* @param targetFile 目标文件
*/
public static void copyFile(String sourceFile,String targetFile){
BufferedInputStream bis = null;
BufferedOutputStream bos = null;
try {
bis = new BufferedInputStream(new FileInputStream(new File(sourceFile)));
bos = new BufferedOutputStream(new FileOutputStream(new File(targetFile)));
//定义一个中转站
byte[] bytes = new byte[1024];
//读取字节
int len = bis.read(bytes);
while (len != -1){
//写出字节
bos.write(bytes,0,len);
//继续读取字节
len = bis.read(bytes);
}
} catch (FileNotFoundException e) {