------- android培训、java培训、期待与您交流! ----------
功能流是指提供特殊功能的流对象。
合并流主要是利用SequenceInputStream类的方法,API如下:
SequenceInputStream | |
SequenceInputStream(Enumeration<InputStream> e) |
构造方法,创建SequeneceInputStream对象 e:输入流的一个枚举 |
SequenceInputStream(InputStream s1,InputStream s2) | 构造方法,创建SequeneceInputStream对象 s1:要读取的第一个输入流 s2:要读取的第二个输入流 |
int read(byte[] b) | 从流中读取b.length个字节 b:存储读取数据的缓冲区 返回:读入缓冲区的字节总数,如果到达末尾则返回-1 |
import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; import java.io.SequenceInputStream; import java.util.Enumeration; import java.util.Vector; public class SequenceDemo { public static void main(String[] args) { //定义变量 byte[] buf=new byte[1024]; int len=0; FileOutputStream fos=null; SequenceInputStream sis=null; Vector<FileInputStream> v=new Vector<FileInputStream>(); try { //创建3个FileInputStream对象放到Vector集合中 v.add(new FileInputStream("1.txt")); v.add(new FileInputStream("2.txt")); v.add(new FileInputStream("3.txt")); //创建合并输入流对象和输出流对象 Enumeration<FileInputStream> en=v.elements(); sis=new SequenceInputStream(en); fos=new FileOutputStream("4.txt"); //将输入流的内容写入到输出流 while((len=sis.read(buf))!=-1){ fos.write(buf,0,len); } } catch (IOException e) { throw new RuntimeException(e); } finally { try { if (sis != null) sis.close(); if (fos!=null) { fos.close(); } } catch (IOException e) { throw new RuntimeException(e); } } } }
import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; import java.io.SequenceInputStream; import java.util.ArrayList; import java.util.Enumeration; import java.util.Iterator; public class SplitDemo { public static void main(String[] args) { splitFile(); mergeFile(); } //把一个MP3文件切割成三份。 public static void splitFile(){ int len=0; int number=0; byte[] buf=new byte[1024*1024]; FileInputStream fis=null; FileOutputStream fos=null; try { fis=new FileInputStream("test.mp3"); while((len=fis.read(buf))!=-1){ fos=new FileOutputStream((++number)+".part"); fos.write(buf,0,len); fos.close(); } } catch (IOException e) { throw new RuntimeException(e); } finally { try { if (fis != null) fis.close(); if (fos != null) fos.close(); } catch (IOException e) { throw new RuntimeException(e); } } } //把切割后的碎片文件合并为一个文件 public static void mergeFile(){ int len=0; byte[] buf=new byte[1024]; ArrayList<FileInputStream> ins=new ArrayList<FileInputStream>(); FileOutputStream fos=null; SequenceInputStream sis=null; try { for (int i = 1; i <= 4; i++) { ins.add(new FileInputStream(i+".part")); } final Iterator<FileInputStream> it=ins.iterator(); Enumeration<FileInputStream> en=new Enumeration<FileInputStream>() { @Override public boolean hasMoreElements() { return it.hasNext(); } @Override public FileInputStream nextElement() { return it.next(); } }; sis=new SequenceInputStream(en); fos=new FileOutputStream("test1.mp3"); while((len=sis.read(buf))!=-1){ fos.write(buf,0,len); } } catch (IOException e) { throw new RuntimeException(e); } finally { try { if (sis != null) sis.close(); if (fos!=null) { fos.close(); } } catch (IOException e) { throw new RuntimeException(e); } } } }
对象的序列化主要用了下面的几个类、接口
class ObjectOutputStream | |
ObjectOutputStream(OutputStream out) | 构造函数,创建一个ObjectOutputStream对象。 out:要写入数据的输出流 IOException:发生IO错误时触发 NotSerializableException:要序列化的对象没实现Serializable接口,不是必须要处理的异常 |
writeObject(Object obj) | 将指定的对象写入ObjectOutputStream obj:要写入的对象 IOException:由底层OutputStream抛出的任何异常 |
class ObjectInputStream | |
ObjectInputStream(InputStream in) | 构造函数,创建一个ObjectInputStream对象。 in:要从中读取数据的输入流 IOException:发生IO错误时触发 |
Object readObject() | 从ObjectInputStream读取对象。 IOException:发生IO错误时触发 ClassNotFoundException:找不到序列化对象的类 |
interface Serializable | |
static final long serialVersionUID | 当一个类实现了Serializable接口时,在编译时会自动给该类添加一个serialVersionUID字段,并根据类里边的函数和字段的签名在运行时生成一个特定值。可以在实现类中覆盖此字段并将serialVersionUID写死。 |
import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; public class ObjectStreamDemo { public static void main(String[] args) { writeObject(); readObject(); } public static void readObject(){ ObjectInputStream ois=null; try { ois=new ObjectInputStream(new FileInputStream("person.object")); Person p1=(Person)ois.readObject(); System.out.println(p1); Person p2=(Person)ois.readObject(); System.out.println(p2); } catch (IOException e) { throw new RuntimeException(e); } catch (ClassNotFoundException e) { throw new RuntimeException(e); } finally { try { if (ois!= null) ois.close(); } catch (IOException e) { throw new RuntimeException(e); } } } public static void writeObject() { ObjectOutputStream oos = null; try { oos = new ObjectOutputStream(new FileOutputStream("person.object")); oos.writeObject(new Person("张三", 12)); oos.writeObject(new Person("李四", 11)); } catch (IOException e) { throw new RuntimeException(e); } finally { try { if (oos != null) oos.close(); } catch (IOException e) { throw new RuntimeException(e); } } } }注意,传进writeObject方法的对象必须实现Serializable接口,否则会抛异常。可以同时保存和读取多个对象,readObject()方法执行时,读取对象时一个接一个,既第一次执行读取第一个,第二次执行读取第二个。还要注意readObject()方法会抛出ClassNotFoundException异常,这个异常必须处理,否则无法编译。
管道流主要利用了PipedInputStream和PipedOutputStream两个类,这两个类一般用在多线程中,例子如下:
import java.io.IOException; import java.io.PipedInputStream; import java.io.PipedOutputStream; class Read implements Runnable { PipedInputStream in=null; Read(PipedInputStream in){ this.in=in; } @Override public void run() { byte[] buf=new byte[1024]; int len; try { System.out.println("开始读入"); Thread.sleep(2000); len = in.read(buf); System.out.println("读入完毕"); } catch (Exception e) { throw new RuntimeException(e); } finally { try { if (in != null) in.close(); } catch (IOException e) { throw new RuntimeException(e); } } String s=new String(buf,0,len); System.out.println(s); } } class Write implements Runnable{ PipedOutputStream out=null; Write(PipedOutputStream out){ this.out=out; } public void run(){ try { System.out.println("写出线程开始执行"); Thread.sleep(2000); out.write("success!".getBytes()); System.out.println("写出完毕"); } catch (Exception e) { throw new RuntimeException(e); } finally { try { if (out != null) out.close(); } catch (IOException e) { throw new RuntimeException(e); } } } } public class PipedStreamDemo { @SuppressWarnings("resource") public static void main(String[] args) { PipedInputStream in=new PipedInputStream(); PipedOutputStream out=new PipedOutputStream(); try { in.connect(out); Read r=new Read(in); Write w=new Write(out); new Thread(r).start(); new Thread(w).start(); } catch (IOException e) { throw new RuntimeException(e); } } }运行结果如下:
开始读入
写出线程开始执行
写出完毕
读入完毕
success!
或
写出线程开始执行
开始读入
写出完毕
读入完毕
success!
RandomAccessFile类是用于随机访问文件的类,意思是不是按顺序读写文件数据,而是可以从任意为值读写数据,因此用它就可以多线程读写。该类不算是IO体系中的子类,而是继承Object。但它属于java.io包中的成员,所以它的作用还是和IO有关。它内部封装了一个数组,通过指针对数组元素进行操作,从而读写数据。可以通过getFilePointer()方法获取指针位置,通过seek()方法改变指针位置。它内部封装了字节输入流和字节输出流。
RandomAccessFile | |
RandomAccessFile(String name,String mode) | 构造函数,name为文件路径,mode为模式,主要有"r"读,"rw"读写。如果文件不存在,当为“r”模式时,会出现异常,如果此时为“rw”模式,则会自动创建文件。 |
RandomAccessFile(File file,String mode) | 构造函数,file为文件对象,mode为模式,主要有"r"读,"rw"读写。如果文件不存在,当为“r”模式时,会出现异常,如果此时为“rw”模式,则会自动创建文件。 |
write(byte[] b) | 写入b.length个字节数据 |
writeInt(int n) | 按4个字节将n写入 |
read(byte[] b) | 读取b.length个字节数据 |
readInt() | 读取4个字节并解码为32位整型 |
seek(int n) | 从n指定字节位置处开始读写,由此可实现多线程读写。当写数据时,如果n指定位置已有数据,则会覆盖原来的数据。 |
skipBytes(int n) | 尝试跳过n个字节 |
import java.io.IOException; import java.io.RandomAccessFile; public class RandomAccessFileDemo { public static void main(String[] args) { writeFile(); readFile(); } public static void readFile(){ RandomAccessFile raf=null; try { raf=new RandomAccessFile("randomData.txt", "rw"); byte[] buf=new byte[4]; raf.read(buf); int age=raf.readInt(); System.out.println("name="+new String(buf)+" age="+age); raf.seek(8); byte[] buf1=new byte[4]; raf.read(buf1); int age1=raf.readInt(); System.out.println("name="+new String(buf1)+" age="+age1); } catch (IOException e) { throw new RuntimeException(e); } finally { try { if (raf != null) raf.close(); } catch (IOException e) { throw new RuntimeException(e); } } } public static void writeFile(){ RandomAccessFile raf=null; try { raf=new RandomAccessFile("randomData.txt", "rw"); raf.write("张三".getBytes()); raf.writeInt(97); raf.seek(8*2); raf.write("李四".getBytes()); raf.writeInt(99); raf.seek(8*0); raf.write("张一".getBytes()); raf.writeInt(96); } catch (IOException e) { throw new RuntimeException(e); } finally { try { if (raf != null) raf.close(); } catch (IOException e) { throw new RuntimeException(e); } } } }
程序输出结果:
name=张一 age=96
name=李四 age=99
之所以第一行name=张一而不是name=张三,就是因为seek()方法把指针重新设置会了起始位置,导致覆盖了原有数据。
import java.io.DataInputStream; import java.io.DataOutputStream; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; public class DataStreamDemo { public static void main(String[] args) { writeBasicData(); readBasicData(); } public static void readBasicData(){ DataInputStream dis=null; try { dis=new DataInputStream(new FileInputStream("DataStreamDemo.txt")); Boolean a= dis.readBoolean(); int b=dis.readInt(); String c=dis.readUTF(); System.out.println("a="+a); System.out.println("b="+b); System.out.println("c="+c); } catch (IOException e) { throw new RuntimeException(e); } finally { try { if (dis != null) dis.close(); } catch (IOException e) { throw new RuntimeException(e); } } } public static void writeBasicData(){ DataOutputStream dos=null; try { dos=new DataOutputStream(new FileOutputStream("DataStreamDemo.txt")); dos.writeBoolean(true); dos.writeInt(4); dos.writeUTF("你好"); } catch (IOException e) { throw new RuntimeException(e); } finally { try { if (dos != null) dos.close(); } catch (IOException e) { throw new RuntimeException(e); } } } }注意,如果用writeUTF()方法向文本文件中写入字符,用记事本打开时,前几个字节会乱码。
import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; public class MemoryDemo { public static void main(String[] args) { ByteArrayInputStream bais=new ByteArrayInputStream("agsrdadf".getBytes()); ByteArrayOutputStream bois=new ByteArrayOutputStream(); int temp=0; while((temp=bais.read())!=-1){ bois.write(temp); } System.out.println(bois.size()); System.out.println(bois.toString()); } }size()方法用于获取ByteArrayOutputStream中封装的字节数组的长度,toString()方法用于将字节数组转化为字符串。
还有一些流对象如CharArrayReader和CharArrayWriter、StringReader和StringWriter都是对内存中的数据进行读写,用法类似,不再举例。
字符编码(encoding),也可以叫做字符集(charset)。可以通过两个转换流对象来进行编码转换,即InputStreamReader和OutputStreamWriter。编码表即字符集与二进制数据的的对应关系。常见的编码表有如下几个:
名称 | 文字 | 描述 |
ASCII | 英文 | 1967年发布,用1个字节的7位表示1个字符。编码表见这里 |
ISO8859-1 | 拉丁文 | 1985年发布,用1个字节表示1个字符,主要用于西欧语言。兼容ASCII。 |
GB2312 | 简体中文 | 1980年发布,用2个字节表示1个字符。6763个汉字。 |
GBK | 简体中文 | 1995年发布,用2个字节表示1个字符 ,兼容GB2312。21003个汉字。 |
GB18030 | 简体中文 | 2000年发布,用1、2、4个字节表示1个字符,兼容ASCII、GBK、Unicode。70244个汉字。 |
Unicode | 世界主要文字 | 1991年发布,用2个字节表示1个字符。 |
UTF-8 | 世界主要文字 | 1996年发布,用1、2、3个字节表示1个字符。兼容ASCII。 |
下面的程序,用UTF-8编码把“你好”两个字写入到一个文本文件中,再用UTF-8编码把“你好”两个字读取到程序的字符串中,并输出。
import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStreamReader; import java.io.OutputStreamWriter; public class CharsetDemo { public static void main(String[] args) { writeData(); readData(); } public static void readData(){ InputStreamReader isr=null; try { 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); } catch (IOException e) { throw new RuntimeException(e); } finally { try { if (isr != null) isr.close(); } catch (IOException e) { throw new RuntimeException(e); } } } public static void writeData(){ OutputStreamWriter osw=null; try { osw=new OutputStreamWriter(new FileOutputStream("utf8.txt"),"UTF-8"); osw.write("你好"); } catch (IOException e) { throw new RuntimeException(e); } finally { try { if (osw != null) osw.close(); } catch (IOException e) { throw new RuntimeException(e); } } } }上述程序中,如果读取的时候,用GBK解码,则会出现乱码,既不能识别含义的字符串。原因是UTF-8编码时,会把“你好”两个字编码为六个字节,存到文本文件中,如果用GBK进行解码,则会把六个字节解码为3个字符,造成乱码。
编码:字符串变成字节数组、
String→byte[]: str.getBytes(charsetName);
解码:字节数组变成字符串
byte[]→String:new String(byte[],charsetName);
import java.io.UnsupportedEncodingException; import java.util.Arrays; public class EncodeAndDecodeDemo { public static void main(String[] args) { test5(); } public static void test1(){ String str="你好"; byte[] code=null; //默认字符编码是GBK code=encode(str, ""); System.out.println("\""+str+"\""+"经默认字符编码编码后为:"+Arrays.toString(code)); str=decode(code, ""); System.out.println(Arrays.toString(code)+"经默认字符编码解码后为:\""+str+"\""); } public static void test2(){ String str="你好"; byte[] code=null; //GBK编解码 code=encode(str,"GBK"); System.out.println("\""+str+"\""+"经GBK编码后为:"+Arrays.toString(code)); str=decode(code, "GBK"); System.out.println(Arrays.toString(code)+"经GBK解码后为:\""+str+"\""); } public static void test3(){ String str="你好"; byte[] code=null; //UTF-8编解码 code=encode(str,"UTF-8"); System.out.println("\""+str+"\""+"经GBK编码后为:"+Arrays.toString(code)); str=decode(code, "UTF-8"); System.out.println(Arrays.toString(code)+"经GBK解码后为:\""+str+"\""); } public static void test4(){ String str="你好"; byte[] code=null; //解错码时如何处理 code=encode(str,"GBK"); System.out.println("\""+str+"\""+"经GBK编码后为:"+Arrays.toString(code)); str=decode(code, "ISO8859-1"); System.out.println(Arrays.toString(code)+"经ISO8859-1解码后为:\""+str+"\""); code=encode(str,"ISO8859-1"); System.out.println("\""+str+"\""+"经ISO8859-1编码后为:"+Arrays.toString(code)); str=decode(code, "GBK"); System.out.println(Arrays.toString(code)+"经GBK解码后为:\""+str+"\""); } public static void test5(){ String str="你好"; byte[] code=null; //解错码时如何处理,此时行不通 code=encode(str,"GBK"); System.out.println("\""+str+"\""+"经GBK编码后为:"+Arrays.toString(code)); str=decode(code, "UTF-8"); System.out.println(Arrays.toString(code)+"经UTF-8解码后为:\""+str+"\""); code=encode(str,"UTF-8"); System.out.println("\""+str+"\""+"经UTF-8编码后为:"+Arrays.toString(code)); str=decode(code, "GBK"); System.out.println(Arrays.toString(code)+"经GBK解码后为:\""+str+"\""); } /** * 给字符串编码 * @param str 要编码的字符串 * @param charset 字符编码 * @return 编码后的字节数组的十进制表示形式 */ public static byte[] encode(String str,String charset){ byte[] code=null; try { if (charset.equals("")) { code=str.getBytes(); }else { code=str.getBytes(charset); } } catch (UnsupportedEncodingException e) { System.err.println("本程序不支持"+charset+"编码"); } return code; } /** * 给字符串解码 * @param code 要解码的字节数组 * @param charset 字符编码 * @return 解码后的字符串 */ public static String decode(byte[] code,String charset){ String str=""; try { if (charset.equals("")) { str=new String(code); }else{ str=new String(code,charset); } } catch (UnsupportedEncodingException e) { System.err.println("本程序不支持"+charset+"编码"); } return str; } }
问题:首先新建一个txt文件,在里边输入“联通”两个字,然后保存,关闭记事本,然后再用记事本打开此文件,会发现txt文件中的文字变为乱码。
原因:当用记事本默认的保存功能保存文字时(既用ANSI编码保存文本时),txt文本文件本身不会保存任何的字符编码信息,只会保存字符编码后的二进制数据,这样就会造成,记事本打开一个文本文件时不知道用什么字符集,所以就运行一个算法来猜字符集,这个算法首先考察前几个字节的数据是否符合UTF-8的特点,如果符合,就把整个文件以UTF-8读取进记事本显示出来,如果不符合就以ANSI编码读进记事本。而“联通”两个字的4字节二进制数据恰巧符合UTF-8的特点,所以此时记事本以UTF-8解码GBK编码的字符,所以出现乱码。解决此问题的方法是,用记事本保存文本时,选择另存为UTF-8,这样记事本就会在txt文件头部加一个标识,从而下一次用记事本打开此文件时,可以识别此文本文件的字符编码。EditPlus也存在此问题。写字板和记事本不存在此问题。
小知识,记事本中的ANSI编码指的是随操作系统语言变化的本地字符编码,比如如果当前操作系统的语言是简体中文,则ANSI编码就是GBK,如果当前操作系统的语言是英文,则ANSI编码就是ISO8859-1,如果当前操作系统的语言是繁体中文,则ANSI编码就是BIG5。
题目:有5个学生,每个学生有3门课的成绩,从键盘输入以上数据,输入格式:如:zhangsan,30,40,50,计算出总成绩,并把学生信息和计算出的总分数按从高到低的书序放在student.txt文件中。
面向对象分析:
1,需要一个类描述学生对象
2,需要一个工具类保存操作学生对象的方法
面向对象设计:
1,通过获取键盘录入一行数据,并将该行中的信息取出封装成学生对象
2,因为学生有很多,所以需要使用集合,因为要排序,所以用TreeSet集合
3,将集合信息写入一个文件
import java.io.BufferedReader; import java.io.BufferedWriter; import java.io.FileWriter; import java.io.IOException; import java.io.InputStreamReader; import java.util.Collections; import java.util.Comparator; import java.util.Set; import java.util.TreeSet; class Student implements Comparable<Student>{ String name; int math,chinese,english; int sum; public Student(String name,int math,int chinese,int english) { this.name=name; this.math=math; this.chinese=chinese; this.english=english; this.sum=math+chinese+english; } public String getName(){ return this.name; } public int getSum(){ return sum; } @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; } 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["+this.name+", "+this.math+", "+this.chinese+", "+this.english+"]"; } } class StudentInfoTool { public static Set<Student> getStudents(){ return getStudents(null); } public static Set<Student> getStudents(Comparator<Student> cmp){ BufferedReader br=null; Set<Student> stus=null; if (cmp==null) { stus=new TreeSet<Student>(); }else { stus=new TreeSet<Student>(cmp); } try { br=new BufferedReader(new InputStreamReader(System.in)); String str=null; while((str=br.readLine())!=null){ if ("over".equals(str)) { break; } String[] info=str.split(","); Student stu=new Student(info[0], Integer.parseInt(info[1]), Integer.parseInt(info[2]), Integer.parseInt(info[3])); stus.add(stu); } } catch (IOException e) { throw new RuntimeException(e); } finally { try { if (br != null) br.close(); } catch (IOException e) { throw new RuntimeException(e); } } return stus; } public static void write2File(Set<Student> stus){ BufferedWriter bw=null; try { bw=new BufferedWriter(new FileWriter("student.txt")); for (Student student : stus) { bw.write(student.toString()+"\t"); bw.write(student.getSum()+"\n"); } } catch (IOException e) { throw new RuntimeException(e); } finally { try { if (bw != null) bw.close(); } catch (IOException e) { throw new RuntimeException(e); } } } } public class ComprehensiveExercise { public static void main(String[] args) { Set<Student> stus=new TreeSet<Student>(); Comparator<Student> cmp=Collections.reverseOrder(); stus=StudentInfoTool.getStudents(cmp); StudentInfoTool.write2File(stus); } }