A股上市公司传智教育(股票代码 003032)旗下技术交流社区北京昌平校区




8、IO

8.2 IO流

    8.2.11  IO包中的其他类

    需求:文件切割器。

    代码:
  1. import java.io.File;
  2. import java.io.FileInputStream;
  3. import java.io.FileOutputStream;
  4. import java.io.IOException;
  5. import java.util.Properties;

  6. public class SplitFileDemo{

  7.        private static final int SIZE = 1024*1024;
  8.       
  9.        public static void main(String[] args) throws IOException{
  10.             File file = new File("0.mp3" );
  11.             splitFile(file);
  12.       }

  13.        public static void splitFile(File file) throws IOException {
  14.              //用读取流关联源文件
  15.             FileInputStream fis = new FileInputStream(file);

  16.              //定义一个1M的缓冲区
  17.              byte[] buf = new byte[SIZE];
  18.       
  19.              //创建目的
  20.             FileOutputStream fos = null;
  21.             
  22.              int len = 0;
  23.              int count = 1;

  24.              //切割文件时,必须记录住被切割文件的名称,以及切割出来碎片文件的个数,以方便于合并。
  25.              //这个信息为了进行描述,使用键值对的方式,用到了properties对象。

  26.             Properties prop = new Properties();

  27.             File dir = new File("c:\\partFiles" );
  28.              if(!dir.exists())
  29.                   dir.mkdirs();

  30.              while((len = fis.read(buf)) != -1){
  31.                   fos = new FileOutputStream(new File(dir,(count++) + ".part"));
  32.                   fos.write(buf,0,len);
  33.                   fos.close();
  34.             }
  35.             
  36.              //将被切割文件的信息保存到prop集合中
  37.             prop.setProperty( "partcount",count + "" );
  38.             prop.setProperty( "filename",file.getName());

  39.             fos = new FileOutputStream(new File(dir,count + ".properties" ));
  40.             
  41.              //将prop集合中的数据存储到文件中
  42.             prop.store(fos, "save file info");

  43.             fis.close();
  44.             fos.close();
  45.       }
  46. }
复制代码
   运行结果:






    需求:文件合并器。  

    代码:
  1. import java.io.File;
  2. import java.io.FileInputStream;
  3. import java.io.FileOutputStream;
  4. import java.io.FilenameFilter;
  5. import java.io.IOException;
  6. import java.io.SequenceInputStream;
  7. import java.util.ArrayList;
  8. import java.util.Collections;
  9. import java.util.Enumeration;
  10. import java.util.Iterator;
  11. import java.util.Properties;

  12. public class MergeFile{
  13.        public static void main(String[] args) throws IOException {
  14.             File dir = new File("c:\\partFiles" );
  15.             mergeFile(dir);
  16.       }

  17.        public static void mergeFile(File dir) throws IOException {

  18.              //获取指定目录下的配置文件对象
  19.             File[] files = dir.listFiles( new SuffixFilter(".properties" ));
  20.             
  21.              if(files.length!=1)
  22.                    throw new RuntimeException(dir + ",该目录下没有properties扩展名的文件或者不唯一" );
  23.             
  24.              //记录配置文件对象
  25.             File confile = files[0];

  26.              //获取该文件中的信息
  27.             Properties prop = new Properties();
  28.             FileInputStream fis = new FileInputStream(confile);

  29.             prop.load(fis);

  30.             String filename = prop.getProperty( "filename");

  31.              int count = Integer.parseInt(prop.getProperty("partcount"));
  32.             
  33.              //获取该目录下的所有碎片文件
  34.             File[] partFiles = dir.listFiles( new SuffixFilter(".part" ));

  35.              if(partFiles.length != (count - 1)){
  36.                    throw new RuntimeException("碎片文件不符合要求,个数不对!应该是" + count + "个");
  37.             }
  38.             
  39.              //将碎片文件和流对象关联并存储到集合中
  40.             ArrayList<FileInputStream> al = new ArrayList<FileInputStream>();

  41.              for(int x = 1; x <= partFiles.length; x++){
  42.                   al.add( new FileInputStream(partFiles[x-1]));
  43.             }

  44.              final Iterator<FileInputStream> it = al.iterator();
  45.             
  46.              //将多个流合并成一个序列流
  47.             Enumeration<FileInputStream> en = Collections.enumeration(al);

  48.             SequenceInputStream sis = new SequenceInputStream(en);

  49.             FileOutputStream fos = new FileOutputStream(new File(dir,filename));

  50.              byte[] buf = new byte[1024*1024];
  51.             
  52.              int len = 0;

  53.              while((len = sis.read(buf)) != -1){
  54.                   fos.write(buf,0,len);   
  55.             }

  56.             fos.close();
  57.             sis.close();
  58.       }
  59. }

  60. class SuffixFilter implements FilenameFilter{
  61.        private String suffix;

  62.        public SuffixFilter(String suffix){
  63.              super();
  64.              this.suffix = suffix;
  65.       }

  66.        public boolean accept(File dir,String name){
  67.              return name.endsWith(suffix);
  68.       }
  69. }
