Java IO

一、简述

Java提供了完整的IO操作,IO分为输入流和输出流,Java又分为了字节和字符两大类。字符专门用来处理字符型数据,非常方便;字节也可处理文本数据,但是更多地是用来处理非文本的数据。

1445671-20180722120114128-1105017424.jpg


二、基于"字节"的IO

1、InputStream和OutputStream

基于字节的IO中的输入和输出分别为InputStream类和OutputStream类,这两个类都是抽象类。


2、FileInputStream和FileOutputStream

FileInputStream用于从文件中读取信息,FileOutputStream用于往文件中写入信息。

可以这样从文件中读取数据:

FileInputStream fis = new FileInputStream("buf.txt");
int length = 0;
byte[] buf = new byte[1024];
while((length = fis.read(buf)) != -1) {
    String str = new String(buf, 0, length);
    System.out.println(str);
}

fis.close()

用FileOutputStream读取数据:

FileOutputStream fos = new FileOutputStream("buf.txt");
byte[] out = "12342134436435\n".getBytes();
fos.write(out);
out = "hsdkafhlsadfhsadf".getBytes();
fos.write(out);

fos.close();

3、BufferedInptuStream和BufferedOutputStream

BufferedInputStream和BufferedOutputStream为输入输出加入了缓冲区,这样读取起来更加高效。

自己也可以实现同样功能的类,如可以这样设计:

import java.io.*;

public class MyBufferedInputStream {
    private InputStream in;
    private byte[] buf = new byte[1024 * 4];
    private int pos = 0, count = 0;
    
    MyBufferedInputStream(InputStream in)
    {
        this.in = in;
    }
    
    //一次读一个字节,从缓冲区(字节数组)获取
    public int myRead() throws IOException
    {
        //通过in对象读取硬盘上数据,并存储buf中
        if(count == 0)
        {
            count = in.read(buf);
            if(count < 0)
                return -1;
            pos = 0;
            byte b = buf[pos];
            
            count--;
            pos++;
            return b&0xff;  //b may be read 11111111B in the memory,decimal is -1,that will be make an error.
        }
        else if (count > 0)
        {
            byte b = buf[pos];

            count--;
            pos++;
            return b&255;
        }
        return -1;
    }
    
    public void myClose() throws IOException
    {
        in.close();
    }
}
import java.io.*;

public class BufferedStreamDemo {
    public static void main(String[] args) throws IOException{
        BufferedOutputStreamDemo();
        BufferedInputStreamDemo();
    }
    
    public static void BufferedInputStreamDemo() throws IOException {
        BufferedInputStream bis = new BufferedInputStream(new FileInputStream("buf.txt"));
        int buf;
        while((buf = bis.read()) != -1) {
            System.out.print((char)buf);
        }
        
        bis.close();
    }
    
    public static void BufferedOutputStreamDemo() throws IOException{
        BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream("buf.txt"));
        byte[] buf = "buffered output stream content.\n".getBytes();
        bos.write(buf);
        buf = "second row.\n".getBytes();
        bos.write(buf);
        bos.flush();  //刷新缓冲区
        
        bos.close();
    }
}

4、DataInputStream和DataOutputStream

DataInputStream和DataOutputStream是专门用于操作基本数据类型的流对象。可以以指定类型输入输出数据。

public class DataStreamDemo {
    public static void main(String[] args) throws IOException {
        writeData();
        readData();
        
        writeUTFDemo();
        readUTFDemo();
    }
    
    public static void readData() throws IOException
    {
        DataInputStream dis = new DataInputStream(new FileInputStream("data.txt"));
        
        int num = dis.readInt();
        double d = dis.readDouble();
        boolean bl = dis.readBoolean();
        
        System.out.println(num + " " + d + " " + bl);
        
        dis.close();
    }
    
    public static void readUTFDemo() throws IOException
    {
        DataInputStream dis = new DataInputStream(new FileInputStream("utfdata.txt"));
        
        String s = dis.readUTF();
        
        System.out.println(s);
        dis.close();
    }
    
