Java 中的 IO流 处理

发布于 2021-07-05 13:05:10 字数 11696 浏览 1260 评论 0

一、输入流&输出流

输入流和输出流相对于内存设备而言。
将外设中的数据读取到内存中:输入
将内存中的数据写入到外设中:输出

二、字节流&字符流

字符流的由来: 其实就是:字节流读取文字字节数据后,不直接操作而是先查找指定的编码表,获取对应的文字。 再对这个文字进行操作。简单说:字节流 + 编码表

三、IO常用的基类

字节流的抽象基类:InputStream/OutputStream 字符流的抽象基类:Reader/Write

注:由这四个类派生出来的子类名称都是以其父类名作为子类名的后缀。 如:InputStream 的子类 FileInputStream,如:Reader 的子类 FileReader

四、FileWrite

public static void main(String[] args) throws IOException{
  //创建一个可以往文件中写入字符数据的字符输出流对象
  //既然是往一个文件中写入文字数据,那么再创建对象时,就必须明确该文件(用于存储数据的目的地)

  //如果文件不存在,则会自动创建。如果文件存在,则会被覆盖
  FileWrite fw = new FileWrite("demo.txt");

  //调用write对象中的write(string)方法,写入数据。
  //其实数据写到临时存储缓冲区中
  fw.write("abcde");

  //进行刷新,将数据直接写到目的地中
  fw.flusg();

  //关闭流,关闭资源,在关闭前会先调用flush刷新缓冲区中的数据到目的地。
  fw.close();

}

换行和续写

//换行
fw.write("abcd\r\nhahah");
private static final String LINE_SEPARATOR = System.getProperty("line.separator");

//续写
//如果构造函数中加入true,可以实现对文件进行续写。
FileWrite fw = new FileWrite("demo.txt",true);

异常处理

public static void main(String[] args){
  FileWrite fw = null;

  try{
    fw = new FileWrite("demo.txt",true);
    fw.write("abcde"+LINE_SEPARATOR+"hahaha");
  }catch(IOException e){
    System.out.println(e.toString());
  }finally{
    try{
      fw.close();
    }catch(IOException e){
      throw new RuntimeException("关闭失败");
    }
  }
}

五、FileReader

public static void main(String[] args) throws IOException{
  //创建读取字符数据的流对象
  //在创建读取流对象是,必须要明确被读取的文件。一定要确定该文件是存在的。
  FileReader fr = new FileReader("demo.txt");

  //用reader中的read方法读取字符
  int ch = fr.read();
  System.out.println(ch);
  while(ch=fr.read()!=-1){
    System.out.println(ch);
  }

  fr.close();
}

//使用read(char[])读取文本文件数据
//先创建字符数组
char[] buf = new char[3];
//将读取到的字符存储到数组中
int num = fr.read(buf);
System.out.println(new String(buf));

int len = 0;
while(len=fr.read(buf)!=-1){
  System.out.println(new String(buf,0,len));
}

六、示例

1、将C盘的一个文本文件复制到d盘

第一种方式

public static void main(String[] args)throws IOException{
  //1、读取一个已有的文本文件,使用字符读取流和文件相关联
  FileReader fr = new FileReader("c:/io.txt");

  //2、创建一个目的,用于存储读到的数据
  FileWrite fw = new FileWrite("d:/io.txt");

  //3、频繁的读写操作
  int ch = 0;
  while((ch = fr.read())!=-1){
    fw.write(ch);
  }

  //4、关闭流资源
  fw.close();
  fr.close();
}

第二种方式

public static void main(String[] args){
  FileReader fr = null;
  FileWrite fw = null;

  try{
    fr = new FileReader("c:/io.txt");
    fw = new FileWrite("d:/io.txt");

    //创建一个临时容器,用于缓存读到的字符
    char[] buf = new char[1024]

    //定义一个变量记录读取到的字符数(其实就是往数组里装的字符个数)
    int len = 0;
    while(len=fr.read(buf) != -1){
      fw.write(buf,0,len);
    }
  }catch(Exception e){

  }finally{
    if(fw!=null){
      try{fw.close()}catch(){}
    }

    if(fr!=null){
      try{fr.close()}catch(){}
    }
  }
}