复制代码
    运行结果:




    操作对象
    ObjectInputStream与ObjectOutputStream


    P.S.
    被操作的对象需要实现Serializable。

    类通过实现java.io.Serializable接口以启用序列化功能,Serializable只是一个标记接口。

    示例1:
  1. import java.io.Serializable;

  2. class Person implements Serializable{
  3.        private String name;
  4.        private int age;

  5.        public Person(String name,int age){
  6.              this.name = name;
  7.              this.age = age;
  8.       }

  9.        public String getName(){
  10.              return name;
  11.       }

  12.        public void setName(String name){
  13.              this.name = name;
  14.       }

  15.        public int getAge(){
  16.              return age;
  17.       }

  18.        public void setAge(int age){
  19.              this.age = age;
  20.       }
  21. }
复制代码
    运行结果:



    示例2:
  1. import java.io.FileInputStream;
  2. import java.io.ObjectInputStream;

  3. public class ObjectStreamDemo{
  4.        public static void main(String[] args) throws Exception {
  5.             readObj();
  6.       }

  7.        public static void readObj() throws Exception {
  8.             ObjectInputStream ois = new ObjectInputStream(new FileInputStream("obj.object" ));
  9.             
  10.             Person p = (Person)ois.readObject();

  11.             System.out.println(p.getName() + ":" + p.getAge());

  12.             ois.close();
  13.       }
  14. }
复制代码
   运行结果:

    如果修改Person类中属性的修饰符为public,就会报如下异常。


   

    原因分析:

    Serializable:用于给被序列化的类加入ID号,用于判断类和对象是否是同一个版本。
    API解释如下:


    为Person类添加序列号属性。此时,再将Person类中属性修饰符修改为public,也不会出现任何异常。
  1. class Person implements Serializable{
  2.        private static final long serialVersionUID = 9527;

  3.        public String name ;
  4.        public int age ;

  5.        public Person(String name,int age){
  6.              this.name = name;
  7.              this.age = age;
  8.       }

  9.        public String getName(){
  10.              return name ;
  11.       }

  12.        public void setName(String name){
  13.              this.name = name;
  14.       }

  15.        public int getAge(){
  16.              return age ;
  17.       }

  18.        public void setAge(int age){
  19.              this.age = age;
  20.       }
  21. }
复制代码

    writeObject方法不能写入类及其所有超类型的瞬态和静态字段的值。

    示例3:  
  1. import java.io.Serializable;

  2. class Person implements Serializable{
  3.        private static final long serialVersionUID = 9527;

  4.        private transient String name;
  5.        private static int age;

  6.        public Person(String name,int age){
  7.              this.name = name;
  8.              this.age = age;
  9.       }

  10.        public String getName(){
  11.              return name;
  12.       }

  13.        public void setName(String name){
  14.              this.name = name;
  15.       }

  16.        public int getAge(){
  17.              return age;
  18.       }

  19.        public void setAge(int age){
  20.              this.age = age;
  21.       }
  22. }
