JavaIO之内存流、打印流、System类、输入流

一、内存流

1.定义

IO操作发生在内存中的这种流称为内存操作流。

2.应用场景

需要进行IO处理,但是又不希望产生文件。这种情况下就可以使用内存作为操作终端。

3.内存流的分类

字节内存流:ByteArrayInputStream和ByteArrayOutputStream

字符内存流:CharArrayReader和CharArrayWriter

4.字节内存流

ByteArrayOutputStream流的构造方法:
public ByteArrayOutputStream();//默认字节数组长度为32.
public ByteArrayOutputStream(int size);//用户自定义字节数组大小。

ByteArrayInputStream流的构造方法:
public ByteArrayInputStream(byte buf[]);//创建ByteArrayInputStream,使用buf作为其缓冲器阵列。 
public ByteArrayInputStream(byte buf[], int offset, int length);//创建ByteArrayInputStream,使用buf作为其缓冲器阵列。
package com.xunpu.memory;

import java.io.*;

/**
 * 给定内容,不通过文件,将给定的内容转换为大写并输出。
 * 步骤:
 * 1.创建输入流,将给定的内容转换为字节数组。
 * 2.创建输出流,将读到的内容转换为要求的内容。
 * 3.打印(直接输出out对象)。
 * 4.关闭流
 */
public class Demo1 {
    public static void main(String[] args) throws IOException {
        String msg="Today is Friday";
        //1.创建输入流,将给定的内容转换为字节数组。
        InputStream in=new ByteArrayInputStream(msg.getBytes());
        //2.创建输出流,将读到的内容转换为要求的内容。
        OutputStream out=new ByteArrayOutputStream();

        //读取内容
        int len=0;
        while((len=in.read())!=-1){//一次只读取1个字节
            out.write(Character.toUpperCase(len));
        }
        //直接输出out对象
        System.out.println(out);

        //关闭流
        in.close();
        out.close();
    }
}

此时发生了IO操作,但是没有文件产生。

之前Ajax刚形成的时候,此类操作非常多。后来出现了新的代码工具,这类代码就出现地较少了。

5.案例:两个文件合并(文件量不大)

假设现在有两个文件,分别是商品文件(aaa.txt)和商品价格文件(bbb.txt),商品和商品价格之间均用空格分隔。bbb.txt是按照商品的顺序来写商品价格的。现在要求将商品和商品对应的价格输出。

思路:

1)判断两个文件是否存在。不存在,抛出FileNotFoundException异常;存在,进行下一步。

2)依次读取每个文件,并将文件中的内容转换为字符串。(*****)

      2.1 创建输入流对象,便于读取数据

      2.2 创建内存输出流对象,便于写数据。

      2.3 每次读取一定字节的数据,同时写到内存输出流中。

      2.4 关闭流。

      2.5 将输出流对象转换为字符串返回。(new String(out.toByteArray()))

3)将商品和商品价格取出(String.split(" "))。

4)拼接每个商品和商品对应的价格。

package com.xunpu.memory;

import java.io.*;

/**
 * 合并两个文件量较小的文件.
 */