    public static void writeData() throws IOException
    {
        DataOutputStream dos = new DataOutputStream(new FileOutputStream("data.txt"));
        dos.writeInt(243);
        dos.writeDouble(2.34234);
        dos.writeBoolean(true);
        
        dos.close();
    }
    
    public static void writeUTFDemo() throws IOException
    {
        DataOutputStream dos = new DataOutputStream(new FileOutputStream("utfdata.txt"));
        
        dos.writeUTF("你好");
        
        dos.close();
    }
}

输出为:

243 2.34234 true
你好

5、PipedInputStream和PipedOutputStream

管道流也用于传输数据,输入管道流与输出管道流连接,输入管道流提供要写入管道输出流的所有数据字节。

不建议使用单线程,因为会发生死锁问题。

class Read implements Runnable
{
    private PipedInputStream in;
    Read(PipedInputStream in)
    {
        this.in = in;
    }
    public void run()
    {
        try {
            byte[] buf = new byte[1024];
            int len = in.read(buf);
            String s = new String(buf, 0, len);
            System.out.println(s);
            in.close();
        }catch(IOException e)
        {
            throw new RuntimeException("管道读取流失败");
        }
    }
}

class Write implements Runnable
{
    private PipedOutputStream out;
    Write(PipedOutputStream out)
    {
        this.out = out;
    }
    public void run()
    {
        try {
            out.write("piped arrived".getBytes());
        }catch(IOException e)
        {
            throw new RuntimeException("管道输出流失败");
        }
    }
}

public class PipedStreamDemo {
    public static void main(String[] args) throws IOException {
        PipedInputStream in = new PipedInputStream();
        PipedOutputStream out = new PipedOutputStream();
        in.connect(out);
        
        Read r = new Read(in);
        Write w = new Write(out);
        new Thread(r).start();
        new Thread(w).start();
    }
}

6、ByteArrayInputStream和ByteArrayOutputStream

这两个流对象专门用来操作字节。

ByteArrayInputStream在构造的时候,需要接收数据源,而且数据是一个字节数据。

ByteArrayOutputStream在构造的时候,不用定义数据目的,因为该对象内部中已经封装了可变长度的字节数组。

因为这两个流对象操作的都是数组,并未使用系统资源,所以不用进行关闭。

源设备可以有:

  • 键盘:System.in
  • 硬盘:FileStream
  • 内存:ArrayStream

目的设备可以有:

  • 控制台:System.out
  • 硬盘:FileStream
  • 内存:ArrayStream
//数据源
ByteArrayInputStream bis = new ByteArrayInputStream("ABCDEFD".getBytes());

//数据目的
ByteArrayOutputStream bos = new ByteArrayOutputStream();

int by = 0;
while((by = bis.read()) != -1)
{
    bos.write(by);
}

System.out.println(bos.size());
System.out.println(bos.toString());

输出为:

7
ABCDEFD

7、SequenceInputStream

SequenceInputStream流对象可以将两个或多个InputStream转换为单一InputStream。

Vector<FileInputStream> v = new Vector<FileInputStream>();
        
v.add(new FileInputStream("1.txt"));
v.add(new FileInputStream("2.txt"));
v.add(new FileInputStream("3.txt"));

Enumeration<FileInputStream> en = v.elements();

SequenceInputStream sis = new SequenceInputStream(en);

FileOutputStream fos = new FileOutputStream("123.txt");

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

fos.close();
sis.close();

8、LineNumberInputStream

此类可以跟踪流中的行号,其实就是传入一个计数变量,每读取一行打印一次罢了,这个流已经过时了,可以看LineNumberReader类。


9、StringBufferInputStream

此类也过时了,可以看StringReader类。


10、PrintStream

PrintStream是打印流,此类提供了打印方法,可以将各种数据类型都原样打印。

构造函数可以接收的参数类型:

  1. file对象。File
  2. 字符串路径。String
  3. 字节输出流。OutputStream
