JavaSE IO流(1) 字符流

*

流的分类

A:按流动方向的不同可以分为输入流和输出流;
B:按处理数据的单位不同分为字节流和字符流;
按功能的不同可分为节点流和处理流;
*节点流:直接操作目标设备,例如:磁盘或一块内存区域。
*处理流:通过操作节点流,从而间接完成输入或输出功能的流。处理流是的存在是建立 在一个已经存在的输入流或输出流的基础之上的

一般来说处理字符或字符串时使用字符流,处理字节或二进制对象时应使用字节流;
备注:字符流必须关闭资源,因为它中间有缓冲区!而字节流不需要!但是一般都会(最后) 关闭资源!

*字节流主要是操作 byte(字节)的类型数据:
字节输出流:OutputStream 字节输入流:InputStream
*字符流 Java 中的字符是 Unicode 编码,是双字节的,1 个字符 等于 2 个字节; 使用字节来处理字符文本就不太方便了,此时可以考虑使用字符流;
字符流主要是操作 char 的类型数据:

字节流和字符流的区别
字节流和字符流在使用上的代码结构都是非常类似的,但是其内部本身也是有区别的,因为 在进行字符流操作的时候会使用到缓冲区(内存中) ,而字节流操作的时候是不会使用 到缓冲区的。 在输出的时候,OutputStream 类即使最后没有关闭内容也可以输出。但是如果是Writer 的话,则如果不关闭,最后一条内容是无法输出的,因为所有的内容都是保存在了缓冲区之中,每当调用了close()方法就意味着清空缓冲区了。那么可以证明字符流确实使用了 缓冲区:
字节流:程序 → 文件 字符流:程序 → 缓冲区(内存中) → 文件
如果现在字符流即使不关闭也可以完成输出的话,则必须强制性清空缓冲区:
方法:public void flush() throws IOException

我的总结: 两者相比,肯定使用字节流更加的方便,而且在程序中像图片、MP3 等都是采用字节的方式 的保存,那么肯定字节流会比字符流使用的更广泛。 但是需要说明的是,但是如果要是想操作中文的话,字符流肯定是最好使的。 (字节流的话 可能会出现乱码(一个汉字分成了两份)!)


字符流

字符流 文件的写入方式
字符流读取字符, 就要先读取到字节数据, 然后转为字符. 如果要写出字符, 需要把字符转为字节再写出.
既然IO流是用于操作数据的,那么数据的最常见体现形式是:文件。

那么先以操作文件为主来演示。

需求:在硬盘上,创建一个文件并写入一些文字数据。

找到一个专门用于操作文件的Writer子类对象。FileWriter。 后缀名是父类名。前缀名是该流对象的功能。
Write类中有一个2K的小缓冲区,如果不关流,就会将内容写道缓冲区里,关流就会将缓冲区内的刷新再关闭。

import java.io.*;
public class FileWriterDemo
{
    public static void main(String[] args) throws IOException
    {
        //创建一个FileWriter对象。该对象一被初始化就必须要明确被操作的文件  
        //而且该文件会被创建到指定目录下。如果该目录下已有同名文件,将被覆盖。
        //其实该步就是在明确数据要存放的目的地
        FileWriter fw = new FileWriter("demo.txt");

        //调用write方法,将字符串写入到流中。
        fw.write("abcde");

        //刷新流对象中的缓冲中的数据,将数据刷到目的地中。
        fw.flush();

        //关闭流资源,但是关闭之前会刷新一次内部的缓冲中的数据,将数据刷新到目的地中。
        //和flush区别:flush刷新后。流可以继续使用,close刷新后,会将流关闭。
        fw.close();
    }
}

文件的续写

FileWriter fw = new FileWriter("demo.txt",true);  //传递一个参数,代表不覆盖已有文件,并在已有文件的末尾进行数据续写
fw.write("nihao \r dajia!")                       //  \r代表换行

异常处理方式

import java.io.*;
class FileWriterDemo2
{
    public static void main(String[] args) 
    {
        FileWriter fw =null;    //在代码块外面建立引用,这样都可以用fw
        try
        {
            fw = new FileWriter("demo.txt");
            fw.write("abcde");
        }
        catch  (IOException e)
        {
            System.out.println("catch:"+e.toString());
        }
        finally
        {
            try
            {
                if(fw!=null)      //一定要判断为非null
                    fw.close();   //在finally中close
            }
            catch (IOException e)
            {
                System.out.println("catch:"+e.toString());
            }
        }
    }
}

文件的读取方式 Reader
当Reader读到流的末尾时,会返回 -1
第一种方式

import java.io.*  
class FileReaderDemo{
    public static void main(String[] args) throws IOException{
        //创建一个文件读取流对象,和指定名称的文件相关联。
        //要保证该文件是已经存在的,如果不存在,会引发异常FileNotFoundException 
        FileReader fr = new FileReader("demo.txt");

        //调用读取流对象的read方法。
        //read(): 一次读一个字符。而且会自动往下读

        int ch = 0;

        while((ch=fr.read())!=-1) {                 //当read到流的末尾, 返回-1
            System.out.println((char)ch);   //(char)将ch转换成char类型
        }
    }
}