public class Demo2 {
    public static void main(String[] args) throws IOException {
        File file1=new File("E:"+File.separator+"JavaFile"+File.separator+"Directory"+File.separator+"bbb"
                +File.separator+"aaa.txt");
        File file2=new File("E:"+File.separator+"JavaFile"+File.separator+"Directory"+File.separator+"bbb"
                +File.separator+"bbb.txt");
        //1.判断两个文件是否都存在
        if(!(file1.exists()&&file2.exists())){
            System.out.println("文件不存在,合并失败");
            throw new FileNotFoundException();
        }
        //进行文件合并操作
        String res=mergeFiles(file1,file2);
        System.out.println(res);
    }
//合并两个文件内容
    private static String mergeFiles(File file1, File file2) throws IOException {
        String s1=readFile(file1);
        String s2=readFile(file2);
        StringBuffer sb=new StringBuffer();
        String[] str1=s1.split(" ");
        String[] str2=s2.split(" ");
        int min=str1.length<str2.length?str1.length:str2.length;
        int i=0;
        for(;i<min;i++){
            sb.append(str1[i]).append(":").append(str2[i]).append(" ").append("\n");
        }
        if(min==str1.length) {
            for(int k=i;k<str2.length;k++)
            sb.append(str2[k]).append(" ");
        }else{
            for(int k=i;k<str1.length;k++)
            sb.append(str1[k]).append(" ");
        }
        return new String(sb);
    }
//读取文件内容
    private static String readFile(File file2) throws IOException {
      //创建输入流对象,便于读取数据
        InputStream in=new FileInputStream(file2);
        //创建内存输出流对象,便于写数据。
        ByteArrayOutputStream out=new ByteArrayOutputStream();
        int len=0;
        byte[] data=new byte[20];//因为文件较小,故设置20个字节
        while((len=in.read(data))!=-1){//每次读20个字节,只要没有读到结尾,就继续读并且写到内存流中
            out.write(data,0,len);
        }
        //关闭流
        out.close();
        in.close();
        //返回内存流的数据
        return new String(out.toByteArray());
    }
}

二、打印流

如果操作的不是二进制数据,要想通过程序向终端目标输出信息,OutputStream不是很方便。它有两大缺陷:

1)所有的数据必须转换为字节数组。2)如果要输出的是int、double等数值型数据就不方便了。

打印流就是解决OutputStream流的缺陷的。

1.自定义实现打印流

package com.xunpu.memory;

import java.io.*;

/**
 * 自定义一个打印流
 * 实现:
 * 打印换行
 * 打印整型数据(换行/不换行)
 * 打印浮点型数据(换行/不换行)
 * 数据最终输出到指定的文件中。
 */
class MyPrint{
    private OutputStream out;//通过out输出流实现打印
    //由外部传入要输出的目标终端
    public MyPrint(OutputStream out){
        this.out=out;
    }
    //核心功能
    public void print(String data) throws IOException {
        this.out.write(data.getBytes());//将字符串转为字节数组,并写到输出流中。
    }
    //换行
    public void println(String data) throws IOException {
        this.print(data+"\n");
    }
    //打印整数
    public void print(int data) throws IOException {
        this.print(String.valueOf(data));
    }
    public void println(int data) throws IOException {
        this.println(String.valueOf(data));
    }

    //打印浮点数
    public void print(double data) throws IOException {
        this.print(String.valueOf(data));
    }
    public void println(double data) throws IOException {
        this.println(String.valueOf(data));
    }
}
public class Demo3 {
    public static void main(String[] args) throws IOException {
        File file=new File("E:"+File.separator+"JavaFile"+File.separator+"Directory"+File.separator+"bbb"
                +File.separator+"aaa.txt");
        OutputStream out=new FileOutputStream(file);
        MyPrint myPrint=new MyPrint(out);
        myPrint.print("姓名:");
        myPrint.println("自定义打印流.");
        myPrint.print("年龄:");
        myPrint.println(1);
        myPrint.print("售价:");
        myPrint.println(9999.99);
    }
}

2.java.io包中提供的打印流

字节打印流:PrintStream  字符打印流:PrintWriter(常用)

打印流的设计属于装饰设计模式,核心依然是某个类的功能,但是为了更好的操作结果,让其支持的功能更多一些。

PrintWriter的声明和构造方法
public class PrintWriter extends Writer;
构造方法:
public PrintWriter (Writer out);
public PrintWriter(OutputStream out);

PrintStream的声明和构造方法
public class PrintStream extends FilterOutputStream implements Appendable, Closeable;
构造方法:
public PrintStream(OutputStream out);
public PrintStream(String fileName);

使用系统提供的字符输出流:

package com.xunpu.memory;

import java.io.*;

/**
 * 测试系统提供的打印流-PrintWriter
 */
public class TestPrint {
    public static void main(String[] args) throws FileNotFoundException {
        OutputStream out=new FileOutputStream("E:"+File.separator+"JavaFile"+File.separator+"Directory"+File.separator+"bbb"
                +File.separator+"aaa.txt");
        PrintWriter printDemo=new PrintWriter(out);
        printDemo.print("姓名:");
        printDemo.println("系统提供的字符打印流");
        printDemo.print("年龄:");
        printDemo.println(2);
        printDemo.print("售价:");
        printDemo.println(9999.99);
        printDemo.close();
    }
}