复制代码
    运行结果:

    RandomAccessFile

    随机访问文件,自身具备读写的方法。
    通过skipBytes(int x),seek(int x)等方法来达到随机访问。

    特点:
    1. 该对象即能读,又能写。
    2. 该对象内部维护了一个byte数组,并通过指针可以操作数组中的元素。
    3. 可以通过getFilePointer方法获取指针的位置,和通过seek方法设置指针的位置。
    4. 其实该对象就是将字节输入流和输出流进行了封装。
    5. 该对象的源或者目的只能是文件。通过构造函数就可以看出。


    P.S.
    RandomAccessFile不是io体系中的子类。

    示例1:
  1. import java.io.IOException;
  2. import java.io.RandomAccessFile;

  3. public class RandomAccessFileDemo{
  4.        public static void main(String[] args) throws IOException {
  5.             writeFile();
  6.       }

  7.        //使用RandomAccessFile对象写入一些人员信息,比如姓名和年龄
  8.        public static void writeFile() throws IOException {
  9.              //如果文件不存在,则创建,如果文件存在,不创建
  10.             RandomAccessFile raf = new RandomAccessFile("ranacc.txt" ,"rw" );
  11.             
  12.             raf.write( "张三".getBytes());
  13.              //使用write方法之写入最后一个字节
  14.             raf.write(97);
  15.              //使用writeInt方法写入四个字节(int类型)
  16.             raf.writeInt(97);
  17.             
  18.             raf.write( "小强".getBytes());
  19.             raf.writeInt(99);

  20.             raf.close();
  21.       }
  22. }
复制代码
    运行结果:



    示例2:  
  1. import java.io.IOException;
  2. import java.io.RandomAccessFile;

  3. public class RandomAccessFileDemo{
  4.        public static void main(String[] args) throws IOException {
  5.             readFile();
  6.       }

  7.        public static void readFile() throws IOException {
  8.             RandomAccessFile raf = new RandomAccessFile("ranacc.txt" ,"r" );
  9.             
  10.              //通过seek设置指针的位置
  11.             raf.seek(9); //随机的读取,只要指定指针的位置即可

  12.              byte[] buf = new byte[4];           
  13.             raf.read(buf);

  14.             String name = new String(buf);
  15.             System.out.println( "name=" + name);

  16.              int age = raf.readInt();
  17.             System.out.println( "age=" + age);

  18.             System.out.println( "pos:" + raf.getFilePointer());

  19.             raf.close();
  20.       }
  21. }
复制代码
    运行结果:

    示例3:  
  1. import java.io.IOException;
  2. import java.io.RandomAccessFile;

  3. public class RandomAccessFileDemo{
  4.        public static void main(String[] args) throws IOException {
  5.             randomWrite();
  6.       }

  7.        public static void randomWrite() throws IOException {
  8.             RandomAccessFile raf = new RandomAccessFile("ranacc.txt" ,"rw" );
  9.             
  10.              //往指定位置写入数据
  11.             raf.seek(3*8);
  12.             
  13.             raf.write( "哈哈".getBytes());
  14.             raf.writeInt(102);

  15.             raf.close();
  16.       }
  17. }