第二种方式:通过字符数组进行读取。

import java.io.*;
class FileReaderDemo
{
    public static void main(String[] args)throws IOException
    {
         FileReader fr = new FileReader("demo.txt");
         //定义一个字符数组。用于存储读到的字符。
         //该read(char[])返回的是读到的字符个数 
         char[] buf = new char[1024];

         int num = fr.read(buf);

         fr.close();
    }
}

循环形式

import java.io.*;
class FileReaderDemo
{
    public static void main(String[] args)
    {
         FileReader fr = new FileReader("demo.txt");
         //定义一个字符数组。用于存储读到的字符。
         //该read(char[])返回的是读到的字符个数 
         char[] buf = new char[1024];
         int num = 0;
         while((num=fr.read(buf))!=-1)
         {
             System.out.println(new String(buf,0,num));   //从下标0开始,取num个
         }
         fr.close();
     }
}

拷贝文本文件

/*
将C盘一个文本文件复制到D盘

复制原理:
1,在D盘创建一个文件。用于存储C盘文件中的数据。
2,定义读取流和C盘文件关联
3,通过不断的读写完成数据存储。
4,关闭资源。
*/
import java.io.*;
public clsaa CopyText{
    public static void main(String[] args)throw IOException{
        copy2();
    }
    public static void copy1()throws IOException     //第一种方法
    {
        //创建目的地
        FileWriter fw = new FileWriter("RuntimeDemoCopy.txt");
        //与已有文件关联。
        FileReader fr = new Filereader("RuntimeDemo.java");

        int ch = 0;

        while((ch=fr.read())!=-1)
        {
            fw.write(ch);
        }
        fw.close();
        fr.close();
    }
    public static void copy2()  //第二种方法,自定义字符数组的拷贝
    {
        FileWriter fw = null;
        FileReader fe = null;
        try
        {
            fw = new FileWriter("RuntimeDemoCopy.txt");
            fr = new FileReader("RuntimeDemo.java");

            char[] buf = new char[1024];

            int len = 0;
            while((len=fr.read(buf))!=-1) //将文件上的数据读取到字符数组中
            {
                fw.write(buf.0,len);  //将字符数组中的数据写到文件上
            }
            catch  (IOException e)
            {
                throw new RuntimeException("读写失败");
            }

            finally
            {
                if(fr!=null)
                    try
                    {
                        fr.close
                    }
                    catch(IOException e)
                    {
                    }
                if(fw!=null)
                    try
                    {
                        fw.close();
                    }
                    catch(IOException e)
                    {
                    }   
                }           
        }
    }
}

这里写图片描述

什么情况下使用字符流呢?
*字符流也可以拷贝文本文件, 但不推荐使用. 因为读取时会把字节转为字符, 写出时还要把字符转回字节.
*程序只需要读取一段文本, 或者只需要写出一段文本的时候可以使用字符流
*读取的时候是按照字符的大小读取的,不会出现半个中文
*写出的时候可以直接将字符串写出,不用转换为字节数组
这里写图片描述
上为字符流,下位字节流

字符流是否可以拷贝非纯文本的文件?
*不可以拷贝非纯文本的文件
*因为在读的时候会将字节转换为字符,在转换过程中,可能找不到对应的字符,就会用?代替,写出的时候会将字符转换成字节写出去
*如果是?,直接写出,这样写出之后的文件就乱了,看不了了


缓冲区

字符流写入缓冲区BufferedWrite
BufferedWriter的write()方法写出字符时会先写到缓冲区, 缓冲区写满时才会写到文件, 降低写文件的次数, 提高效率

/*
缓冲区的出现是为了提高流的操作效率而出现的。
所以在创建缓冲区之前,必须要先有流对象。
该缓冲区中提供了一个跨平台的换行符。 newLine(); 就是跨平台的换行,"\r\n"仅仅是windows的换行符
*/

import java.io.*;

class BufferedWriterDemo
{
    public static void main(String[] args) throws IOException
    {
        //创建一个字符写入流
        FileWriter fw = new FileWriter("buf.txt");

        //为了提高字符写入流效率,加入了缓冲技术。
        //只要将需要被提高效率的流对象作为参数传递给缓冲区的构造函数即可。
        BufferedWriter bufw = new BufferedWriter(fw);

        for(int x=1; x<5; x++)
        {
            bufw.write("abcd"+x);
            bufw.newLine();
            bufw.fluse();
        }

        //记住,只要用到缓冲区,就要记得刷新。bufw.flush();       
        //其实关闭缓冲区,就是在关闭缓冲区中的流对象。
        bufw.close();
    }
}

字符流读取缓冲区BufferReader
该缓冲区提供了一个一次读一行的方法 readLine(),方便于对文本数据的获取
BufferedReader的read()方法读取字符时会一次读取若干字符到缓冲区, 然后逐个返回给程序, 降低读取文件的次数, 提高效率
当返回null时,表示读到文件末尾。

