IO流基础(字节流)

来源:互联网 发布:虚拟社交网络利大于弊 编辑:程序博客网 时间:2024/06/05 09:41

字节流:

InputStream是表示字节输入流的所有类的超类。

     |--- FileInputStream:操作文件从文件系统中的某个文件中获得输入字节。哪些文件可用取决于主机环境。FileInputStream用于读取诸如图像数据之类的原始字节流。要读取字符流,请考虑使用 FileReader。

     |--- FilterInputStream包含其他一些输入流,它将这些流用作其基本数据源,它可以直接传输数据或提供一些额外的功能。

       |--- BufferedInputStream该类实现缓冲的输入流。在创建 BufferedInputStream 时,会创建一个内部缓冲区数组。  没有特殊方法没有读取行方法

       |--- Stream

     |--- ObjectInputStream

     |--- PipedInputStream

-----------------------------------------------

OutputStream此抽象类是表示输出字节流的所有类的超类。

     |--- FileOutputStream文件输出流是用于将数据写入FileFileDescriptor 的输出流。

     |--- FilterOutputStream此类是过滤输出流的所有类的超类。

       |--- BufferedOutputStream该类实现缓冲的输出流。该类实现缓冲的输出流。通过设置这种输出流,应用程序就可以将各个字节写入底层输出流中,而不必针对每次字节写入调用底层系统。没有换行方法

       |--- PrintStream

       |--- DataOutputStream

     |--- ObjectOutputStream

     |--- PipedOutputStream

----------------------------------------------------------------

InputStream主要方法

abstract  int read()

           从输入流中读取数据的下一个字节。

 int read(byte[] b)

          从输入流中读取一定数量的字节,并将其存储在缓冲区数组 b 中。

  int read(byte[] b, int off, int len)

           将输入流中最多 len 个数据字节读入 byte 数组。

 void reset()

           将此流重新定位到最后一次对此输入流调用 mark 方法时的位置。

  long skip(long n)

           跳过和丢弃此输入流中数据的n 个字节。

 OutputStream主要方法

void close()

            关闭此输出流并释放与此流有关的所有系统资源。

  void flush()

           刷新此输出流并强制写出所有缓冲的输出字节。

 void write(byte[] b)

      将 b.length 个字节从指定的 byte 数组写入此输出流。

  void write(byte[] b, int off, int len)

           将指定 byte 数组中从偏移量 off 开始的 len 个字节写入此输出流。

abstract  void write(int b)

           将指定的字节写入此输出流。

 

--------------------------------

读取键盘输入

System.out:对应的是标准输出设备。控制台     输出的是字节流

System.in:对应的是标准的输入设备:键盘      输入的是字节流

static void setIn(InputStream in)   重新分配“标准”输入流。

   将输入源标准输入设备  改成 一个指定文件   System.setIn(newFileInputStream("a.txt"));

static void setOut(PrintStream out)  重新分配“标准”输出流。

    将输出到 标准输出设备  改成 一个指定文件  System.setOut(PrintStream("a.txt"));

 

//记住,键盘录入最常见写法

BufferedReader bufr = new BufferedReader(newInputStreamReader(System.in));

BufferedWriter bufw = new BufferedWriter(newOutputStreamWriter(System.out));//输出到控制台

 ================================================================================

流对象:其实很简单,就是读取和写入。但是因为功能的不同,流的体系中提供N多的对象。那么开始时,到底该用哪个对象更为合适呢?这就需要明确流的操作规律。

流的操作规律:

1,明确源和目的。

       数据源:就是需要读取,可以使用两个体系:InputStreamReader

       数据汇:就是需要写入,可以使用两个体系:OutputStreamWriter

2,操作的数据是否是纯文本数据?

       如果是:数据源:Reader

                  数据汇:Writer

       如果不是:数据源:InputStream

                    数据汇:OutputStream

3,虽然确定了一个体系,但是该体系中有太多的对象,到底用哪个呢?

       明确操作的数据设备。

       数据源对应的设备:硬盘(File),内存(数组),键盘(System.in)

       数据汇对应的设备:硬盘(File),内存(数组),控制台(System.out)

4,需要在基本操作上附加其他功能吗?比如缓冲。

       如果需要就进行装饰。

============================================

复制一个mp3文件:

思路:

1、用字节读取流对象和mp3关联。

2、用字节写入流对象创建一个mp3文件。用于存储获取到的mp3数据。