复制代码
     运行结果:



    管道流
    PipedInputStream和PipedOutputStream:输入输出可以直接进行连接,通过结合线程使用。


    示例1:
  1. import java.io.PipedInputStream;
  2. import java.io.PipedOutputStream;

  3. public class PipedStream{
  4.        public static void main(String[] args) throws Exception {
  5.             PipedInputStream input = new PipedInputStream();
  6.             PipedOutputStream output = new PipedOutputStream();

  7.             input.connect(output);

  8.              new Thread(new Input(input)).start();
  9.              new Thread(new Output(output)).start();
  10.       }
  11. }

  12. class Input implements Runnable{
  13.        private PipedInputStream in;
  14.       
  15.       Input(PipedInputStream in){
  16.              this.in = in;
  17.       }

  18.        public void run(){
  19.              try{
  20.                    byte[] buf = new byte[1024];
  21.                    int len = in.read(buf);

  22.                   String s = new String(buf,0,len);
  23.                   System.out.println( "s=" + s);
  24.                   in.close();
  25.             } catch(Exception e){
  26.                   e.printStackTrace();
  27.             }
  28.       }
  29. }

  30. class Output implements Runnable{
  31.        private PipedOutputStream out;
  32.       
  33.       Output(PipedOutputStream out){
  34.              this.out = out;
  35.       }

  36.        public void run(){
  37.              try{
  38.                   out.write( "hi,管道来了!" .getBytes());
  39.                   out.close();
  40.             } catch(Exception e){
  41.                   e.printStackTrace();
  42.             }
  43.       }
  44. }
复制代码
   运行结果:


    操作基本数据类型
    DataInputStream与DataOutputStream

    示例1:
  1. import java.io.DataOutputStream;
  2. import java.io.FileOutputStream;
  3. import java.io.IOException;

  4. public class DataStreamDemo{
  5.        public static void main(String[] args) throws IOException {
  6.             writeData();
  7.       }

  8.        public static void writeData() throws IOException {
  9.             DataOutputStream dos = new DataOutputStream(new FileOutputStream("data.txt" ));
  10.       
  11.             dos.writeUTF( "您好");

  12.             dos.close();
  13.       }
  14. }
复制代码
   运行结果:





    示例2:
  1. import java.io.DataInputStream;
  2. import java.io.FileInputStream;
  3. import java.io.IOException;

  4. public class DataStreamDemo{
  5.        public static void main(String[] args) throws IOException {
  6.             readData();
  7.       }

  8.        public static void readData() throws IOException {
  9.             DataInputStream dis = new DataInputStream(new FileInputStream("data.txt" ));
  10.       
  11.             String str = dis.readUTF();
  12.             
  13.             System.out.println(str);

  14.             dis.close();
  15.       }
  16. }
复制代码
    运行结果:

    操作字节数组
    ByteArrayInputStream与ByteArrayOutputStream


    P.S.
    关闭字节数组输出输出流无效,因为它们没有调用底层资源,所有的操作都是在内存中完成的。

    示例1:
  1. import java.io.ByteArrayInputStream;
  2. import java.io.ByteArrayOutputStream;
  3. import java.io.IOException;

  4. public class ByteArrayStreamDemo{
  5.        public static void main(String[] args) throws IOException {
  6.             ByteArrayInputStream bis = new ByteArrayInputStream("abcdef" .getBytes());
  7.             ByteArrayOutputStream bos = new ByteArrayOutputStream();

  8.              int ch = 0;

  9.              while((ch = bis.read()) != -1){
  10.                   bos.write(ch);
  11.             }

  12.             System.out.println(bos.toString());
  13.       }
  14. }