BufferedInputStream bis = new BufferedInputStream(new FileInputStream("buf.txt"));
        
PrintStream ps = new PrintStream(System.out, true);
int buf = 0;
while((buf = bis.read()) != -1) {
    ps.print((char)buf);
}

bis.close();

三、基于"字符"的IO

1、Reader和Writer

Reader和Writer分别对应InputStream和OutputStream的字符形式,用来表示字符IO的输入和输出流,这两个类也是抽象类。


2、InputStreamReader和OutputStreamWriter

InputStreamReader是读取转换流,OutputStream是输出转换流,这两个流搭建起了字节IO和字符IO的桥梁。

public class TranslateStream {
    public static void main(String[] args) throws IOException{
        InputStream in = System.in;
        
        InputStreamReader isr = new InputStreamReader(in);  //byte stream to char stream
        
        BufferedReader bufr = new BufferedReader(isr);  //use buffer to improve speed
        String line = null;
        while((line = bufr.readLine()) != null)
        {
            if("over".equals(line))  //exit condition
                break;
            System.out.println(line.toUpperCase());
        }
        
        bufr.close();
    }
}

3、FileReader和FileWriter

此二类分别对应FileInputStream和FileWriter类。

//创建一个文件读取流对象,和指定名称的文件相关联
//要保证该文件是已经存在的,若不存在,抛异常FileNotFoundException
FileReader fr = new FileReader("demo.txt");

int ch;
while((ch = fr.read()) != -1) {
    System.out.print((char)ch); 
}

char[] text = new char[20];
int n = fr.read(text);
System.out.println("n:"+ n + new String(text));

fr.close();
//有同名文件将被覆盖,没有则创建
//即明确数据要存放的目的地
FileWriter fw = null;
try {
    fw = new FileWriter("demo.txt");
    fw.write("abcde");    //向流中写数据
    //fw.flush();         //刷新缓冲区
    fw.write("jhahaha");  //向流中写数据  
}catch(IOException e) {
    e.printStackTrace();
}finally {
    try {
        if(fw != null) {
            //关闭流资源,关闭资源之前会刷新一次缓冲区
            fw.close(); 
        }
    }catch(IOException e) {
        e.printStackTrace();
    }
}

4、BufferedReader和BufferedWriter

见名便知此二类是字符型的缓冲区流,因为是字符流,因此可以直接用String接收。

FileReader fr = new FileReader("buf.txt");
BufferedReader bufr = new BufferedReader(fr);
String str;
while((str = bufr.readLine()) != null) {
    System.out.println(str);
}
bufr.close();
//创建一个字符写入流对象
FileWriter fw = new FileWriter("buf.txt", true);

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

for(int i = 0; i < 5; i++) {
    bufw.write("abdedsd" + i);
    bufw.newLine();  //换行符,可跨平台
    bufw.flush();    //刷新缓冲区
}


//只要用到缓冲区,就要记得刷新
//bufw.flush();

//其实关闭缓冲区,就是在关闭缓冲区中的流对象,故不用写fw.close了
bufw.close();

5、PipedReader和PipedWriter

对应字节型的管道流对象。


6、CharArrayReader和CharArrayWriter

用于操作字符数组。


7、LineNumberReader
FileReader fr = new FileReader("buf_copy.txt");
        
LineNumberReader lnr = new LineNumberReader(fr);
String line = null;
lnr.setLineNumber(5);  //设置行号
while((line = lnr.readLine()) != null)
{
    System.out.println(lnr.getLineNumber() + ":" + line);
}

lnr.close();

8、PrintWriter

字符打印流构造函数可以接收的参数类型有:

  1. file对象。File
  2. 字符串路径。String
  3. 字节输出流。OutputStream
  4. 字符输出流。Writer
BufferedReader bufr = new BufferedReader(new InputStreamReader(System.in));

//PrintWriter out = new PrintWriter(System.out,true);
PrintWriter out = new PrintWriter(new FileWriter("printwriter.txt"), true);