3、通过循环读写,完成数据的存储。

4、关闭资源。

代码演示:

import java.io.*;class mp3CopyDemo {public static void main(String[] args) throws Exception{double start = System.currentTimeMillis();//获取当前时间mp3Copy();double end = System.currentTimeMillis();//获取当前时间System.out.println("复制一首歌需要:"+(end-start)+"毫秒");}public static void mp3Copy() throws Exception{BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream("五月天 - 2而我知道.mp3"));BufferedInputStream bis = new BufferedInputStream(new FileInputStream("D:\\KuGou\\五月天 - 而我知道.mp3"));int a = 0;while ((a=bis.read())!=-1){bos.write(a);//写入什么数据}bis.close();bos.close();}}

为什么不能用字符流,复制mp3呢?

因为:读取完数据后,字符流会去查码表,如果码表里面,没有这个数字,就会进入未知数据区域寻找编码,这样图片的编码就会变化。

-------------------------------------------------------------------------------------------------------

自定义 字节缓冲区

把读取到的一个数据中的字节文件 read(b)存储到 byte[] b字节数组中

一直重复直到字节中的数组装满

然后将 字节数组中的字节数据一字节一字节的写入到 目标文件中

int reader()  返回的是读取到的单个字节(字符)的int值

int read(byte[] ) 将数据读取到字节数组中 返回读取到的字节长度

int read(byte[] ) 原理

把读取到的一个数据中的字节文件 通过read()返回 存储到 byte[] b字节数组中

一直重复直到字节中的数组装满  然后将字节数组中的数据使用完后继续读取数据直到数据末尾

import java.io.*;class myBufferedInputStream{InputStream in = null;private int count = 0;private int pow = 0;byte[] b = new byte[1024];myBufferedInputStream(InputStream in){this.in = in;}public int myRead() throws Exception{if (count==0){count = in.read(b);if(count<0)return -1;count--;pow = 0;pow++;byte by=b[0];return by&255; //byte型数据自动提升为整型数据 然后 和255相与}else if (count > 0){count--;byte by = b[pow++];return by&0xff;}return -1;}public void myClose() throws Exception{in.close();}}
补充了解:

int read(byte[] )的返回值类型的一些说明

/*为什么 返回的是byte b[pow] 却声明 函数返回值类型为 int  为什么要类型提升??
因为 字节流中的读取是一字节一字节的读取 数据本身按一字节一字节读取的话 可能会有某个字节的值为-1
这会和 读取到数据末尾返回的-1 相同 导致数据读取伪结束 产生错误 
通过 把 byte型-1 和 255相与 提升到 int类型数据 同时保留数据不变 myRead就不会返回-1了(数据本身某部分字节为-1 
提升到int 变成非-1) 从而避免了 和 数据末尾的-1撞衫的情况  保全读取数据的完整

字节数据  一字节 1 byte=8 bit(位)  int = 4 byte =16 bit(位)
字节数据都是由01二进制数组成的 
数据中某字节可能是 1111-1111  这样的字节数据 为-1 这会和读取到数据末尾的标识 -1 撞衫
所以 需要将读取到的 字节数据 提升 到 int整型数据 然后返回  可是直接提升到 int也是-1 
1111-1111  直接提升为整型为 1111-1111 1111-1111 1111-1111 1111-1111 出现数据变化
单身 通过 字节数据和255 相与 可取前8位 其余补0 这样 即保证数据不失真 而且 返回的int不会是-1 避免了撞衫的情况
byte & int 首先 byte自动提升为 int
1111-1111 1111-1111 1111-1111 1111-1111  (byte 1111-1111提升后成int)
0000-0000 0000-0000 0000-0000 1111-1111   255
0000-0000 0000-0000 0000-0000 1111-1111
前八位不变 其余补0  前八位就是 原数据中的 字节数据 实现了原数据的字节返回给调用者 write将int强转为byte 使数据保真

按位与  两位同时为“1”,否则为0
按位或  一1为1 两0为0
按位异或  相同为0 不同为1
========================================================

转换流特有功能:转换流可以将字节转成字符,原因在于,将获取到的字节通过查编码表获取到指定对应字符。

转换流的最强功能就是基于 字节流 + 编码表 。没有转换,没有字符流。

发现转换流有一个子类就是操作文件的字符流对象:

InputStreamReader

       |--FileReader

OutputStreamWriter

       |--FileWrier

想要操作文本文件,必须要进行编码转换,而编码转换动作转换流都完成了。所以操作文件的流对象只要继承自转换流就可以读取一个字符了。

但是子类有一个局限性,就是子类中使用的编码是固定的,是本机默认的编码表,对于简体中文版的系统默认码表是GBK。

FileReader fr = newFileReader("a.txt");

InputStreamReader isr = newInputStreamReader(new FileInputStream("a.txt"),"gbk");

以上两句代码功能一致,

如果仅仅使用平台默认码表,就使用FileReader fr = new FileReader("a.txt"); //因为简化。

如果需要按照指定码表,必须用转换流。

转换流 = 字节流+编码表。

凡是操作设备上的文本数据,涉及编码转换,必须使用转换流。

--=-=-=-=-=-==-=-=-=-=
读取转换流   
接收的是字节输入流对象 InputStream   字节输入流-->字符读取流
InputStreamReader  (InputStream in)   (extends Reader)
是字节流通向字符流的桥梁:读取字节并将其解码为字符
InputStreamReader 继承了Reader类 
通过构造函数 BufferedReader(Reader )创建字符缓冲区
BufferedReader bfr = new BufferedReader(new InputStreamReader(System.in));
BufferedReader缓冲区提供了一个一次读取一行的方法String readLine(),方便对文本数据的获取 不获取换行符
当返回null时,表示读到文件末尾

写入转换流  
接收的是字节输出流对象 OutputStream    字符写入流-->字节输出流
OutputStreamWriter  (OutputStream out) (extends Writer)
是字符流通向字节流的桥梁:将要写入流中的字符编码成字节。
为了获得最高效率,可考虑将 OutputStreamWriter 包装到 BufferedWriter 中
BufferedWriter out = new BufferedWriter(new OutputStreamWriter(System.out)); //一次写完

需求:
通过键盘录入一行数据后,就将该行数据进行打印
如果所录入的数据是over,那么停止输入
结束输入的方式有两中 
1,自定义结束
2,ctrl+c(就是-1)强制结束  
import java.io.*;class ReadIn {public static void main(String[] args) throws Exception{//ReadIn0();zhuanhuanliu2();}public static void zhuanhuanliu2() throws Exception{/*//获取输入流对象InputStream in = System.in;//将字节输入流对象 转换为 字符读取流对象InputStreamReader isr = new InputStreamReader(in);//为了提高效率,将字符串进行缓冲区技术高效操作BufferedReader bfr = new BufferedReader(isr);*///键盘录入最常见写法】BufferedReader bfr = new BufferedReader(new InputStreamReader(System.in));/*//创建输出流对象OutputStream out = System.out; //将字节输出流对象 转换为 字符写入流对象OutputStreamWriter osw = new OutputStreamWriter(out);BufferedWriter bfw = new BufferedWriter(osw);//特有换行方法*///BufferedWriter bfw = new BufferedWriter(new OutputStreamWriter(System.out));//将输入的数据 存储到指定文件中BufferedWriter bfw = new BufferedWriter(new OutputStreamWriter(new FileOutputStream("流测试.txt")));//能够操作文件的是FileOutputStream类String s = null;while ((s=bfr.readLine())!=null) //读取行 readLine(){if(s.equals("over"))break;bfw.write(s);  //将字符串s写入到指定目标 文件或输出台bfw.newLine();bfw.flush();// osw.write(s); 只刷入osw.write(s);没刷入bfw.newLine();}bfr.close();bfw.close();}public static void zhuanhuanliu() throws Exception{//获取输入流对象InputStream in = System.in;//将字节输入流对象 转换为 字符读取流对象InputStreamReader isr = new InputStreamReader(in);//为了提高效率,将字符串进行缓冲区技术高效操作BufferedReader bfr = new BufferedReader(isr);String s = null;while ((s=bfr.readLine())!=null) //读取行 readLine(){System.out.println(s);if(s.equals("over"))break;}bfr.close();}public static void ReadIn0() throws Exception{InputStream in = System.in; //创建字节输入流对象输入的形式是字符  //int b = in.read();//执行一次读取一个字节  回车键(\r\n)是两字符的  //System.out.println(b);StringBuilder sb = new StringBuilder() ;//实际上是一行一行的读取  想到字符流中的LineRead() -->转换流while (true){int by = in.read();//当read()返回-1的时候结束读取 同时结束输入(ctrl+c就是-1)否则一直循环 等待输入if(by=='\r')continue;if(by=='\n'){String s = sb.toString();System.out.println(s);if("over".equals(s))break;sb.delete(0,by);}elsesb = sb.append((char)by);}in.close();}}
=========================================================================

类 File 文件和目录路径名的抽象表示形式。 
用来将文件或者目录封装成对象
方便对文件与目录的属性信息进行操作。
File对象可以作为参数传递给流的构造函数。
File.separator
static String separator 与系统有关的默认名称分隔符  跨系统的默认分隔符  windows中的分隔符为\\也是/ 

了解File类中的常用方法。
构造方法 此时并没有在指定处 创建文件或目录  硬盘指定处创建文件 createNewFile()  创建目录mkdir()
File(File parent, String 文件名)    文件名可以是文件名也可以是目录名
根据 parent 抽象路径名和 child 路径名字符串创建一个新(文件或目录) File 实例。
File(String 创建的文件名)      文件名可以是文件名也可以是目录名
通过将给定路径名字符串转换为抽象路径名来创建一个新(文件或目录) File 实例
File(String 必须存在的目录父目录, String 创建的文件名)     文件名可以是文件名也可以是目录名
根据 parent 路径名字符串和 child 路径名字符串创建一个新(文件或目录) File 实例。

注意:创建的实物 是文件还是目录  是由mkdir()和createNewFile() 决定的  
  目录 也可以命名为 a.txt

File类的常见方法
1,创建

boolean createNewFile()  文件对象不存在的时候创建 文件存在则不创建返回false 文件不存在则返回false  和输出流不一样,输出流对象 创建文件,当文件存在的时候会覆盖

boolean mkdir() 创建此抽象路径名指定的目录。 只能够创建一级目录 abd/hhh 不能创建abd/hhh/jj
boolean mkdirs() 创建多级目录。
2,删除
boolean delete() 删除此抽象路径名表示的 文件或目录。 删除失败 返回false 
public void deleteOnExit() 在虚拟机终止时,请求删除此抽象路径名表示的文件或目录。
3,判断
boolean exists() 测试此抽象路径名(File对象)表示的文件或目录是否存在。
boolean isDirectory()  测试此抽象路径名(File对象)表示的文件是否是一个目录。  需要先判断 isexists()  
boolean isFile()  测试此抽象路径名(File对象)表示的文件是否是一个标准文件。   需要先判断 isexists() 
boolean isHiden()  判断文件是否被隐藏
boolean isAbsolute()测试此抽象路径名是否为绝对路径名。 绝对路径名 D:/a.txt 相对路径名 a.txt
int compareTo(File pathname) 按字母顺序比较两个抽象路径名。 
boolean canRead() 该文件对象(抽象路径名)能否被读取。 
boolean canWrite() 该文件对象能否被写入。 
4,获取信息
String getName() 返回由此抽象路径名表示的 文件或目录的名称。 
String getParent() 返回 父目录 的路径名字符串;如果此路径名没有指定父目录,则返回 null。 
String getPath() 将此抽象路径名转换为一个路径名字符串。 封装的是什么路径 返回就什么路径
getAbsolutePath() 获取绝对路径 不论文件是否存在  路径包括目录和文件
long lastModified() 返回此抽象路径名表示的文件最后一次被修改的时间。 
long length() 返回由此抽象路径名表示的文件的长度。 
static File[] listRoots() 列出可用的文件系统根。 
String[] list() 返回一个字符串数组,这些字符串指定此抽象路径名表示的目录中的文件和目录。 文件和目录 
//调用list()方法的对象必须是目录 该目录还必须存在
File[] listFiles()  返回调用者对象(目录)下的所有文件和目录的File对象  文件的File对象和目录的File对象

5:重命名。

     boolean renameTo(Filedest):可以实现移动的效果。剪切+重命名。

代码演示:

import java.io.*;class fileDemo {public static void main(String[] args) throws IOException{//fileMethod();//fileMethod_1();//renameto();//listDemo();list2Demo();}//过滤 获取某类型某名称 文件 目录public static void list2Demo(){//创建指定目录的File对象  将指定目录封装成对象File dir = new File("F:/java30/d19");//获取 指定目录下的所有 文件 和 目录(文件夹)//String[] sts = dir.list();//通过过滤 只获取 mp3文件String[] list(FilenameFilter接口 dir ,String name)String[] sts = dir.list(new FilenameFilter(){public boolean accept(File dir,String name){return name.endsWith(".mp3");}});for (String st:sts ){System.out.println(st);}}//获取指定 目录下 所有文件 目录public static void listDemo(){//获取盘符File[] fs = File.listRoots();for(File f:fs){System.out.println(f);//获取各盘符下的所有 文件 目录String[] sts = f.list();for (String st:sts ){System.out.println(st);}}System.out.println("获取某指定目录H:\\下的所有文件和目录");//获取某指定目录下的所有文件和目录File fi = new File("H:\\");//创建抽象路径名//获取指定 目录下 所有文件 目录String[] sts = fi.list();//调用list()方法的对象必须是目录 该目录还必须存在for (String st:sts ){System.out.println(st);}}//renameTo 功能类似 移动 可换名public static void renameto() throws IOException{File f1 = new File("e:\\"+"00renameto.txt");//创建//f1.createNewFile();File f = new File("D:\\"+"00b.txt");sop("reNameTo:"+f.renameTo(f1));//被移动的文件必须要先存在}//判断 创建的File对象是 文件  还是 目录public static void fileMethod_1() throws IOException{//创建File对象File f = new File("h:\\"+"00b.txt");//创建实物 文件 或 目录sop("当文件对象实物不存在时创建:"+f.createNewFile());//判断 File对象所指的文件或目录 是否存在sop("是File对象是否创建实物吗:"+f.exists());//判断创建的File对象是文件呢还是目录sop("是文件吗:"+f.isFile());sop("是目录吗:"+f.isDirectory());sop("getName()"+f.getName());sop("getPath()"+f.getPath());sop("getParent()"+f.getParent());//如果获取的是相对路径则返回null  如果相对路径中有上一层目录 该目录就是返回结果sop("getAbsolutePath()"+f.getAbsolutePath());f.delete();}public static void fileMethod() throws IOException{//将文件00b.txt封装成fiel对象,可以将已有的和未有的文件或目录封装成对象File f2 = new File("h:\\00b.txt");//指定路径下创建文件//默认分隔符File.separator等价于\\//File f1 = new File("h:"+File.separator+"00b.txt");//在指定路径下 将以有或未有的文件或目录封装成文件对象  当指定路径不存在时 跑异常File f4 = new File("H:\\Download\\","001.txt");//在相对路径下创建文件对象File f1 = new File("00b.txt");//创建文件路径的file对象//File d = new File("F:\\java30\\d20");File d = new File("F:"+File.separator+"java30"+File.separator+"d20");//默认分隔符的使用File f3 = new File(d,"00c.txt");//构造函数封装什么打印什么sop("f1:"+f1);sop("f2:"+f2);sop("f3:"+f3);sop("f4:"+f4);// boolean canExecute()  测试应用程序是否可以执行此抽象路径名表示的文件。 文件对象能否至执行sop("文件对象能否能执行(文件创建前) "+f1.canExecute());sop("文件是否存在(文件创建前) "+f1.canExecute());//当文件对象不存在时 通过createNewFile()方法创建文件sop("当文件对象f1不存在时创建:"+f1.createNewFile());sop("当文件对象f2不存在时创建:"+f2.createNewFile());sop("当文件对象f3不存在时创建:"+f3.createNewFile());sop("当文件对象f4不存在时创建:"+f4.createNewFile());sop("文件对象能否能执行(文件创建后) "+f1.canExecute());sop("文件是否存在(文件创建后) "+f1.canExecute());//通过文件对象删除指定的文件sop("删除f1  "+f1.delete());sop("删除f2  "+f2.delete());sop("删除f3  "+f3.delete());//sop("删除f4  "+f4.delete());sop("文件对象能否至执行(文件删除后) "+f1.canExecute());//虚拟机推出的时候删除文件sop("程序退出时删除f4  ");f4.deleteOnExit();//当前目录下创建目录/目录 mkdir()File dir = new File("abd/hhh");//只能够创建一级目录 abd/hhh 不能创建abd/hhh/jjsop("当前目录下创建目录dir:"+dir.mkdir());//目录已经存在返回false 不存在 返回true//创建指定目录下的目录//File dir2 = new File("h:/"+"abd");//当指定目录不存在时 创建目录失败 不抛异常//sop("指定目录下创建目录dir2:"+dir2.mkdir());//sop("删除目录dir1  "+dir.delete());//sop("删除目录dir2  "+dir2.delete());}public static void sop(Object o){System.out.println(o);}}




0 0
原创粉丝点击