复制代码
   运行结果:

    8.2.12  编码表

    编码表的由来
    计算机只能识别二进制数据,早期由来是电信号。为了方便应用计算机,让它可以识别各个国家的文字。
    就将各个国家的文字用数字来表示,并一一对应,形成一张表,这就是编码表。

    常见的编码表
    ASCII:美国标准信息交换码,用一个字节的7位可以表示。
    ISO8859-1:拉丁码表。欧洲码表,用一个字节的8位表示。
    GB2312:中国的中文编码表。
    GBK:中国的中文编码表升级,融合了更多的中文文字符号。
    Unicode:国际标准码,融合了多种文字。
    所有文字都用两个字节来表示,Java语言使用的就是unicode
    UTF-8:最多用三个字节来表示一个字符。
    ......

    示例1:
  1. import java.io.IOException;

  2. public class EncodeDemo{
  3.        public static void main(String[] args) throws IOException{
  4.              //字符串-->字节数组:编码
  5.              //字符数组-->字符串:解码
  6.             String str = "您好";

  7.              //编码
  8.              byte[] buf1 = str.getBytes("GBK" );
  9.             printBytes(buf1);

  10.              byte[] buf2 = str.getBytes("UTF-8" );
  11.             printBytes(buf2);

  12.              //解码
  13.             String s1 = new String(buf1);
  14.             System.out.println( "s1 = " + s1);

  15.             String s2 = new String(buf2,"UTF-8" );
  16.             System.out.println( "s2 = " + s2);
  17.       }

  18.        private static void printBytes(byte[] buf){
  19.              for(byte b : buf){
  20.                   System.out.print(b + " ");
  21.             }
  22.             System.out.println();
  23.       }
  24. }
复制代码
    运行结果:


    如果编码编错了,解不出来。
    如果编对了,解错了,有可能有救。

    示例2:
  1. import java.io.IOException;

  2. public class EncodeDemo{
  3.        public static void main(String[] args) throws IOException{
  4.             String str = "您好";
  5.             
  6.              byte[] buf = str.getBytes("gbk" );

  7.             String s1 = new String(buf,"iso8859-1" );

  8.             System.out.println( "s1 = " + s1);

  9.              byte[] buf2 = s1.getBytes("iso8859-1" );
  10.             String s2 = new String(buf2,"gbk" );

  11.             System.out.println(s2);
  12.       }

  13.        private static void printBytes(byte[] buf){
  14.              for(byte b : buf){
  15.                   System.out.println(b);
  16.             }
  17.       }
  18. }
复制代码
   运行结果:

    如果编对了,解错了,也可能没救了。

    示例3:
  1. import java.io.IOException;

  2. public class EncodeDemo{
  3.        public static void main(String[] args) throws IOException{
  4.             String str = "您好";
  5.             
  6.              byte[] buf = str.getBytes("gbk" );

  7.             String s1 = new String(buf,"UTF-8" );

  8.             System.out.println( "s1 = " + s1);

  9.              byte[] buf2 = s1.getBytes("UTF-8" );
  10.             
  11.             printBytes(buf2);

  12.             String s2 = new String(buf2,"gbk" );

  13.             System.out.println(s2);
  14.       }

  15.        private static void printBytes(byte[] buf){
  16.              for(byte b : buf){
  17.                   System.out.print(b + " ");
  18.             }
  19.             System.out.println();
  20.       }
  21. }
复制代码
    运行结果:

    原因分析:
    “您好”的gbk编码在UTF-8码表中查不到对应的字符,所以已经用“?”代替。
    “?”在UTF-8中的编码为-17 -65 -67
    故即使使用UTF-8码表进行解码,获取的字节也不是“您好”的gbk编码后的字节。
    所以再也不能成功解码了。

    P.S.
    “谢谢”的gbk编码在UTF-8码表中可以查到对应的字符,为“ππ”。
    因此,使用UTF-8码表对“ππ”进行解码,获取的字节也依然是“您好”的gbk编码后的字节。
    所以,不会出现“您好”发生的情况。

    实验:联通乱码问题。

    步骤:
    1. 新建一个1.txt文件。


    2. 输入联通,保存。


    3. 关闭,重新打开此文件,发现乱码。


    原因分析:
    “联通”经过gbk编码后成四个字节:11000001、10101010、11001101、10101000。
    正好符合UTF-8的编码规则。所以,记事本按照UTF-8进行了解码,从而出现了乱码现象。

    练习:
    在java中,字符串“abcd”与字符串“ab您好”的长度是一样,都是四个字符。
    但对应的字节数不同,一个汉字占两个字节。
    定义一个方法,按照最大的字节数来取子串。
    如:对于“ab你好”,如果取三个字节,那么子串就是ab与“你”字的半个。
    那么半个就要舍弃。如果取四个字节就是“ab你”,取五个字节还是“ab你”。

    代码:
  1. import java.io.IOException;

  2. public class Test{
  3.        public static void main(String[] args) throws IOException {
  4.             String str = "ab你好cd谢谢" ;

  5.              int len = str.getBytes("gbk" ).length;

  6.              for(int x = 1; x < len; x++){
  7.                   System.out.println( "截取的" + (x + 1) + "个节结果是:" + cutStringByByte(str,x+1));
  8.             }
  9.       }

  10.        public static String cutStringByByte(String str,int len) throws IOException {
  11.              byte[] buf = str.getBytes("gbk" );

  12.              int count = 0;
  13.              for(int x = len - 1; x >= 0; x--){
  14.                    //gbk编码的值两个字节值一般都为负,记录连续的负数个数,如果为奇数,则舍弃
  15.                    if(buf[x] < 0)
  16.                         count++;
  17.                    else
  18.                          break;
  19.             }

  20.              if(count % 2 == 0){
  21.                    return new String(buf,0,len,"gbk");
  22.             } else{
  23.                    return new String(buf,0,len-1,"gbk");
  24.             }
  25.       }
  26. }