七、字符流-缓冲区

缓冲区的出现提高了对数据的读写效率。对应类:

BufferedWriter
BufferedReader

缓冲区要结合流才可以使用,在流的基础上对流的功能进行了增强

public static void main(String[] args) throws IOException{
  FileWrite fw = new FileWrite("buf.txt");

  //为了提高写入的效率。使用字符流的缓冲区
  //创建了一个字符写入流的缓冲区对象,并和指定要被缓冲的流对象相关联
  BufferedWriter bufw = new BufferedWriter(fw);

  //使用缓冲区的写入方法将数据先写入到缓冲区中
  bufw.write("abcdef");

  //使用缓冲区的刷新方法将数据刷到目的地
  bufw.flush();

  //关闭缓冲区(其实关闭的就是被缓冲的流对象)
  bufw.close();

  //fw.write("abcdef");

  //fw.close();
}

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

  BufferedReader bufr = new BufferedReader(fr);

  String line1 = bufr.readLine();
  System.out.println(line1);

  String line2 = bufr.readLine();
  System.out.println(line2);

  //或
  String line = null;
  while(line=bufr.readLine() !=null){
    System.out.println(line);
  }

  bufr.close();


  //char[] buf = new char[1024];
  //int len = 0;
  //while(len=fr.read(buf) != -1){
  //}
}

八、字符流-缓冲区-装饰设计模式

对一组对象的功能进行增强时,就可以使用该模式进行问题的解决。

public class PersonDemo{
  public static void main(String[] args){
    Person p = new Person();
    p.chifan();

    NewPerson p1 = new NewPerson();
    p1.chifan();

    NewPerson2 p2 = new NewPerson2();
    p2.chifan();
  }
}

class Person{
  void chifan(){
    System.out.println("吃饭");
  }
}

//这个类的出现是为了增强Person而出现的。
class NewPerson{
  private Person p;
  NewPerson(NewPerson p){
    this.p = p;
  }

  public void chifan(){
    System.out.println("开胃菜");
    p.chifan();
    System.out.println("甜点");
  }
}

//继承和覆盖
class NewPerson2 extends Person{
  public void chifan(){
    System.out.println("开胃菜");
    super.chifan();
    System.out.println("甜点");
  }
}

装饰和继承都能实现一样的特点:进行功能的扩展增强,有什么区别呢?

首先有一个继承体系:
Writer
  |--TextWriter:用于操作文本
    |--BUfferTextWriter:加入了缓冲技术的操作文本的对象
  |--MediaWriter:用于操作媒体
    |--BufferMediaWriter:加入了缓冲技术的操作媒体的对象

但是这样做好像并不理想,如果这个体系进行功能扩展,有多个流对象。
那么这个流要提高效率,是不是也要产生子类呢?
是,这时就会发现只为提高功能,进行的继承,导致继承体系越来越臃肿,不够灵活。

重新思考这个问题?
既然加入的都是同一种技术-缓冲。
前一种是让缓冲和具体的对象相结合。
可不可以将缓冲进行单独的封装,哪个对象需要缓冲就将哪个对象和缓冲关联。
class BufferWriter TextWriter{
  BufferWriter(Writer w){}
}

Writer
  |--TextWriter:用于操作文本
  |--MediaWriter:用于操作媒体
  |--BufferWriter:加入了缓冲技术的操作写的对象

装饰要比继承灵活,特点:装饰类和别装饰类都必须所属同一个接口或者父类。

九、字符流-缓冲区-LineNumberReader

跟踪行号的缓冲字符输入流

