12.3字节流与字符流基本操作
12.3.1字节流
字节流主要是操作byte类型数据,以byte数组为准,主要操作类就是Outputstream类和
InputStream 类。
1.字节输出流:Outputstream
Outputstream是整个IO包中字节输出流的最大父类,此类的定义如下:
public abstract class Outputstream
extends Object
implements Closeable, Flushable
序 号 方法或常量 类 型 描 述
1 public void close() throws IOException 普通 关闭输出流
2 public void flush() throws IOException 普通 刷新缓冲区
3 public void write(byte口 b) throws IOException 普通 将一个byte数组写入数据流
4 public void write (byte口 b, int of^ int len) throws
IOException普通 将一个指定范围的byte数组 写入数据流
5 public abstract void write(int b) throws IOException普通 将一个字节数据写入数据流
此时可使用FileOutputStream子类,此类的构造方法如下:
public FileOutputStream(File file) throws FileNotFoundException
【例12.14】使用write(byte b[])的方式向文件中写入字符串
package jiaqi;
import java.io.File;
import java.io.FileOutputStream;
import java.io.OutputStream;
public class demo380_1
{
public static void main(String[] args)throws Exception
{
//找到一个文件
File f=new File("d:" + File.separator + "test.txt");
//实例化父对象
OutputStream out = new FileOutputStream(f);
String str="hello word!!!";
//输出只能是byte
byte b[] = str.getBytes();
out.write(b);
out.close();
}
}
【例12.15】使用write(int t)的方式写入文件内容
package jiaqi;
import java.io.File;
import java.io.FileOutputStream;
import java.io.OutputStream;
public class demo382_2 {
public static void main(String[] args)throws Exception
{
// TODO 自动生成的方法存根
File f = new File("d:" + File.separator + "test.txt");
OutputStream out=new FileOutputStream(f);
String str="hello wordl!!";
byte b[]=str.getBytes();
for(int i=0;i<b.length;i++)
{
out.write(b[i]);
}
out.close();
}
}
2.追加新内容
之前的所有操作中,如果重新执行程序,则肯定会覆盖文件中的己有内容,那么此时可以通过FielOutputStream
类的另一种构造方法进行实例化,向文件中追加内容,此构造方法如下所示:
public FileOutputStream(File file,boolean append) throws FileNotFoundException
在构造方法中,如果将append的值设置为true
,则表示在文件的末尾追加内容。
【例12.16】修改之前的程序,追加文件内容
package jiaqi;
import java.io.File;
import java.io.FileOutputStream;
import java.io.OutputStream;
public class demo383_1 {
public static void main(String[] args) throws Exception
{
File f = new File("d:" + File.separator + "test.txt");
OutputStream out = new FileOutputStream(f,true);
String str="hello word!!";
byte b[]=str.getBytes();
out.write(b);
out.close();
}
}
【例子】追加时加上换行
package jiaqi;
import java.io.File;
import java.io.FileOutputStream;
import java.io.OutputStream;
public class demo384_1
{
public static void main(String[] args)throws Exception
{
File f = new File("d:" + File.separator + "test.txt");
OutputStream out = new FileOutputStream(f,true);
String str="\r\nhello word!!";
byte b[] = str.getBytes();
out.write(b);
out.close();
}
}
3.字节输入流:Inputstream
既然程序可以利用Outputstream
类向文件中写入内容,则也可以通过InputStream
类从文件中把内容读取进来。InputStream类的定义如下:
public abstract class InputStream extends Object
implements Closeable
与OutputStream
类一样,InputStream
本身也是一个抽象类,必须依靠其子类,如果现在是 从文件中读取,子类肯定是FilelnputStream
。
FilelnputStream类的构造方法如下:
public FilelnputStream(File file) throws FileNotFoundException
序号 方法或常量 类型 描述
1 public int available() throws IOException 普通 可以取得输入文件的大小
2 public void close() throws IOException 普通 关闭输入流
3 public abstract int read() throws IOException 普通 读取内容,以数字的方式读取
4 public int read(byte[] b) throws IOException 普通 将内容读到byte数组之中,同时返回读入的个数
【例12.17】从文件中读取内容
package jiaqi;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.InputStream;
public class demo385_1 {
public static void main(String[] args)throws Exception
{
// TODO 自动生成的方法存根
File f = new File("d:"+ File.separator +"test.txt");
InputStream input = new FileInputStream(f);
byte b[] = new byte[1024];
input.read(b);
System.out.println(new String(b));
}
}
结果:
Hello World!!
Hello World!
(后面会有大量的空格,此处省略其显示)
上面的程序中,内容己经被读取进来了,但是发现后面有很多个空格。因为开辟的byte数组大小为1024,而实际的内容才只有28个字节,也就是说存在了 998个空白的空间,在将byte数组变为字符串的时候也将这998个无用的空间转为字符串了,这样的操作肯定是不合理的。
如果要想解决以上的问题,则要观察read方法,在此方法上有一个返回值,此返回值表示向数组中写入了多少个数据。
【例12.18】修正以上的错误
package jiaqi;
import java.io.File;
import java.io.FileInputStream;
import java.io.InputStream;
public class demo385_2
{
public static void main(String[] args)throws Exception
{
File f = new File("d:" + File.separator + "test.txt");
InputStream input = new FileInputStream(f);
byte b[] = new byte[1024];
int len = input.read(b);
System.out.println("长度" + len);
System.out.println(new String(b,0,len));
}
}
虽 然 指 定 了 b y t e 数 组 的 范 围 , 但 是 程 序 开 辟 很 多 的 空 问 , 会 造 成 资 源 的浪 费 , 那 么 能 否 根 据 文 件 的 数 据 量 来 选 择 开 辟 空 间 的 大 小 呢 ? 要 想 完 成 这 样 的 操 作 , 则 要 从 F i l e类中着手,因为在File类中存在一个length()方法,此方法就可以取得文件的大小。
【例12.19】开辟指定大小的byte数组
package jiaqi;
import java.io.File;
import java.io.FileInputStream;
import java.io.InputStream;
public class demo386_1
{
public static void main(String arg[])throws Exception
{
File f = new File("d:" + File.separator + "test.txt");
InputStream input = new FileInputStream(f);
byte b[] = new byte[(int)f.length()];
input.read(b);
input.close();
System.out.println(new String(b));
}
}
除了以上的方式外,也可以通过循环,从文件中一个个地把内容读取进来,直接使用read()方法即可。
【例12.20】使用read()通过循环读取
package jiaqi;
import java.io.File;
import java.io.FileInputStream;
import java.io.InputStream;
public class demo387_1 {
public static void main(String[] args)throws Exception
{
// TODO 自动生成的方法存根
File f = new File("d:" + File.separator + "test.txt");
InputStream input = new FileInputStream(f);
byte b[] = new byte[(int)f.length()];
for(int i=0;i<b.length;i++)
b[i] = (byte)input.read();
input.close();
System.out.println(new String(b));
}
}
上面程序是在明确知道了具体数组大小的前提下开展的,如果此时不知遣要输入的内容有多大,则只能通过判断是否读到文件末尾的方式来读取文件。
【例12.21】另一种方式的读取
package jiaqi;
import java.io.File;
import java.io.FileInputStream;
import java.io.InputStream;
public class demo387_2
{
public static void main(String[] args)throws Exception
{
File f = new File("d:" + File.separator + "test.txt");
InputStream input = new FileInputStream(f);
int temp=0,len=0;
byte b[] = new byte[1024];
while((temp=input.read())!=-1)
{
b[len]=(byte)temp;
len++;
}
input.close();
System.out.println(len);
System.out.println(new String(b,0,len));
}
}
12.3.2字符流
在程序中一个字符等于两个字节,Java提供了 Reader和Writer两个专门操作字符流的类。
1.字符输出流Writer
Writer
本身是一个字符流的输出类,此类的定义如下:
public abstract class Writer extends Object
implements Appendable, Closeable, Flushable
此类本身也是一个抽象类,如果要想使用此类,则肯定要使用其子类,此时如果是向文件中写入内容,应该使用FileWriter
的子类
Writer
类的常用方法
序号 方法或常量 类型述
1 public abstract void close() throws lOException 普通 关闭输出流
2 public void write(String str) throws lOException 普通 将字符串输出
3 public void write(char[] ebuf) throws lOException 普通 将字符数组输出
4 public abstract void flush() throws lOException 普通 强制性清空缓存
FileWriter
类的构造方法定义如下:
public FileWriter(File file) throws lOException
【例12.22】向文件中写入数据
package jiaqi;
import java.io.File;
import java.io.FileWriter;
import java.io.Writer;
public class demo389_1
{
public static void main(String[] args) throws Exception
{
// TODO 自动生成的方法存根
File f = new File("d:" + File.separator + "test.txt");
Writer out = new FileWriter(f);
String str = "duyanhe";
//outputstream write(byte b[])
//writer write(String str)
out.write(str);
out.close();
}
}
整个程序outputstream的操作流程并没有什么太大的区别,唯一的好处是,可以直接输出字符串,而不用将字符串变为byte数组之后再输出了。
2.使用FileWriter追加文件的内容
在使用字符流操作的时候,也可以实现文件的追加功能,直接使用FileWriter
类中的以下构造即可实现追加:
public FileWriter(File file,boolean append) throws lOException 将append的值设置成true,就表示追加。
【例12.23】追加文件内容
package jiaqi;
import java.io.File;
import java.io.FileWriter;
import java.io.Writer;
public class demo389_2
{
public static void main(String[] args)throws Exception
{
File f = new File("d:" + File.separator + "test.txt");
Writer out = new FileWriter(f,true);
String str = "dyh\r\n";
out.write(str);
out.close();
}
}
3.字符输入流:Reader
Reader类是使用字符的方式从文件之中取出数据,Reader的定义如下:
public abstract class Reader extends Object
implements Readable, Closeable
Reader
类本身也是抽象类,如果现在要从文件中读取内容,则可以直接使用FileReader
子类。
Reader类的常用方法
序 号 方法或常量 类 型 描 述
1 public abstract void close() throws IOException 普通 关闭输出流
2 public int read() throws IOException 普通 读取单个字符
3 public int read(char[] cbuf) throws IOException 普通 将内容读到字符数组之中,返回读入的长度
FileReader
的构造方法定义如下:
public FileReader(File file) throws FileNotFoundException
【例12.24】从文件中读取内容
package jiaqi;
import java.io.File;
import java.io.FileReader;
import java.io.Reader;
public class demo390_1 {
public static void main(String[] args)throws Exception
{
File f = new File("d:"+File.separator+"test.txt");
Reader input = new FileReader(f);
char ch[] = new char[1024];
int len = input.read(ch);
input.close();
System.out.println("len:"+len);
System.out.println(ch);
}
}
如果此时不知道数据的长度,也可以像之前操作字节流
那样,使用循环的方式进行内容的读取。
【例12.25]使用循环的方式读取内容
package jiaqi;
import java.io.File;
import java.io.FileReader;
import java.io.Reader;
public class demo391_1
{
public static void main(String[] args) throws Exception
{
File f = new File("d:"+File.separator+"test.txt");
Reader input = new FileReader(f);
char ch[] = new char[1024];
int len=0,temp=0;
while((temp=input.read())!=-1)
{
ch[len] = (char)temp;
len++;
}
input.close();
System.out.println("len"+len);
System.out.println(new String(ch,0,len));
}
}
12.3.3字节流与字符流的区别
字节流与字符流的使用非常相似,那么两者除了操作代码上的不同之外,是否还有其他的不同呢?
实际上字节流
在操作的时候本身不会用到缓冲区(内存),是于文件本身直接操作,而字符流
在操作时使用到缓冲区,通过缓冲区再操作文件.
下面以两个写文件的操作为例进行比较,但是在操作的时候字节流和字符流的操作完成后都不关闭输出流。
【例12.26】使用字节流
不关闭执行
package jiaqi;
import java.io.File;
import java.io.FileOutputStream;
import java.io.OutputStream;
public class demo392_1 {
public static void main(String[] args)throws Exception
{
// TODO 自动生成的方法存根
File f = new File("d:"+File.separator+"test.txt");
OutputStream out = new FileOutputStream(f);
String str = "hello!!";
byte b[] = str.getBytes();
out.write(b);
}
}
结果
此时,没有关闭字节流操作,但是文件中依然存在了输出的内容,证明字节流是直接操作文件本身的。而下面继续使用字符流完成,再观察效果。
【例12.27】使用字符流
不关闭执行
package jiaqi;
import java.io.File;
import java.io.FileWriter;
import java.io.Writer;
public class demo393_1
{
public static void main(String[] args)throws Exception
{
File f = new File("d:"+File.separator+"test.txt");
Writer out = new FileWriter(f);
String str = "hello!!";
out.write(str);
}
}
结果
程 序 运 行 后 会 发 现 文 件 中 没 有 任 何 的 内 容 , 这 是 因 为 字 符 流 操 作 的 时 候 使 用 了 缓 冲 区 ,而 在 关 闭 字 符 流 的 时 候 会 强 制 性 地 将 缓 冲 区 中 的 内 容 进 行 输 出 , 但 是 如 果 程 序 没 有 关 闭 , 则 缓 冲区中的内容是无法输出的。 所以得出结论:字符流使用了缓冲区,而字节流没有使用缓冲区。
如 果 想 在 不 关 闭 的 时 候 也 可 以 将 字 符 流 的 内 容 全 部 输 出 , 则 可 以 使 用 Writer
类 中 的 flush ( )
方法完成。
【例12.28]强制性清空缓冲区
package jiaqi;
import java.io.File;
import java.io.FileWriter;
import java.io.Writer;
public class demo393_2 {
public static void main(String[] args) throws Exception
{
File f = new File("d:"+File.separator+"test.txt");
Writer out = new FileWriter(f);
String str = "hello!!";
out.write(str);
out.flush();
}
}
此时,文件中己经存在了内容,更进一步证明内容是保存在缓冲区的。这一点在读者的开发中要特别引起注意。
12.3.4范例——文件复制
【例12.29】实现复制功能
将程序中的异常,直接通过main抛出解决【与书上不同】
package jiaqi;
import java.io.*;
public class demo395_1
{
public static void main(String[] args)throws Exception
{
//参数个数不对
if(args.length!=2)
{
System.out.println("输入参数不正确");
System.out.println("例子:java demo395_1 源文件路径 目标文件路径");
System.exit(1);
}
//读取两个参数
File f1 = new File(args[0]);
File f2 = new File(args[1]);
//源文件存在
if(f1.exists())
{
//打开
InputStream input = new FileInputStream(f1);//读f1
OutputStream out = new FileOutputStream(f2);//写f2
if(input!=null&&out!=null)//判断输入输出是否准备好
{
int temp=0;
while ((temp=input.read())!=-1)
{
out.write(temp);
}
System.out.println("复制完成!!");
}
//关闭
input.close();
out.close();
}
//源文件不存在
else
{
System.out.println("源文件不存在!!");
System.exit(1);
}
}
}
结果
本 程 序 借 助 于 循 环 的 方 式 实 现 了 文 件 的 复 制 操 作 ,但 是 在 整 个 操 作 过 程 之 中 也 会 存 在 一 个问 题 , 即 每 次 只 能 够 复 制 一 个 字 节
, 如 果 文 件 量 小 的 程 序 还 可 以 忍 受 , 但 是 文 件 量 一 大 , 这 样的复制就很难使用了。为此可以进一步修改程序,实现一块一块数据的复制操作
。
【例12.30】修改数据复制程序
package jiaqi;
import java.io.*;
public class demo397_1
{
public static void main(String[] args)throws Exception
{
//参数个数不对
if(args.length!=2)
{
System.out.println("输入参数不正确");
System.out.println("例子:java demo395_1 源文件路径 目标文件路径");
System.exit(1);
}
//读取两个参数
File f1 = new File(args[0]);
File f2 = new File(args[1]);
//源文件存在
if(f1.exists())
{
//打开
InputStream input = new FileInputStream(f1);//读f1
OutputStream out = new FileOutputStream(f2);//写f2
byte b[] = new byte[1024];
if(input!=null&&out!=null)//判断输入输出是否准备好
{
int temp=0;
while ((temp=input.read(b))!=-1)
{
out.write(b,0,temp);
}
System.out.println("复制完成!!");
}
//关闭
input.close();
out.close();
}
//源文件不存在
else
{
System.out.println("源文件不存在!!");
System.exit(1);
}
}
}