复制代码
   运行结果:

    P.S.
    中文经过gbk编码后,也有两个字节不都为负数的情况,例如“琲”,字节值为-84、105。
    第一个字节值为负,第二个字节值为正。因此,上面的代码得出的结果依然正确。

    定义一个方法,按照最大的字节数来取子串,使用UTF-8编码的代码如下:

    代码:
  1. public class Test{
  2.        public static void main(String[] args) throws Exception {
  3.             String str = "ab你好cd谢谢" ;

  4.              int len = str.getBytes("utf-8" ).length;

  5.              for(int x = 1; x < len; x++){
  6.                   System.out.println( "截取的" + (x + 1) + "个节结果是:" + cutStringByByte(str,x+1));
  7.             }
  8.       }

  9.        public static String cutStringByByte(String str,int len) throws Exception {
  10.              byte[] buf = str.getBytes("utf-8" );

  11.              int count = 0;
  12.              for(int x = len - 1; x >= 0; x--){
  13.                    if(buf[x] < 0)
  14.                         count++;
  15.                    else
  16.                          break;
  17.             }

  18.              if(count % 3 == 0)
  19.                    return new String(buf,0,len,"utf-8");
  20.              else if (count % 3 == 1)
  21.                    return new String(buf,0,len-1,"utf-8");
  22.              else
  23.                    return new String(buf,0,len-2,"utf-8");
  24.       }
  25. }
复制代码
    运行结果:
   

~END~




46 个回复

倒序浏览
好快啊!顶一下!
回复 使用道具 举报
更新好快,支持
回复 使用道具 举报
给力!!!!!!!!
回复 使用道具 举报
更新很快!很赞!
回复 使用道具 举报
阳哥给力啊,这么快!
回复 使用道具 举报
阳哥辛苦了
回复 使用道具 举报
赞一个。
回复 使用道具 举报
赞!赞!赞!赞!赞!赞!
回复 使用道具 举报
更新很快!!辛苦了
回复 使用道具 举报
阳哥给力啊,这么快
回复 使用道具 举报
给力,更新的很快
回复 使用道具 举报
z赞  好详细
回复 使用道具 举报

好快啊!顶一下!
回复 使用道具 举报
赞一个!!!
回复 使用道具 举报
:):):):):):)
回复 使用道具 举报

好快啊!顶一下!
回复 使用道具 举报
今天的好难记啊。- -。回想一遍朦朦胧胧,貌似什么都没记住
回复 使用道具 举报
点个赞!!!
回复 使用道具 举报
阳哥,太牛,好赞 !!!!!!!!!!!!!!!!赞
回复 使用道具 举报
123下一页
您需要登录后才可以回帖 登录 | 加入黑马