readLine方法读取一个文本行。通过下列字符之一即可认为某行已终止:换行 (‘\n’)、回车 (‘\r’) 或回车后直接跟着换行。
readLine方法返回的时候只返回回车符之前的数据内容。并不返回回车符。

import java.io.*;

class BufferedReaderDemo
{
    public static void main(String[] args) throws IOException
    {
        //创建一个读取流对象和文件相关联
        FileReader fr = new FileReader("buf.txt");

        //为了提高效率,加入缓冲技术。将字符读取流对象作为参数传递给缓冲对象的构造函数。
        BufferedReader bufr = new BufferedReader(fr);

        String line = null;
        while((line=bufr.readLine())!=null)      //readLine()读取一行代码,当读到末尾行时返回null
        {
            System.out.println(line);
        }
        bufr.close();
    }
}

通过缓冲区复制文本文件

import java.io.*;

class CopyTextByBuf
{
    public static void main(String[] args)
    {
        BufferedReader bufr = null;
        BufferedWriter bufw = null;
        try
        {
            bufr = new BufferedReader(new FileReader("1.txt"));
            bufw = new BufferedWriter(new FileWriter("2.txt"));

            String line = null;

            wile((line=bufr.readLine())!=null)
            {
                bufw.write(line);
                bufw.newLine();                    //写入一行要换行
                bufw.flush();
            }
        }
        catch (IOException e)
        {
            throw new RuntimeException("读写失败")
        }
        finally
        {
            try
            {
                if(bufr!=null)
                    bufr.close();   
            }
            catch (IOException e)
            {
                throw new RuntimeException("读取关闭失败")
            }
            try
            {
                if(bufw!=null)
                    bufw.close();   
            }
            catch (IOException e)
            {
                throw new RuntimeException("写入关闭失败")
            }
        }
    }
}

MyBufferedReader
readLine方法原理图
这里写图片描述

readLine基于read方法,所谓我们也可以基于read方法做一个自己的readline方法

/*
明白了BufferedReader类中特有方法readLine的原理后,可以自定义一个类中包含一个功能和readLine一致的方法
来模拟一下啊BufferedReader
*/
import java.io.*;
class MyBufferedReader
{
    private FileReader r;
    MyBufferedReader(FileReader r)
    {
        this.r = r;
    }
    //可以一下读一行数据的方法。
    public String myReadLine() throws IOException
    {
        //定义一个临时容器。原BuffererdReader封装的是字符数组。
        //为了演示方便。定义一个StringBuilder容器。因为最终还是要将数据变成字符串。
        StringBuilder sb = new StringBuilder();

        int ch = 0;
        while((ch=r.read())!=-1)
        {
            if(ch=='\r')                         //  \r\n是windows中的回车符
                continue;                       //如果读到/r  则不存
            if(ch=='\n')
                return sb.toString();           //读到行结尾,把缓冲区里的字符串变成数据返回去
            else    
                sb.append((char)ch);
        }
        if(sb.length()!=0)
            return sb.toString();
        return null;
    }
    public void myClose() throws IOException
    {
        r.close();
    }
}

class MyBufferedReaderDemo
{
    public static void main (String[] args)throws IOException
    {
        FileReader fr = new FileReader("buf.txt");
        MyBufferedReader myBuf = new MyBufferedReader(fr);
        String line = null;
        while((line = myBuf.myReadLine())!=null)
        {
            System.out.println(line);
        }
        myBuf.myClose();
    }
}

LineNumberReader
LineNumberReader是BufferReader的子类
带行号的装饰类
setLineNumber() 设置当前行号
getLineNumber() 获得当前行号

FileReader fr = new FileReader("1.txt");

LineNumberReader lnr = new LineNumberReader(fr);

String line = null;
lnr.setLineNumber(100);         //设置当前行号从100开始
while((line=lnr.readline())!=null) {
    System.out.println(lnr.getLineNumber()+"::"+line);
}
lnr.close();

这里写图片描述

LineNumberReader的原理

import java.io.*;

class MyLineNumberReader
{
    private Reader r;
    private int lineNumber;
    MyLineNumberReader(Reader r)
    {
        this.r = r;
    }
    public String myReadLine() throws IOException
    {
        lineNumber++;
        StringBuilder sb = new StringBuilder();

        int ch = 0;

        while((ch=r.read())!=-1)
        {
            if(ch=='\r')
                continue;
            if(ch=='\n')
                return sb.toString();
            else
                sb.append((char)ch);
        }
        if(sb.length()!=0)
            return sb.toString();
        return null;
    }
    public void setLineNumber(int lineNumber)
    {
        this.lineNumber = lineNumber;
    }
    public int getLineNumber()
    {
        return lineNumber;
    }
    public void myClose() throws IOException
    {
        r.close();
    }
}

class  MyLineNumberReaderDemo
{
    public static void main(String[] args) throws IOException
    {
        FileReader fr = new FileReader("1.txt");

        MyLineNumberReader mylnr = new MyLineNumberReader(fr);

        String line =null;

        while((line=mylnr.myReadLine())!=null)
        {
            System.out.println(mylnr.getLineNumber()+"::"+line);
        }

        mylnr.myClose();
    }
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值