String line = null;

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

out.close();
bufr.close();

9、StringBuffer和StringBuilder

StringBuffer类是线程安全的可变字符序列,是一个类似String的字符串缓冲区,但不能修改。

StringBuffer主要操作append()和insert方法,可以重载来接收任意类型的数据。每个方法才能有效地将给定的数据转换成字符串,然后将字符串的字符追加或插入到字符串缓冲区中。

StringBuilder类是StringReader的简单替换,若是字符串被单个线程使用,则建议使用StringBuilder。


四、基于"磁盘"的IO

1、File

IO流可以对流进行操作却不可直接操作文件,File类在IO包中唯一代表磁盘文件本身,该类定义了一些和平台无关的方法来操作文件。

File file = new File("F://Project//java//FileIOStream//test.txt");
if(file.exists()) {
    String name = file.getName();
    String parent = file.getParent();
    long length = file.length();
    boolean bool = file.canWrite();
    System.out.println("file name:" + name);
    System.out.println("file parent:" + parent);
    System.out.println("file size:" + length);
    System.out.println("是否为可写文件?" + bool);
}

File dir = new File("F://Project//java//");
if(dir.isDirectory()) {
    File[] files = dir.listFiles();
    for(int i = 0; i < files.length; i++) {
        File f = files[i];
        System.out.println("第" + (i + 1) + "个文件的名称是:" + f.getAbsolutePath());
    }
}

2、RandomAccessFile

该类不算是IO体系的子类,而是直接继承自Object。但是它可操作IO包中成员,因其具备读和写功能。

内部封装了一个数组,而且通过指针对元素进行操作,可以通过getFilePointer获取指针位置,同时可以通过seek改变指针的位置。

其实完成读写的原理就是内部封装了字节输入流和输出流。

通过构造函数可看出其只能操作文件且操作文件还有模式:r,rw...

如果模式为r,不会创建文件,会去读一个已存在的文件,如果该文件不存在,则会报异常。

如果模式为rw,操作的文件不存在,会自动创建,如果存在不会覆盖。

而且该对象的构造函数要操作的文件不存在,会自动创建,若存在不会覆盖。

因为该类支持随机读写访问文件,所以可以通过seek来实现多线程文件下载。

public static void readFile() throws IOException
{
    RandomAccessFile raf = new RandomAccessFile("ran.txt", "r");
    
    //调整对象中指针
    //raf.seek(7);
    
    //跳过指定的字节数
    raf.skipBytes(7);
    
    byte[] buf = new byte[4];
    raf.read(buf);
    
    String name = new String(buf);
    
    int age = raf.readInt();
    
    System.out.println("name:" + name);
    System.out.println("age:" + age);
    
    raf.close();
}

public static void writeFile() throws IOException
{
    RandomAccessFile raf = new RandomAccessFile("ran.txt", "rw");
    
    raf.write("neo".getBytes());
    raf.writeInt(97);  //write只写低8位,会丢失,故用其提供的带Int版本的.
    raf.write("mike".getBytes());
    raf.writeInt(108);  //write只写低8位,会丢失,故用其提供的带Int版本的.
    
    raf.close();
}

public static void writeFile_2() throws IOException
{
    RandomAccessFile raf = new RandomAccessFile("ran.txt", "rw");
    raf.seek(8*0);
    raf.write("周期".getBytes());
    raf.writeInt(37);
    
    raf.close();
}

五、练习

1、复制图片

思路:

  1. 用字节读取流对象和图片关联
  2. 用字节写入流对象创建一个图片文件。用于存储获取到的图片数据。
  3. 通过循环读写,完成数据的存储
  4. 关闭资源

实现:

public class CopyPic {
    public static void main(String[] args) {
        FileOutputStream fos = null;
        FileInputStream fis = null;
        try 
        {
            fos = new FileOutputStream("cpp_copy.png");
            fis = new FileInputStream("C++.png");
            
            byte[] buf = new byte[1024];
            
            int len = 0;
            
            while((len = fis.read(buf)) != -1)
            {
                fos.write(buf, 0, len);
            }
            
            System.out.println("复制成功!");
        }
        catch(IOException e)
        {
            throw new RuntimeException("复制文件失败.");
        }
        finally
        {
            try 
            {
                if (fos != null)
                {
                    fos.close();
                }
            }catch(IOException e)
            {
                e.printStackTrace();
            }

            try
            {
                if (fis != null)
                {
                    fis.close();
                }
            }catch(IOException e)
            {
                e.printStackTrace();
            }
        }
    }
}

2、成绩录入

有五个学生,每个学生有3门课的成绩
从键盘录入以上数据
输入的格式:如:zhangsan,30,40,60计算出总成绩
并把学生的信息和计算出的部分数高低存放在磁盘文件"stud.txt"中

  1. 描述一个学生对象
  2. 定义一个可操作学生对象的工具类

思路:

  1. 通过获取键盘录入一行数据,并将该行中的信息取出封装成学生对象.
  2. 因为学生很多,所以用集合存储.因为以要排序,因而可以使用TreeSet.
  3. 将集合的信息写入到一个文件中.

实现:

class Student implements Comparable<Student>
{
    private String name;
    private int math, chinese, english;
    private int sum;
    
    Student(String name, int ma, int ch, int en)
    {
        this.name = name;
        this.math = ma;
        this.chinese = ch;
        this.english = en;
        this.sum = ma + ch + en;
    }
    
    public String getName()
    {
        return this.name;
    }
    
    public int getSum()
    {
        return sum;
    }
    
    public int hashCode()
    {
        return name.hashCode() + sum * 78;
    }
    
    public boolean equals(Object obj)
    {
        if(!(obj instanceof Student))
            throw new ClassCastException("类型不匹配");
        
        Student s = (Student)obj;
        
        return this.name.equals(s.name) && this.sum==s.sum;
    }
    
    public String toString()
    {
        return "student[" + name + " math:" + math + " chinese:" + chinese + " english:" + english + "]";
    }
    
    @Override
    public int compareTo(Student s) {
        int num = new Integer(this.sum).compareTo(new Integer(s.sum));
        if(num == 0)
            return this.name.compareTo(s.name);
        return num;
    }
}

class StudentInfoTool
{
    public static Set<Student> getStudents() throws IOException
    {
        return getStudents(null);
    }
    
    public static Set<Student> getStudents(Comparator<Student> cmp) throws IOException
    {
        BufferedReader bufr = new BufferedReader(new InputStreamReader(System.in));
        
        String line = null;
        
        Set<Student> stus = null;
        if(cmp == null)
            stus = new TreeSet<Student>();
        else
            stus = new TreeSet<Student>(cmp);
        
        while((line = bufr.readLine()) != null)
        {
            if("over".equals(line))
                break;
            
            String[] info = line.split(",");
            
            Student stu = new Student(info[0], Integer.parseInt(info[1]), 
                    Integer.parseInt(info[2]),
                    Integer.parseInt(info[3]));
            stus.add(stu);
        }
        
        bufr.close();
        
        return stus;
    }
    
    public static void write2File(Set<Student> stus) throws IOException
    {
        BufferedWriter bufw = new BufferedWriter(new FileWriter("stuinfo.txt"));
        
        for(Student stu : stus)
        {
            bufw.write(stu.toString() + "\t");
            bufw.write(stu.getSum() + "");
            bufw.newLine();
            bufw.flush();
        }
        
        bufw.close();
    }
}

public class Practice {
    public static void main(String[] args) throws IOException {
        Comparator<Student> cmp = Collections.reverseOrder();
        
        Set<Student> stus = StudentInfoTool.getStudents(cmp);
        
        StudentInfoTool.write2File(stus);
    }
}

转载于:https://www.cnblogs.com/coolcpp/p/java-io.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值