LineNumberReader extends BufferedReader
class LineNumberReaderDemo{
  public static void main(String[] args){
    FileRead fr = new FileRead("test.txt");

    LineNumberReader lnr = new LineNumberReader(fr);

    String line = null;
    lnr.setLineNumber(100)
    while(line=lnr.readLine()!=null){
      System.out.println(lnr.getLineNumber()+":"+line);
    }
    lnr.close();
    fr.close();
  }
}

十、字节流

基本操作于字符流类相同,但它不仅可以操作字符,还可以操作其他媒体文件

public static void demo_write() throws IOException{
  //1、创建字节流输出流对象。用于操作文件
  FileOutputStream fos = new FileOutputStream("bytedemo.txt");

  //2、写数据。直接写入目的地中。不用flush
  fos.write("abcde".getBytes());

  //3、关闭资源
  fos.close();
}

public static void demo_read() throws IOException{
  //1、创建一个读取流对象。和指定文件关联
  FileInputStream fis = new FileInputStream("bytedemo.txt");

  /*byte[] buf = new byte[1024];
  int len = 0;
  while(len=fis.read(buf) != -1){
    System.out.println(new String(buf,0,len));
  }*/

  System.out.println(fis.available());//5
  byte[] buf = new byte[fis.available()]
  fis.read(buf);
  System.out.println(new String(buf));
}

示例:复制 mp3

class CopyMp3Test{
  public static void main(String[] args) throws IOException{
    copy_1();//第一种方式
    copy_2();//第二种方式
    copy_3();//第三种方式
    copy_4();//第四种方式
  }

  public static void copy_1() throws IOException{
    FileInputStream fis = new FileInputStream("test.mp3");

    FileOutputStream fos = new FileOutputStream("test1.mp3");

    byte[] buf = new byte[1024];
    int len = 0;
    while(len=fis.read(buf) != -1){
      fos.write(buf,0,len);
    }

    fis.close();
    fos.close();
  }

  public static void copy_2() throws IOException{
    FileInputStream fis = new FileInputStream("test.mp3");
    BufferedInputStream bufis = new BufferedInputStream(fis);

    FileOutputStream fos = new FileOutputStream("test1.mp3");
    BufferedOutputStream bufos = new BufferedOutputStream(fos);


    int ch = 0;
    while(ch=bufis.read() != -1){
      bufos.write(ch);
    }

    bufis.close();
    bufos.close();
  }

  //不建议
  public static void copy_3() throws IOException{
    FileInputStream fis = new FileInputStream("test.mp3");

    FileOutputStream fos = new FileOutputStream("test1.mp3");

    byte[] buf = new byte[fis.available()];
    fis.read(buf);
    fos.write(buf);

    fis.close();
    fos.close();
  }

  //千万不要用,效率没有
  public static void copy_3() throws IOException{
    FileInputStream fis = new FileInputStream("test.mp3");

    FileOutputStream fos = new FileOutputStream("test1.mp3");

    int ch = 0;
    while(ch = fis.read() != -1){
      fos.write(ch);
    }

    fis.close();
    fos.close();
  }
}

十一、标准输入、输出

System.in;//标准输入(键盘)
System.out;//标准输出
public static void main(String[] args) throws IOException{
  InputStream in = System.in;//标准输入(键盘)
  int ch = in.read();//阻塞方法
  System.out.println(ch);
}

十二、转换流

//InputStreamReader是字节流通向字符流的桥梁,解码
//OutputStreamWrite是字符流通向的字节流桥梁,编码
public static void main(String[] args) throws IOException{
  //字节流
  InputStream in = System.in;

  //字符流
  //BufferedReader bufr = new BufferedReader(in);//字符流不能操作字节流对象,因此需要转换流

  //InputStreamReader是字节流通向字符流的桥梁
  //OutputStreamWrite是字符流通向的字节流桥梁
  //将字节转换为字符的桥梁,转换流
  InputStreamReader isr = new InputStreamReader(in);

  //字符流
  BufferedReader bufr = new BufferedReader(isr);

  String line = null;

  while(line=bufr.readLine()!=null){
    if("over".equals(line)){
      break;
    }
    System.out.println(line.toUpperCase());
  }
}