PrintStream流实现格式化输出:(JDK1.5)

public PrintStream printf(String format, Object ... args);
package com.xunpu.memory;

import java.io.*;

/**
 * 测试系统提供的打印流-PrintStream
 */
public class TestPrint {
    public static void main(String[] args) throws FileNotFoundException {
        OutputStream out=new FileOutputStream("E:"+File.separator+"JavaFile"+File.separator+"Directory"+File.separator+"bbb"
                +File.separator+"aaa.txt");
        PrintStream printStream=new PrintStream(out);
        String name="张三";
        int age=20;
        double salary=6666.66;
        printStream.printf("姓名:%s  年龄:%d  工资:%.2f\n",name,age,salary);
        printStream.close();
    }
}

String类也提供了格式化字符串方法:

public static String format(String format, Object... args);

示例:

        String str=String.format("姓名:%s 年龄:%d  工资:%.2f\n",name,age,salary);
        System.out.println(str);
        str=String.format("%2$d  %1$.2f",74.503,22,31);//2$:第2个值,1$.2f:第一个数,且保留两位小数。
        System.out.println(str);

三、System类对IO的支持

实际上,我们一直使用的系统输出就是利用了IO流的模式完成。在System类中定义了三个操作常量:

public final static PrintStream out;//标准输出(显示器)
public final static PrintStream err;//错误输出
public final static InputStream in;//标准输入(键盘)

一直在使用的System.out.println()中,System是类,out是其中的属性,也是PrintSream类的一个对象。

1.系统输出

out和err都是系统输出的属性,out输出的是希望用户能看到的内容,err输出的是字样为红色,希望用户可以重视这个错误信息。

package com.xunpu.testsystem;

/**
 * 测试系统输出 out err
 */
public class Test1 {
    public static void main(String[] args) {
        try{
            Integer.parseInt("abc");
        }catch(Exception e){
            System.out.println(e);
            System.err.println(e);//输出的字样为红色
        }
    }
}

现在,这两种输出在实际开发中都不会用到了,取而代之的是“日志"。

out属性是PrintStream字节打印流的对象,PrintStream继承自FileOutputStream类,因此可以直接使用out为OutputStream类的实例化对象,此时OutputStream的输出位置为屏幕。

package com.xunpu.testsystem;

import java.io.IOException;
import java.io.OutputStream;

/**
 * 测试系统输出 --将System.out对象赋给OutputStream,输出位置变为屏幕。
 */
public class Test1 {
    public static void main(String[] args) throws IOException {
        OutputStream out=System.out;
        out.write("hello world".getBytes());
    }
}

2.系统输入

Java本身并没有提供直接的用户输入操作,但是可以通过java.io模式实现用户通过键盘来进行输入。当程序发现需要用户输入数据时,就会等待用户输入,此时程序阻塞直到用户输入完成(按下回车)。

package com.xunpu.testsystem;

import java.io.IOException;
import java.io.InputStream;

/**
 * 测试用户从键盘输入
 */
public class TestIn {
    public static void main(String[] args) throws IOException {
        InputStream in=System.in;
        byte[] data=new byte[10];
        System.out.println("请输入信息:");
        //发现需要用户输入信息,程序暂停执行,进入阻塞状态,直到用户输入完成(按回车),程序才能继续向下执行。
        int len=in.read(data);
        System.out.println(new String(data,0,len));
    }
}

可以发现,信息并没有完全打印出来。出现这种情况的原因是:开辟的字节数组长度小于用户从键盘输入的长度,此时只能够接收部分数据。这是由于一次读取不完造成的,最好的做法是引入内存操作流来控制,数据先保存在内存流中,然后再一次性输出。

package com.xunpu.testsystem;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;

/**
 * 测试用户从键盘输入
 */
