File文件对象只能操作文件或者文件夹的属性,例如文件或文件夹的创建,删除,获取文件属性,我们最终建立文件的目的,是往文件里面存数据,File对象是做不了这个的,这时,我们就要用到IO流。
字节流
InputStream抽象类是表示字节输入流的所有类的超类:
FileInputStream:从文件系统中的某个文件中获得输入字节。 用于读取诸如图像数据之类的原始字节流。要读取字符流,请考虑使用 FileReader。
FilterInputStream:包含其他一些输入流,它将这些流用作其基本数据源,它可以直接传输数据或提供一些额外的功能。
BufferedInputStream:该类实现缓冲的输入流。
DataInputStreamStream:操作基本数据类型值的流。
ObjectInputStream:对象的序列化。
PipedInputStream:管道输出流是管道的发送端。
SequenceInputStream:序列流。
ByteArrayInputStream:操作内存数组。关闭动作无效。
System.in:键盘录入。
OutputStream:此抽象类是表示输出字节流的所有类的超类:
FileoutputStream:文件输出流是用于将数据写入File或FileDescriptor的输出流。
注意处理IO异常。续写和换行。
FilterOutputStream:此类是过滤输出流的所有类的超类。
BufferedOutputStream:该类实现缓冲的输出流。
PrintStream:字节打印流,保证数值的表现形式不变,实现自动刷新和换行。
DataOutputStream:操作基本数据类型值的流。
ObjectOutputStream:对象的反序列化。
PipedOutputStream:管道输入流应该连接到管道输出流。
ByteArrayOutputStream:操作内存数组。关闭动作无效。
System.out:控制台打印到屏幕上。
FileOutputStream
将数据写入到文件里,使用字节输出流:FileOutputStream。在演示字节输出流之前,有以下三点需要注意:
1.输出流所关联的目的地,如果不存在,会自动创建。如果存在,则替换并且覆盖(这与File对象,如果存在,创建失败有所区别)
2.底层流资源使用完以后记得要释放资源。也即IO一定要写finally.
3.一定要在释放资源前先判断输出流对象是否为空,因为try中创建输出流对象失败,则fos依然是null,但是空指针没办法调用close()函数释放资源,这回导致抛出NullPointerException异常。
OutputStream提供了如下三个方法:
void write(int c):将指定的字节/字符输出到输出流中,其中c既可以代表字节,也可以代表字符。
void write(byte[]/char[] buf):将字节数组/字符数组中的数据输出到指定输出流中。
void write(byte[]/char[] buf,int off,int len):将字节数组/字符数组中从off位置开始,长度为len的字节/字符输出到输出流中。
下面创建字节输出流对象。调用输出流的write()功能的代码
public class FileOutputStreamDemo {
public static void main(String[] args) throws IOException {
/*
* 将数据写入到文件中。
* 使用字节输出流。
* FileOutputStream。
*/
File dir = new File("tempfile");
if(!dir.exists()){
dir.mkdir();
}
//1.创建字节输出流对象。用于操作文件,在对象初始化时,必须明确数据存储的目的地。
//输出流所关联的目的地,如果不存在,会自动创建。如果存在,则替换并覆盖。(这与File对象,如果存在、创建失败有所区别)
FileOutputStream fos = new FileOutputStream("tempfile\\fos.txt");
//2.调用输出流的写功能。
//String str = "abcde";
//byte[] buf = str.getBytes();
fos.write("abcde".getBytes());
//3.释放资源。
fos.close();
}
}
下面演示处理IO异常的规范写法
public class IOExceptionDemo {
/*
* IO异常的处理方式:IO一定要写finally!
*/
public static void main(String[] args) {
File dir = new File("tempfile");
if(!dir.exists()){
dir.mkdir();
}
FileOutputStream fos = null;//如果try中内容失败,fos还是null,所以finally要先判断。
try {
fos = new FileOutputStream("tempfile\\fos.txt");
fos.write("abcdefg".getBytes());
} catch (IOException e) {
System.out.println(e.toString() + "---");
} finally {
if (fos != null) {// 一定要在释放资源前先判断!
try {
fos.close();
} catch (IOException e) {
throw new RuntimeException("关闭失败" + e);//不要把异常抛给main函数、并让主函数声明、虚拟机处理!
}
}
}
}
}
需求实现续写和换行,行分隔符可以通过System.getProperty(“line.separator”)获得
public class NewlineDemo {
private static final String LINE_SEPARATOR = System.getProperty("line.separator");
public static void main(String[] args) {
/*
* 续写和换行。
*
* Linux换行符是"\n"
* Windows换行符是"\r\n"
* System.getProperty("line.separator")
*/
File dir = new File("tempfile");
if(!dir.exists()){
dir.mkdir();
}
FileOutputStream fos = null;
try{
fos = new FileOutputStream("tempfile\\fos.txt",true);//传入true实现续写。
String str = LINE_SEPARATOR + "abc";
fos.write(str.getBytes());
}catch(IOException e){
System.out.println(e.toString()+"--");
}finally{
if(fos != null){
try{
fos.close();
}catch(IOException e){
throw new RuntimeException(""+e);
}
}
}
}
}
FileInputStream
那如何将已有文件读取出来?既然是读,使用InputStream抽象类,而且是要操作文件,FileInputStream。
InputStream里面包含如下三个方法。
int read():从输入流读取单个字节,返回所读的字节数据(字节数据可直接转换成int类型)
int read(char[] cbuf,int off,int len):从输入流中最多读取len个字符的数据,并将其存储在字符数组cbuf中,放入数组cbuf时,并不是从数组起点开始,而是从off位置开始,返回实际读取的字符数。
int read(char[] cbuf):从输入流中最多读取cbuf.length个字符的数据,并将其存储在cbuf中。
下面演示创建文件字节读取流对象、逐个读取并打印文本文件中的字节:
public class FileInputStreamDemo {
public static void main(String[] args) throws IOException {
/*
* 将已有文件的数据读取出来。
* 既然是读,使用InputStream
* 而且是要操作文件,FileInpuStream。
*/
//为了确保文件一定在读之前是存在的,将字符串路径封装成File对象。
File file = new File("tempfile\\fos.txt");
if(!file.exists()){
throw new RuntimeException("要读取的文件不存在");
}
//创建文件字节读取流对象时,必须明确与之关联的数据源。
FileInputStream fis = new FileInputStream(file);
//调用读取流对象的读取方法。read();
/*
int by1 = fis.read();
System.out.println("by1 = "+by1);//97
int by2 = fis.read();
System.out.println("by2 = "+by2);//98
int by3 = fis.read();
System.out.println("by3 = "+by3);//99
int by4 = fis.read();
System.out.println("by4 = "+by4);//-1
int by5 = fis.read();
System.out.println("by5 = "+by5);//-1
*/
int by = 0;
while((by = fis.read()) != -1){
System.out.println(by);
}
//关闭资源。
fis.close();
}
}
一个一个读取字节太过于麻烦,可以创建缓冲区字节数组,大小自定义:
public class FileInputStreamDemo2 {
private static final int DEFAULT_SIZE = 1024*1024*2;//2MB 缓冲区
public static void main(String[] args) {
//演示第二种读取方式。read(byte[]); --> 第二种方式较好!
File file = new File("tempfile\\fos.txt");
if(!file.exists()){
throw new RuntimeException("要读取的文件不存在");
}
FileInputStream fis = null;
try{
fis = new FileInputStream(file);//流与文件关联
//创建一个缓冲区字节数组。
byte[] buf = new byte[DEFAULT_SIZE];//缓冲区大小一般设置为1024的整数倍。
//调用read(byte[])方法
/*
int len = fis.read(buf);//len记录的是往字节数组里存储的字节个数
System.out.println(len + "..." + new String(buf,0,len));//2...ab
int len1 = fis.read(buf);
System.out.println(len1 + "..." + new String(buf,0,len1));//1...c
int len2 = fis.read(buf);
System.out.println(len2 + "..." + new String(buf));//-1...cb
*/
int len = 0;
while((len = fis.read(buf)) != -1){
System.out.println(new String(buf,0,len));
}
}catch(IOException e){
//一般将异常信息写入到日志文件中,进行记录。
}finally{
if(fis != null){
try{
fis.close();
}catch(IOException e){
//一般可以throw new RuntimeException异常。或者将异常信息写入到日志文件中,进行记录。
}
}
}
}
}
其实还有一种比较蠢方式,就是把自定义缓冲区的大小设置为和文件本身大小一样大:
public class FileInputStreamDemo3 {
public static void main(String[] args) throws IOException {
FileInputStream fis = new FileInputStream("tempfile\\fos.txt");
System.out.println(fis.available());//可以获取与之关联文件的字节数。可以理解为file.length();
byte[] buf = new byte[fis.available()];//创建了一个和文件大小一样的缓冲区,刚刚好。不建议用。
fis.read(buf);
String s = new String(buf);
System.out.println(s);
fis.close();
}
}
需求:复制一个文件:(文件类型不限可以使文本数据,也可以是媒体数据)
思路:读,用到输入流;写,用到输出流。而且操作的还是文件。需要用到字节流中操作文件的流对象
public class CopyTextTest {
public static void main(String[] args) throws IOException {
/*
* 需求:复制一个文件。
* 思路:
* 读取源数据,将数据写到目的中。
* 用到了流,操作设备上的数据。
* 读,用到输入流;写,用到输出流。
* 而且操作的还是文件。需要用到字节流中操作文件的流对象。
*/
copyText();
}
public static void copyText() throws IOException {
//1.创建一个输入流和源数据相关联。
FileInputStream fis = new FileInputStream("复制文本文件图解.bmp");
//2.创建一个输出流,并通过输出流创建一个目的。
FileOutputStream fos = new FileOutputStream("tempfile\\io_copy.bmp");
//读一个,写一个。-->这种方式非常不好,效率太低,千万别用此方法。
int by = 0;
while((by = fis.read()) != -1){
fos.write(by);
}
fos.close();
fis.close();
}
}
同样的这里一个字节一个字节太慢,准备使用我们自己顶一个数组缓冲区:
public class CopyTextByBufTest {
public static void main(String[] args) {
copyTextByBuf();
}
public static void copyTextByBuf() {
FileInputStream fis = null;
FileOutputStream fos = null;
try{
fis = new FileInputStream("tempfile\\fos.txt");
fos = new FileOutputStream("tempfile\\copy_fos.txt");
//创建缓冲区
byte[] buf = new byte[1024];//1KB,这就是缓冲区.
//定义记录字符个数的变量
int len = 0;
//循环读写
while((len = fis.read(buf)) != -1){
fos.write(buf, 0, len);
}
}catch(IOException e){
//异常日志。
}finally{
if(fos != null){
try {
fos.close();
} catch (IOException e) {
//异常日志。
}
}
if(fis != null){
try {
fis.close();
} catch (IOException e) {
//异常日志。
}
}
}
}
}
BufferedInputStream BufferedOutputStream
在前面我们已经介绍了利用自定义缓冲区实现高效的字节流读取。java也考虑到了这一点,在底层将缓冲区封装成了对象,实际上就是在一个类中封装了数组,对流所操作的数据进行缓存。缓冲区的作用就是为了提高操作数据的效率。
需求:利用缓冲区完成复制图片的例子。
public class CopyPicByBufferDemo {
public static void main(String[] args) throws IOException {
/*
* java将缓冲区封装成了对象,实际上就是在一个类中封装了一个数组,对流所操作的数据进行缓存。
* 缓冲区的作用就是为了提高操作数据的效率。这样可以避免频繁的在硬盘上寻道操作。
* 缓冲区创建时,必须有被缓冲的流对象。
*
* 利用缓冲区完成复制图片的例子。
*/
copyPicByBuffer();
}
public static void copyPicByBuffer() throws IOException {
//演示缓冲区。
//1.创建具体的流对象。
FileInputStream fis = new FileInputStream("tempfile\\1.jpg");
//2.让缓冲区与指定流相关联。
//对流中的数据进行缓冲。位于内存中的缓冲区的数据读写速度远远大于硬盘上的读写。
BufferedInputStream bufis = new BufferedInputStream(fis);//缓冲区默认读8MB字节
FileOutputStream fos = new FileOutputStream("tempfile\\copy_1.jpg");
BufferedOutputStream bufos = new BufferedOutputStream(fos);
byte[] buf = new byte[1024];
int len = 0;
while((len = bufis.read(buf)) != -1){
bufos.write(buf, 0, len);//使用缓冲区的写入方法将数据先写入到缓冲区中。
bufos.flush();//将缓冲区的数据刷新到底层目的地中。(即使不写,缓冲区满了,java也会自动刷新 )
}
//关闭缓冲区,其实关闭的就是被缓冲的流对象。
bufos.close();
bufis.close();
}
}
编码表
在计算机中,无论是文本还是图片,MP3,视频等所有数据都是以字节形式存在。字节流:能操作以字节为单位的文件的流对象。所以字节流能操作计算机上的一切数据。但是对于纯文本数据的文件,用字节流操作中英文数据时由于编码问题,不同语言的字符对应的字节数不同,会显得比较繁琐,这时我们就该考虑字符流了。
编解码详述:
编码:字符串—>字节数组(默认都是按照Windows系统本地的编码GBK存储的)
解码: 字节数组—>字符串
String str=”中国”;
byte[] buf=str.getBytes(“utf-8”);//编码
String s1=new String(buf,”utf-8”);//解码
代码示例:
public class EncodeDemo {
public static void main(String[] args) throws IOException {
/*
* 字符串-->字节数组:编码。
* 字节数组-->字符串:解码。
*
* 字符串的编码,默认都是按照windows系统本地的编码GBK存储的。
* 获取平台默认字符集:System.getProperty("file.encoding");
* (注:字符类型char,在java底层是以unicode码完成的,作为中间转换码,可以忽略不用考虑。)
*
* 你好:
* GBK: -60 -29 -70 -61 一个中文两个字节,每个字节打头的都是1,所以都是负数。
* UTF-8: -28 -67 -96 -27 -91 -67
*
* 1.如果你编码错了,解不出来。
* 2.如果编对了,解错了,有可能还有救。
* 再次按错误的码表编码,获取原字节,然后再选择另一种码表解码即可。
*
* 应用:tomcat服务器提供的Web服务使用的是iso8859-1在服务器端编解码。
*/
test1();//编解码示例
//test2();//有时有救
//test3();//有时没救
//test4();//移动联通问题
}
public static void test1() throws UnsupportedEncodingException {
String str = "你好";
//编码。
//byte[] buf = str.getBytes();//使用平台默认字符集gbk编码
//byte[] buf = str.getBytes("gbk");
byte[] buf = str.getBytes("utf-8");
//printBytes(buf);
//解码。
//String s1 = new String(buf);//使用平台默认字符集gbk解码
//String s1 = new String(buf, "gbk");
String s1 = new String(buf, "utf-8");
System.out.println("s1 = "+s1);
}
public static void test2() throws IOException {
String str = "你好";
byte[] buf = str.getBytes("gbk");//11000100 11100011 10111010 11000011
String s1 = new String(buf, "iso8859-1");//iso都是单字节,所以没有改变原字节。
System.out.println("s1 = "+s1);//????
byte[] buf2 = s1.getBytes("iso8859-1");//获取原字节:11000100 11100011 10111010 11000011
String s2 = new String(buf2,"gbk");
System.out.println("s2 = "+s2);//你好
}
public static void test4() throws IOException {
String str = "联通";
byte[] buf = str.getBytes("gbk");
for(byte b : buf){
System.out.println(Integer.toBinaryString(b&255));
}
/*
11000001
10101010
11001101
10101000
问题:
在桌面上创建两个txt文本文件,1.txt写上"移动",2.txt写上"联通"。
发现再次打开1.txt完好,2.txt出现乱码。这是为什么呢?
回答:
记事本保存时,会按照windows平台默认GBK编码文本文件并保存。(ANSI也是本地默认字符集的意思)
这可以通过2.txt文本文件大小为4个字节得知。所以编码没有问题。
但是通过上面的小程序可以发现,"联通"的二进制码形式上符合utf-8的编码风格规范,
所以记事本再次打开时,会按照utf-8的格式解码这个文本文件,所以会出现乱码。
*/
}
}
上述的test2()函数,我们有如下的处理:
需求:按照字节数截取一个字符串。”abc你好”如果截取到半个中文,舍弃。比如截取4字节,abc,截取5个字节abc你。定义功能实现。(友情提示:GB2312编码的一个中文是两个字节,而且两个字节的最高位都是1,也就是说是一个负数。)
思路:
1.提示告诉我中文两个字节都是负数。
2.判断截取的最后一个字节是否是负数。如果不是,直接截取;如果是,就往回判断,前一个是否是负数。并记录住负数的个数。如果连续的负数有奇数个,舍弃最后一个字节;如果连续的负数是偶数个,不舍弃。
public class Test {
public static void main(String[] args) throws IOException {
/*
* 按照字节数截取一个字符串。"abc你好"如果截取到半个中文,舍弃。比如截取4字节,abc,截取5个字节abc你。
* 定义功能实现。
* 友情提示:GB2312编码的一个中文是两个字节,而且两个字节的最高位都是1,也就是说是一个负数。
*
* 思路:
* 1.提示告诉我中文两个字节都是负数。
* 2.判断截取的最后一个字节是否是负数。
* 如果不是,直接截取。
* 如果是,就往回判断,前一个是否是负数。并记录住负数的个数。如果连续的负数有奇数个,舍弃最后一个字节。
* 如果连续的负数是偶数个,不舍弃。
*
* 拓展1:GBK扩容后,中文既有负数,又有正数。它只保证第一个字节是负数,第二个字节不保证。此程序依然适用。
* 拓展2:如果将字符串编码成utf-8格式,咋办?这时,一个中文3个字节。
*/
//字符串转成字节数组。
String str = "a琲bc你好d琲e";
byte[] buf = str.getBytes("utf-8");
for(int i = 1; i <= buf.length; i++){
String temp = cutStringByCount2(str,i);
System.out.println("截取"+i+"个字节是:"+temp);
}
}
public static String cutStringByCount1(String str, int len) throws IOException {
//1.将字符串转成字符数组。因为要判断截取的字节是否是负数,所以要先有字节。
byte[] bytes = str.getBytes("gbk");
//2.定义计数器,记录住负数的个数。
int count = 0;
//3.对字节数组进行遍历。应该从截取长度的最后一个字节开始判断,并往回判断。
for(int x = len-1; x >= 0; x--){
//4.在遍历过程中,只要满足负数就进行计数。只要不是负数,直接结束遍历。
if(bytes[x]<0){
count++;
}else{
break;
}
}
//5.对遍历后,计数器的值进行判断,奇数,就舍弃最后字节并将字节数组转成字符串。
//偶数,不舍弃,将字节数组转成字符串。
if(count%2 == 0)
return new String(bytes, 0, len);
else
return new String(bytes, 0, len-1);
}
public static String cutStringByCount2(String str, int len) throws IOException {
byte[] bytes = str.getBytes("utf-8");
int count = 0;
for(int x = len-1; x >= 0; x--){
if(bytes[x] < 0){
count++;
}else{
break;
}
}
if(count%3 ==0)
return new String(bytes, 0, len, "utf-8");
else if(count%3 == 1)
return new String(bytes, 0, len-1, "utf-8");
else
return new String(bytes, 0, len-2, "utf-8");
}
}
字符流
字符流只能用来操作字符数据–文本。但是由于字符流封装了编码部分,所以操作字符比较方便。字符流=字节流+编码表
Reader:用于读取字符流的抽象类。子类必须实现的方法只有read(char[],int,int)和 close()。
BufferedReader:从字符输入流中读取文本,缓冲各个字符,从而实现字符、数组和行的高效读取。可以指定缓冲区的大小,或者可使用默认的大小。大多数情况下,默认值就足够大了。
LineNumberReader:跟踪行号的缓冲字符输入流。此类定义了方法 setLineNumber(int)和 getLineNumber(),它们可分别用于设置和获取当前行号。
InputStreamReader:是字节流通向字符流的桥梁:它使用指定的 charset读取字节并将其解码为字符。它使用的字符集可以由名称指定或显式给定,或者可以接受平台默认的字符集。
FileReader:用来读取字符文件的便捷类。此类的构造方法假定默认字符编码和默认字节缓冲区大小都是适当的。要自己指定这些值,可以先FileInputStream上构造一个InputStreamReader。
CharArrayReader:没有调用系统流,只是操作内存中的数组。
StringReader:没有调用系统流,只是操作内存中的数组。
Writer:写入字符流的抽象类。子类必须实现的方法只有write(char[],int,int),flush()和close()。
BufferedWriter:将文本写入字符输出流,缓冲各个字符,从而提供单个字符、数组和字符串的高效写入。
OutputStreamWriter:是字符流通向字节流的桥梁:可使用指定的 charset将要写入流中的字符编码成字节。它使用的字符集可以由名称指定或显式给定,否则将接受平台默认的字符集。
FileWriter:用来写入字符文件的便捷类。此类的构造方法假定默认字符编码和默认字节缓冲区大小都是可接受的。要自己指定这些值,可以先在 FileOutputStream上构造一个OutputStreamWriter。
PrintWriter:字符打印流,保证数值的表现形式不变,可以对字节流、字符流自动刷新并换行。
CharArrayWriter:没有调用系统流,只是操作内存中的数组。
StringWriter:没有调用系统流,只是操作内存中的数组
注意:只要是输出字符流,都有查表后的缓冲区,所以它的write()方法后面一定要跟随flush()刷新操作!(即使没写但通过运行,那也是因为close()封装了flush()方法)
转换流
1.InputStreamReader类
InputStreamReader 将字节流转换为字符流。是字节流通向字符流的桥梁。如果不指定字符集编码,该解码过程将使用平台默认的字符编码,如:GBK。
构造方法:
—InputStreamReader isr = new InputStreamReader(InputStream in);//构造一个默认编码集的InputStreamReader类
—InputStreamReader isr = new InputStreamReader(InputStream in,String charsetName);//构造一个指定编码集的InputStreamReader类。
—参数 in对象通过 InputStream in = System.in;获得。//读取键盘上的数据。 或者InputStream in = new FileInputStream(String fileName);//读取文件中的数据。
public static void transReadNoBuf() throws IOException {
/**
* 没有缓冲区,只能使用read()方法。
*/
//读取字节流
// InputStream in = System.in;//读取键盘的输入。
InputStream in = new FileInputStream("D:\\demo.txt");//读取文件的数据。
//将字节流向字符流的转换。要启用从字节到字符的有效转换,可以提前从底层流读取更多的字节.
InputStreamReader isr = new InputStreamReader(in);//读取
// InputStreamReader isr = new InputStreamReader(new FileInputStream("D:\\demo.txt"));//综合到一句。
char []cha = new char[1024];
int len = isr.read(cha);
System.out.println(new String(cha,0,len));
isr.close();
}
public static void transReadByBuf() throws IOException {
/**
* 使用缓冲区 可以使用缓冲区对象的 read() 和 readLine()方法。
*/
//读取字节流
// InputStream in = System.in;//读取键盘上的数据
InputStream in = new FileInputStream("D:\\demo.txt");//读取文件上的数据。
//将字节流向字符流的转换。
InputStreamReader isr = new InputStreamReader(in);//读取
//创建字符流缓冲区
BufferedReader bufr = new BufferedReader(isr);//缓冲
// BufferedReader bufr = new BufferedReader(new InputStreamReader(new FileInputStream("D:\\demo.txt")));可以综合到一句。
/* int ch =0;
ch = bufr.read();
System.out.println((char)ch);*/
String line = null;
while((line = bufr.readLine())!=null){
System.out.println(line);
}
isr.close();
}
2.OutputStreamWriter类
OutputStreamWriter 将字节流转换为字符流。是字节流通向字符流的桥梁。如果不指定字符集编码,该解码过程将使用平台默认的字符编码,如:GBK。
构造方法:
—OutputStreamWriter osw = new OutputStreamWriter(OutputStream out);//构造一个默认编码集的OutputStreamWriter类
—OutputStreamWriter osw = new OutputStreamWriter(OutputStream out,String charsetName);//构造一个指定编码集的OutputStreamWriter类。
—参数 out对象通过 InputStream out = System.out;获得。//打印到控制台上。 或者 InputStream out = new FileoutputStream(String fileName);//输出到文件中。
public static void transWriteNoBuf() throws IOException {
OutputStream out = System.out;//打印到控制台
// OutputStream out = new FileOutputStream("D:\\demo.txt");//打印到文件
OutputStreamWriter osr = new OutputStreamWriter(out);//输出
// OutputStreamWriter osr = new OutputStreamWriter(new FileOutputStream("D:\\demo.txt"));//两句可以综合到一句。
// int ch = 97;//a
// int ch = 20320;//你
// osr.write(ch);
String str = "你好吗?";//你好吗?
osr.write(str);
osr.flush();
osr.close();
}
public static void transWriteByBuf() throws IOException {
// OutputStream out = System.out;//打印到控制台。
OutputStream out = new FileOutputStream("D:\\demo.txt");//打印到文件。
OutputStreamWriter osr = new OutputStreamWriter(out);//输出
// OutputStreamWriter osr = new OutputStreamWriter(new FileOutputStream("D:\\demo.txt"));//综合到一句。
BufferedWriter bufw = new BufferedWriter(osr);//缓冲
// int ch = 97;//a
// int ch = 20320;//你
// osr.write(ch);
String str = "你好吗?\r\n我很好!";//你好吗?
bufw.write(str);
bufw.flush();
bufw.close();
}
实例:
public class TransStreamDemo3 {
/**
* @param args
* @throws IOException
*/
public static void main(String[] args) throws IOException {
// writeText_1();
// writeText_2();
// writeText_3();
// ReadTest_1();
// ReadTest_2();
// ReadTest_3();
}
public static void ReadTest_3() throws IOException {
InputStreamReader isr = new InputStreamReader(new FileInputStream("D:\\utf-8.txt"),"UTF-8");
char []ch = new char[20];
int len = isr.read(ch);
System.out.println(new String(ch,0,len) );
isr.close();
}
public static void ReadTest_2() throws IOException {
InputStreamReader isr = new InputStreamReader(new FileInputStream("D:\\utf-8.txt"),"GBK");
char []ch = new char[20];
int len = isr.read(ch);
System.out.println(new String(ch,0,len) );
isr.close();
}
public static void ReadTest_1() throws IOException {
FileReader fr = new FileReader("D:\\demo.txt");
char []ch = new char[20];
int len = fr.read(ch);
System.out.println(new String(ch,0,len) );
fr.close();
}
public static void writeText_3() throws IOException {
OutputStreamWriter osw = new OutputStreamWriter(new FileOutputStream("D:\\utf-8.txt"),"UTF-8");
osw.write("你好吗");
osw.close();
}
public static void writeText_2() throws IOException {
FileWriter fw = new FileWriter("D:\\gbk1.txt");
fw.write("你好啊");
fw.close();
}
public static void writeText_1() throws IOException {
OutputStreamWriter osw = new OutputStreamWriter(new FileOutputStream("D:\\gbk.txt"),"GBK");
/*
*和上面的等同
* FileWriter fw = new FileWriter("D:\\gbk1.txt");
* 操作文件的字节流 + 默认的编码表
*/
osw.write("你好吗");
osw.close();
}
}
FileWriter FileReader
二者是OutputStreamWriter和InputStreamReader也就是转换流的子类。专门用于操作文本文件的字符流对象。
局限性:
- 只能操作纯文本文件
- 只能使用默认编码表
- 由字符流的体系结构我们可以清楚的看到,FileReader类继承自InputStreanmReader转换流类。理由是:想要操作文本文件,必修进行编码转换,而编码转换动作转换流都做完了,所以操作文件的流对象只要继承自转换流就可以读取一个字符了。
FileReader fr=new FileReader("a.txt");
InputStreamReader isr=new InputStreamReader(new FileInputStream("a.txt"),"gbk");
上面两句代码功能一致
需求:使用自定义字符缓冲区,复制文本文件。
public class SubTransStreamDemo2 {
public static void main(String[] args) throws IOException {
/*
* 使用字符流缓冲区,复制文本文件示例。
*
* 字节流使用的缓冲区是字节数组,
* 字符流使用的缓冲区是字符数组。
*
* 如果仅仅是为了复制,建议使用字节流。
* 但是一旦涉及到中文字符的操作,用字符流较好,
* 比如在while循环体内对读到的文本字符数据的"你"字替换为"我"字,字节流就办不到了。
*
*/
copyText();
}
public static void copyText() throws IOException {
//1.明确数据源,定义字符读取流和数据源关联。
FileReader fr = new FileReader("IO流_2.txt");
//2.明确数据目的,定义字符输出流,创建存储数据的目的。
FileWriter fw = new FileWriter("tempfile\\copy_2.txt");
//3.创建自定义缓冲区。
char[] buf = new char[1024];//char占两个字节,所以声明了一个2KB的数组。
int len = 0;
while((len = fr.read(buf)) != -1){
fw.write(buf, 0, len);
}
fw.close();
fr.close();
}
}
BufferedWriter BufferedReader
BufferedXxx缓冲字符流存在的好处是:为了高效,并且有readLine(),newLine()方法。首先演示一下字符流缓冲区读写操作。
public class CharStreamBufferDemo {
public static void main(String[] args) throws IOException {
/*
* 演示字符流的缓冲区。
* BufferedReader
* BufferedWriter
*
* BufferedXxx缓冲字符流存在的好处是:
* 为了高效,并且有readLine()、newLine()方法。
*/
//writeTextByBuffered();
readTextByBuffered();
}
public static void writeTextByBuffered() throws IOException {
//1.明确目的。
FileWriter fw = new FileWriter("tempfile\\bufw.txt");
//2.创建缓冲区对象。明确要缓冲的流对象。
BufferedWriter bufw = new BufferedWriter(fw);
/*
bufw.write("abc");//写入到缓冲区。
bufw.write("\r\nhello");
换行的简便写法:newLine(),为什么给封装了呢?
因为文本文件相比于媒体文件最大的特点就是换行,文本体现的形式可以一行一行的体现,
其他类型文件不具备,所以把换行操作封装了。
bufw.newLine();//System.getProperty("line_separator");
*/
for(int x = 1; x <= 4; x++){
bufw.write(x + "abc");
bufw.newLine();
bufw.flush();
}
bufw.close();
}
public static void readTextByBuffered() throws IOException {
FileReader fr = new FileReader("tempfile\\bufw.txt");
BufferedReader bufr = new BufferedReader(fr);
//因为只有文本具备行的特性,所以又有了一个只针对文本的新方法:readLine();不包含终止符'\n'、'\r'。
//问题:test.txt中有几个字符?答案:9 --> \r \n两个字符。
/*
String line1 = bufr.readLine();
System.out.println(line1);//1abc
String line2 = bufr.readLine();
System.out.println(line2);//2abc
String line3 = bufr.readLine();
System.out.println(line3);//3abc
String line4 = bufr.readLine();
System.out.println(line4);//4abc
String line5 = bufr.readLine();
System.out.println(line5);//null-->和以前返回-1不一样了。
*/
String line = null;
while((line = bufr.readLine()) != null){
System.out.println(line);
}
bufr.close();
}
}