流的操作规律

之所以要弄清楚这个规律,是因为流对象太多,开发时不知道用哪个对象合适。

想要知道开发时候用到哪个对象。只要通过四个明确即可。

1、明确源和目的
  源:InputStream  Read
  目的:OutputStream  Write

2、明确数据是否是纯文本数据
  源:是纯文本:Reader
    :否:InputStream
  目的:是纯文本:Write
    :否:OutputStream

到这里,就可以明确需求中具体要使用哪个体系。

3、明确集体的设备
  源设备:
    硬盘:File
    键盘:System.in
    内存:数组
    网络:Socket流

  目的设备:
    硬盘:File
    键盘:System.out
    内存:数组
    网络:Socket流

4、是否需要其他额外功能
  是否需要高效(缓冲区)
    是:就加上buffer

十三、转换流的编码解码

OutputStreamWrite 是字符流通向字节流的桥梁,可使用指定的 charset 将要写入流中的字符编码成字节。它使用的字符集可以由名称指定或显式给定 否则将接受平台默认的字符集。

public static void writeText() throws IOException{
  //这两句代码的功能是等同的。
  //FileWriter:其实就是转换流指定了本机默认码表的体现。而且这个转换流的子类对象。可以方便操作文本文件。
  //简单说:操作文件的字节流+本机默认的编码表。这是按照默认码表来操作文件的便捷类

  //如果操作文本文件需要明确具体的编码。FileWrite就不行了。必须要用转换流
  OutputStreamWrite osw = new OutputStreamWrite(new FileOutputStream("gbk_1.txt"));
  FileWriter fw = new FileWriter("gbk_1.txt")


  osw.write("你好");
  osw.close();

  //指定编码
  OutputStreamWrite osw = new OutputStreamWrite(new FileOutputStream("utf8.txt","UTF-8"));
}

public static void readText() throws IOException{
  FileReader fr = new FileReader("gbk_1.txt");

  char[] buf = new char[10];
  int len = fr.read(buf);
  String str = new String(buf,0,len);
  System.out.println(str);
  fr.close();

  FileReader fr = new FileReader("utf8.txt");//读utf-8的会乱码
//修改如下:

  InputStreamReader isr = new InputStreamReader(new FileInputStream("utf8.txt"),"UTF-8");
  char[] buf = new char[10];
  int len = isr.read(buf);
  String str = new String(buf,0,len);
  System.out.println(str);
  fr.close();
}

十四、File 对象-构造函数&字段

  • 用来将文件或者文件夹封装成对象
  • 方便对文件与文件夹的属性信息进行操作
  • File对象可以作为参数传递给流的构造函数
  • 了解File类中的常用方法
public static void constructorDemo(){
  //可以将一个已存在的,或者不存在的文件或者目录封装成File对象
  File f1 = new File("c:\\a.txt");

  File f2 = new File("c:\\","a.txt");

  File f = new File("c:\\");
  File f3 = new File(f,"a.txt");

  File f4 = new File("c:"+System.getProperty("file.separator")+"abc\\a.txt");//分隔符
  File f5 = new File("c:"+File.separator+"abc"+File.separator+"a.txt");//分隔符
  System.out.println(f4);
  System.out.println(f4);
}

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

列表为空,暂无数据

关于作者

JSmiles

生命进入颠沛而奔忙的本质状态,并将以不断告别和相遇的陈旧方式继续下去。

0 文章
0 评论
84935 人气
更多

推荐作者

深爱成瘾

文章 0 评论 0

甜点

文章 0 评论 0

Ss Yy

文章 0 评论 0

dgmis009

文章 0 评论 0

花想c

文章 0 评论 0

樱花落人离去

文章 0 评论 0

    我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击“接受”或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。