public class TestIn {
    public static void main(String[] args) throws IOException {
        InputStream in=System.in;
        ByteArrayOutputStream out=new ByteArrayOutputStream();
        byte[] data=new byte[10];
        System.out.println("请输入信息:");
        //发现需要用户输入信息,程序暂停执行,进入阻塞状态,直到用户输入完成(按回车),程序才能继续向下执行。
        int len=0;
        while((len=in.read(data))!=-1){
            out.write(data,0,len);
            //当读到的数据长度小于data的长度时,说明此时已经读完,跳出循环
            if(len<data.length){
                break;
            }
        }
        System.out.println(out);
    }
}

四、两种输入流

1.BufferedReader流

缓冲的字符输入流。此外,还有字节输入流BufferedInputStream。一般会选用BufferedReader操作,这是因为它有

 public String readLine() throws IOException;//读取一行数据

BufferedReader的定义和构造方法:

public class BufferedReader extends Reader;//定义
 public BufferedReader(Reader in);//构造方法

要想使用BufferedReader类,就必须先定义一个Reader类,要和System.in联系起来,System.in是InputStream类。因此,需要用到InputStreamReader类,实现将字节流(in)转为字符流(BufferedReader)。

package com.xunpu.testsystem;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;

/**
 * 使用BufferedReader实现键盘输入
 */
public class TestInput {
    public static void main(String[] args) throws IOException {
        InputStream in=System.in;
        InputStreamReader reader=new InputStreamReader(in);
        BufferedReader bufferedReader=new BufferedReader(reader);
        System.out.println("请输入内容:");
        String str=bufferedReader.readLine();//默认使用回车换行
        System.out.println(str);
    }
}

默认的换行模式是BufferedReader的最大缺陷。

优点:接收的数据类型为String类型,因此可以使用String类的各种操作进行数据处理并且转为不同的数据类型。

2.java.util.Scanner类

Scanner类是一个专门进行输入流处理的类,可以处理各种数据类型,同时可以结合正则表达式进行各种处理。

Scanner类的方法:

public boolean hasNextXxx();//判断是否有指定类型的数据
public 数据类型 nextXxx();//取得指定类型的数据
public Scanner useDelimiter(Pattern pattern);//定义分隔符
public Scanner(InputStream source);//构造方法

使用Scanner类的方法:

package com.xunpu.testsystem;

import java.util.Scanner;

/**
 * Scanner类中 方法的使用
 */
public class TestScanner {
    public static void main(String[] args) {
        Scanner sc=new Scanner(System.in);
        System.out.println("请输入年龄:");
        if(sc.hasNextInt()){
            int age=sc.nextInt() ;
            System.out.println("输入的年龄为:"+age);
        }else{
            System.out.println("输入的不是数字,不符合要求!!");
        }

        //正则表达式的使用   要求输入的日期格式为:XXXX-XX-XX
        System.out.println("请输入出生日期:");
        if(sc.hasNext("\\d{4}-\\d{2}-\\d{2}")){
            String birthday=sc.next();
            System.out.println("输入的出生日期为:"+birthday);
        }else{
            System.out.println("输入的格式不符合要求!!");
        }
        sc.close();
    }
}

通过Scanner类读文件:

package com.xunpu.testsystem;

import java.io.*;
import java.util.Scanner;

/**
 * 通过Scanner类来读文件
 */
public class UseScanner {
    public static void main(String[] args) throws FileNotFoundException {
        InputStream in=new FileInputStream(new File("E:"+File.separator+"JavaFile"+File.separator+"Directory"+File.separator+"bbb"
                +File.separator+"aaa.txt"));
        Scanner sc=new Scanner(in);
        sc.useDelimiter("\n");//自定义分隔符
        while(sc.hasNext()){
            System.out.println(sc.next());
        }
    }
}

设置文件编码均为GBK后,得到:

五、总结

打印流(PrintStream&PrintWriter)解决的是OutputStream的缺陷,BufferedReader解决的是InputStream的缺陷,Scanner解决的是BufferedReader的缺陷。

之后除了二进制文件拷贝的处理外,只要是针对程序的信息输出都是使用打印流(PrintStream、PrintWriter),信息输出使用Scanner。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值