16.IO之其他流

第一  打印流

一、概述:

该流提供了打印方法,可以将各种数据类型的数据都原样打印

原理将97先变成字符保持原样将数据打印到目的地

1、字节打印流:PrintStream  构造函数可以接收的参数类型: 1)file对象。File 2)字符串路径。String 3)字节输出流。OutputStream

2、字符打印流:PrintWriter 构造函数可以接收的参数类型: 1)file对象。File 2)字符串路径。String 3)字节输出流。OutputStream 4)字符输出流,Writer。

  PrintStream out = new PrintStream("print.txt");

// int by = read();

// write(by);

// out.write(610);//只写最低8位,

// out.print(97);//将97先变成字符保持原样将数据打印到目的地。

  out.close();

从控制台写到控制台

private static void controlToC() throws IOException {		BufferedReader bf = new BufferedReader(new InputStreamReader(System.in));		PrintWriter pw = new PrintWriter(System.out);		String line = null;		while ((line = bf.readLine()) != null) {			if ("over".equals(line)) {				break;			}			pw.println(line.toUpperCase());			pw.flush();		}		pw.close();		bf.close();	}

从控制台写到文件中

public static void main(String[] args) throws IOException {	  BufferedReader bufr = new BufferedReader(new InputStreamReader(System.in));//特有构造器,turn就自动刷新,可是写到文件中不能自动刷新new FileWriter("out.txt"),所以写到一个输出流中,完成自动刷新	  PrintWriter out = new PrintWriter(new FileWriter("out.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();	 }	} 

获取系统信息,并保存:

步骤:

1)获取系统信息: Properties getProperties() 2)将信息输出到指定输出流中 void  list(PrintStream out) 3)将输出流中数据存入指定文件中 new PrintStream("systeminfo.txt")

public class SysInfo {      public static void main(String[] args) {          Properties pro = System.getProperties();            try {              pro.list(new PrintStream("sysinfo.txt"));// list方法将属性列表输出到指定的输出流。          } catch (FileNotFoundException e) {              e.printStackTrace();          }      }  }

第二  合并流(序列流)

一、概述:

SequenceInputStream 表示其他输入流的逻辑串联。它从输入流的有序集合开始,并从第一个输入流开始读取,直到到达文件末尾,接着从第二个输入流读取,依次类推,直到到达包含的最后一个输入流的文件末尾为止。

二、如何合并多个文件:

1、创建集合,并将流对象添加进集合或者SequenceInputStream(InputStream s1,InputStream s2)加入两个输入流

2、创建Enumeration对象,将集合元素加入。

3、创建SequenceInputStream对象,合并流对象

4、创建写入流对象,FileOutputStream,将数据写入流资源

5、定义数组,将读取流数据存入数组,并将数组中元素写入文件中。

public class SequenceInputStreamDemo {	  /*	   * 需求:将1.txt 2.txt 3.txt文件中的数据合并到一个文件中。	   */	Vector(枚举)中才有Enumeration//		 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(); //这样就获取了枚举了	  //枚举和迭代一样,枚举没过时,过时是编译器不认识了,不过  ArrayList有效率	  ArrayList<FileInputStream> al = new ArrayList<FileInputStream>();	  for(int x=1; x<=3; x++){	   al.add(new FileInputStream(x+".txt"));	  }	  Enumeration<FileInputStream> en = Collections.enumeration(al); 	  //有个返回枚举的方法,它的原理就是下边这个	  /* 	  final Iterator<FileInputStream> it = al.iterator(); //内部类访问局部变量用final	  Enumeration<FileInputStream> en = new Enumeration<FileInputStream>(){          //new了个  Enumeration,匿名内部类	//枚举接口的实现中利用迭代器,这样写比较麻烦,不懂,参见io流--51	   @Override	   public boolean hasMoreElements() {	    return it.hasNext();	   }	   @Override	   public FileInputStream nextElement() {	    return it.next();	   }	  };*/	  //合并	  SequenceInputStream sis = new SequenceInputStream(en);	  FileOutputStream fos = new FileOutputStream("1234.txt");	  byte[] buf = new byte[1024];	  int len = 0;	  while((len=sis.read(buf))!=-1){	   fos.write(buf,0,len);	  }	  fos.close();	  sis.close();	 } 	} 

切割文件重点,定义一个你需要把文件切成多大的数组,然后把数据读到数组中,读满后写出到文件中,这样就完成了切割:

 * 文件的切割和合并   */  public class SplitFile {      public static void main(String[] args) throws IOException {  //      splitFile();          sequence();      }            /*       * 切割文件       */      public static void splitFile() throws IOException      {          FileInputStream fis = new FileInputStream("0.jpg");          FileOutputStream fos = null;                    byte[] byf = new byte[1024*1024];//定义需要切割大小的空间                    int len = 0;          int count = 1;//为文件取名用的数字          while((len=fis.read(byf)) != -1){              fos = new FileOutputStream((count++) + ".patch");//创建指定的文件              fos.write(byf,0,len);//把读取到的数据写入指定的文件中              fos.close();//关闭资源          }          fis.close();//关闭资源      }            /*       * 合并文件       */      public static void sequence() throws IOException{          ArrayList<FileInputStream> list = new ArrayList<FileInputStream>();          list.add(new FileInputStream("1.patch"));          list.add(new FileInputStream("2.patch"));          list.add(new FileInputStream("3.patch"));                    //通过迭代器方法获取枚举          final Iterator<FileInputStream> it = list.iterator();//内部类使用的成员变量必须是final修饰的                    //通过内部类实现枚举          Enumeration<FileInputStream> en = new Enumeration<FileInputStream>() {                            @Override              public FileInputStream nextElement() {                  return it.next();              }                            @Override              public boolean hasMoreElements() {                  return it.hasNext();              }          };                    SequenceInputStream sequence = new SequenceInputStream(en);                    FileOutputStream fos = new FileOutputStream("10.jpg");                    byte[] byf = new byte[1024];          int len = 0;          while((len=sequence.read(byf)) != -1){              fos.write(byf,0,len);          }          sequence.close();          fos.close();      }  }

练习1:文件切割(按大小切)

public class SplitFileDemo {	private static final int SIZE = 1024 * 1024;	public static void main(String[] args) throws Exception {		File file = new File("c:\\aa.mp3");		splitFile_2(file);	}	// 修改后	private static void splitFile_2(File file) throws IOException {		// 用读取流关联源文件。		FileInputStream fis = new FileInputStream(file);		// 定义一个1M的缓冲区。		byte[] buf = new byte[SIZE];		// 创建目的。		FileOutputStream fos = null;		int len = 0;		int count = 1;		/*		 * 切割文件时,必须记录住被切割文件的名称,以及切割出来碎片文件的个数。 以方便于合并。		 * 这个信息为了进行描述,使用键值对的方式。用到了properties对象		 */		Properties prop = new Properties();		File dir = new File("c:\\partfiles");// 将碎片放到固定文件夹中,也可以不写		if (!dir.exists())			dir.mkdirs();		while ((len = fis.read(buf)) != -1) {			fos = new FileOutputStream(new File(dir, (count++) + ".part"));			// 不能写new			// FileOutputStream("1.txt"),问题1:第二次new把第一次new的覆盖了,所有1不能写死,还要合并。			// 问题2:扩展名不能写死,碎片文件不能阅读			fos.write(buf, 0, len);			fos.close();		}		// 将被切割文件的信息保存到prop集合中。		prop.setProperty("partcount", count + "");// 碎片文件的个数		prop.setProperty("filename", file.getName());// 文件的名字		fos = new FileOutputStream(new File(dir, count + ".properties"));		// 将prop集合中的数据存储到文件中。		prop.store(fos, "save file info");//		fos.close();		fis.close();	}	// 原始:	public static void splitFile(File file) throws IOException {		// 用读取流关联源文件。		FileInputStream fis = new FileInputStream(file);		// 定义一个1M的缓冲区。		byte[] buf = new byte[SIZE];		// 创建目的。		FileOutputStream fos = null;		int len = 0;		int count = 1;		File dir = new File("c:\\partfiles");		if (!dir.exists())			dir.mkdirs();		while ((len = fis.read(buf)) != -1) {			fos = new FileOutputStream(new File(dir, (count++) + ".part"));			fos.write(buf, 0, len);		}		fos.close();		fis.close();	}}

练习2:合并

public class MergeFile {	public static void main(String[] args) throws IOException {		File dir = new File("c:\\partfiles");		mergeFile_2(dir);	}	public static void mergeFile(File dir) throws IOException {		ArrayList<FileInputStream> al = new ArrayList<FileInputStream>();		for (int x = 1; x <= 3; x++) {			al.add(new FileInputStream(new File(dir, x + ".part")));		}		Enumeration<FileInputStream> en = Collections.enumeration(al);		SequenceInputStream sis = new SequenceInputStream(en);		FileOutputStream fos = new FileOutputStream(new File(dir, "1.bmp"));// 合并到当前目录下		byte[] buf = new byte[1024];		int len = 0;		while ((len = sis.read(buf)) != -1) {			fos.write(buf, 0, len);		}		fos.close();		sis.close();	}}

可是并知道有多少个碎片,也不知道碎片对应原来的名字是什么

修改后

public static void mergeFile_2(File dir) throws IOException {	  /*	   * 获取指定目录下的配置文件对象。	   */	  File[] files = dir.listFiles(new SuffixFilter(".properties"));//过滤器,过滤文件名	  if(files.length!=1)	   throw new RuntimeException(dir+",该目录下没有properties扩展名的文件或者不唯一");//自定义异常	  //记录配置文件对象。	  File confile = files[0];	 	  //获取该文件中的信息================================================。	  Properties prop = new Properties();	  FileInputStream fis = new FileInputStream(confile);	  prop.load(fis);//流中信息加载进来	  String filename = prop.getProperty("filename");	  int count = Integer.parseInt(prop.getProperty("partcount"));	 	  //获取该目录下的所有碎片文件。 ==============================================	  File[] partFiles = dir.listFiles(new SuffixFilter(".part"));	  if(partFiles.length!=(count-1)){	   throw new RuntimeException(" 碎片文件不符合要求,个数不对!应该"+count+"个");//如果少了一个	  }	   //将碎片文件和流对象关联 并存储到集合中。	  ArrayList<FileInputStream> al = new ArrayList<FileInputStream>();	  for(int x=0; x<partFiles.length; x++){	   al.add(new FileInputStream(partFiles[x]));	  }	  	  //将多个流合并成一个序列流。	  Enumeration<FileInputStream> en = Collections.enumeration(al);	  SequenceInputStream sis = new SequenceInputStream(en);	  FileOutputStream fos = new FileOutputStream(new File(dir,filename));	  byte[] buf = new byte[1024];	  int len = 0;	  while((len=sis.read(buf))!=-1){	   fos.write(buf,0,len);	  }	  fos.close();	  sis.close();	} }

第三  Objectstream(对象流)

一、概述:

ObjectStream是可以操作对象的流。它的写方法是ObjectOutputStream,读方法是ObjectInputStream。它主要操作的是对象,而对象中也能封装数据,所以它也具备操作基本数据类型的方法。被它操作的对象必须是实现了序列化的对象也就是Serializable接口,但是输入流还多支持一种Externalizable 接口的对象。持久化

实例:

 * 对对象进行读写操作  public class ObjectstreamDemo {      public static void main(String[] args) throws Exception {  //      writeObj();          readObj();      }            /*       * 写对象       */      public static void writeObj() throws Exception{          ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("obj.object"));  //仅仅是把对内存的对象存在硬盘上,下次拿来用,不需要用记事本解析        oos.writeObject(new Person("zhangsan", 23, "ASC"));          oos.close();      }            /*       * 读取对象  ,读出来不是一个组合的对象,读出是数据.注意:没class文件,取不出来     */      public static void readObj() throws Exception{          ObjectInputStream ois = new ObjectInputStream(new FileInputStream("obj.object"));       //  他是person的属性        Person p = (Person) ois.readObject();          System.out.println(p);          ois.close();      }  }

用作被操作的类:

 * 用作对象的持久化存储,也就是用流存放到硬盘上    *

 * Serializable:用于给被序列化的类加入ID号。

 * 用于判断类和对象是否是同一个版本。

 * 静态不能被序列化    * transient关键字修饰的不能被序列化  

public class Person implements Serializable///*标记接口*/实现这个接口,让该类被序列化  {      //给类指定一个序列化标识,方便序列化,不定义会自动生成一个,如果不定义序列化,改变属性就会读不出来    private static final long serialVersionUID = 42L;            private String name;  //private transient String name;    private int age;      private static String country = "cn";            Person(String name, int age, String country){          this.name = name;          this.age = age;          Person.country = country;      }            public String toString(){          return name+":"+age+":"+country;      }  }

第四  RandomAccessFile

一、概述:

1、RandomAccessFile称之为随机访问文件的类,自身具备读写方法。

2、该类不算是IO体系中的子类,而是直接继承Object,但是它是IO包成员,因为它具备读写功能,内部封装了一个数组,且通过指针对数组的元素进行操作,同时可通过seek改变指针的位置。

3、可以完成读写的原理:内部封装了字节输入流

4、构造函数:RandomAccessFile(File file,String mode),可已从它的构造函数中看出,该类只能操作文件(也有字符串),而且操作文件还有模式。

模式传入值:”r“:以只读方式打开;”rw“:打开以便读写

如果模式为只读,则不会创建文件,会去读一个已存在的文件,若文件不存在,则会出现异常,如果模式为rw,且该对象的构造函数要操作的文件不存在,会自动创建,如果存在,则不会覆盖,也可通过seek方法修改。

二、特有方法:

1、seek(int n):设置指针,可以将指针设置到前面或后面

2、skipBytes(int n):跳过指定字节数,不可往前跳

实例:

public class RandomAccessFileDemo {      public static void main(String[] args) throws IOException {          writeFile();  //      readFile();      }            public static void writeFile() throws IOException{          RandomAccessFile raf = new RandomAccessFile("random.txt","rw");          //如果文件不存在,则创建,如果文件存在,不创建,把下边的代码注释掉,再读这些东西还在        raf.write("张三".getBytes());  //内部封装的是一个字节数组,转成字节数组        raf.writeInt(97);//write方法只读取最低8位,用writeint          raf.write("李四".getBytes());  //不是覆盖,是接着写        raf.writeInt(98);   //不是覆盖,是接着写                  //********往指定位置上存储数据,也可以修改数据          raf.seek(8*4);          raf.write("王五".getBytes());          raf.writeInt(103);                    raf.close();      }            public static void readFile() throws IOException{          RandomAccessFile raf = new RandomAccessFile("random.txt","r");                    //调整对象中的指针  //      raf.seek(8*1);//根据数组定,数组大小*跳几个                    //跳过指定的字节数,只能向前不能向后          raf.skipBytes(8);                    byte[] buf = new byte[4];           raf.read(buf);                    String name = new String(buf);          int age = raf.readInt();          System.out.println(name + " : " + age);          raf.close();      }  }

第五  管道流

一、概述:

可以将管道输出流连接到管道输入流来创建通信管道。管道输出流是管道的发送端。通常,数据由某个线程写入PipedOutputStream 对象,并由其他线程从连接的PipedInputStream 读取。不建议对这两个对象尝试使用单个线程,因为这样可能会造成该线程死锁。如果某个线程正从连接的管道输入流中读取数据字节,但该线程不再处于活动状态,则该管道被视为处于毁坏状态(read是阻塞状态,他会一直等)

二、使用步骤:

1、要先创建一个读和写的两个类,实现Runnable接口,因为是两个不同的线程,覆盖run方法,注意,需要在内部处理异常,因为重写run方法

2、创建两个管道流,并用connect()方法将两个流连接

3、创建读写对象,并传入两个线程内,并start执行

实例:

 / * 管道流   * 用多线操作,单线程容易造成死锁   */  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();      }    }    class Write implements Runnable {      private PipedOutputStream out;        Write(PipedOutputStream out) {          this.out = out;      }        @Override      public void run() {          try {              Thread.sleep(4000);              out.write("piped stream come".getBytes());              out.close();          } catch (IOException e) {              e.printStackTrace();          } catch (InterruptedException e) {              e.printStackTrace();          }      }    }    class Read implements Runnable {      private PipedInputStream in;        Read(PipedInputStream in) {          this.in = in;      }        @Override      public void run() {          byte[] byf = new byte[1024];          int len = 0;            try {              while ((len = in.read(byf)) != -1) {                  String str = new String(byf, 0, len);                  System.out.println(str);              }          } catch (IOException e) {              e.printStackTrace();          }      }  }  

第六  DataStream(操作基本类型的流)

一、概述:

DataStream是可以用于操作基本数据类型的数据的流对象:DataInputStream与DataOutputStream,它主要的特点就是操作基本数据类型。

实例:

 * DataInputStream与DataOutputStream   *    * 可以用于操作基本数据类型的数据的流对象。   */  public class DataStreamDemo {      public static void main(String[] args) throws IOException {  //      write();  //      read();  //      writeUTF();          readUTF();                    //一般加入编码的方法  //      OutputStreamWriter osw = new OutputStreamWriter(new FileOutputStream("utf.txt"),"utf-8");  //      osw.write("你好");  //      osw.close();      }            public static void write() throws IOException{          DataOutputStream dos = new DataOutputStream(new FileOutputStream("data.txt"));                    dos.writeInt(236);          dos.writeBoolean(false);          dos.writeDouble(2323.02154);                    dos.close();      }            public static void read() throws IOException{          DataInputStream dis = new DataInputStream(new FileInputStream("data.txt"));                    int i = dis.readInt();          boolean b = dis.readBoolean();          double d = dis.readDouble();                    System.out.println("int: " + i);          System.out.println("boolean: " + b);          System.out.println("double: " + d);                    dis.close();      }            //加入了改编后的UTF-8编码,所以只有它自己能读      public static void writeUTF() throws IOException{          DataOutputStream dos = new DataOutputStream(new FileOutputStream("data.txt"));                    dos.writeUTF("你家婆");                    dos.close();                }            public static void readUTF() throws IOException{          DataInputStream dis = new DataInputStream(new FileInputStream("utf.txt"));                    String s = dis.readUTF();          System.out.println(s);                    dis.close();                }        } 

第七  ByteArraytStream(操作数组的流)

一、概述:

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

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

参数是字节数组,访问网络写的是字节,需要new string(bos.tobyteArray)

注:因为这两个流对象都操作的数组,并没有使用系统资源。所以,不用进行close关闭,而且关闭也是无效的。

源设备,   键盘 System.in,硬盘 FileStream,内存 ArrayStream   目的设备:    控制台 System.out,硬盘FileStream,内存 ArrayStream。

public class ByteArrayStreamDemo {      public static void main(String[] args) {          //数据源          ByteArrayInputStream bis = new ByteArrayInputStream("defef".getBytes());                    //目的地          ByteArrayOutputStream bos = new ByteArrayOutputStream();                    int ch = 0;                    while((ch=bis.read()) != -1){              bos.write(ch);          }                    System.out.println(bos.size());          System.out.println(bos.toString());                    try {              //将此 byte 数组输出流的全部内容写入到指定的输出流参数中              bos.writeTo(new FileOutputStream("b.txt"));          } catch (FileNotFoundException e) {              e.printStackTrace();          } catch (IOException e) {              e.printStackTrace();          }      }  }

第八  字符编码

一、‘常见的编码表:

1、ASCII:美国标准信息交换码表。用一个字节的7位表示

2、IOS8859-1:拉丁码表;欧洲码表。用一个字节的8位表示

3、GB2312:中国的中文编码表

4、GBK:中国的中文编码表升级,融合了更多的中文文字字符。打头的是两个高位为1的两个字节编码。为负数

5、Unicode:国际标准码,融合了多种文字

6、UTF-8:升级版国际码表,是可变长度的码表。最多用三个字节表示一个字符的编码表,包括:一位、两位、三位表示的字符

      UTF-8有自己的字节码:

一个字节:0开头

两个字节:字节一  ---> 110     位数:10 ~ 6

                    字节二  --->  10      位数:5 ~ 0

三个字节:字节一  ---> 1110     位数:15 ~ 12

                    字节二  --->  10      位数:11 ~ 6

                    字节三 --->  10       位数:5 ~ 0

二、编码和解码:

1、编码:字符串变成字节数组

      解码:字节数组变成字符串

2、转换:

1)默认字符集:

      String  --->  byte[]   :srt.getBytes()

      byte[]   --->  String  :new String(byte[])

2)指定字符集:

      String  --->  byte[]   :srt.getBytes(charsetName)

      byte[]   --->  String  :new String(byte[],charsetName)

三、对于编码和解码的字符集转换

1、如果编码失败,解码就没意义了。

2、如果编码成功,解码出来的是乱码,,则需对乱码通过再次编码(用解错码的编码表),然后再通过正确的编码表解码。针对于IOS8859-1是通用的。

3、如果用的是GBK编码,UTF-8解码,那么再通过2的方式,就不能成功了,因为UTF-8也支持中文,在UTF-8解的时候,会将对应的字节数改变,所以不会成功。

特别注意:对于中文的”联通“,这两个字比较特别,它的二进制位正好是和在UTF-8中两个字节打头的相同,可以找到对应的符号,但不再是”联通“了。

但是只要他前面还有其他汉字就不会被被UTF-8解码了,也就不会出现乱码了。

  1. * 编码:字符串变成字节数组。 * 解码:字节数组变成字符串。 * String-->byte[]; str.getBytes(charsetName); * byte[] -->String: new String(byte[],charsetName); */ public class EncodeDemo { public static void main(String[] args) { method(); } public static void show() { String s = "你好"; try { byte[] b = s.getBytes("GBK"); System.out.println(Arrays.toString(b)); // printBytes(buf); String str = new String(b, "iso8859-1");// 编码正确,解码错误 System.out.println(str); // 对str进行iso8859-1编码。以这样的方式解决乱码 byte[] b1 = str.getBytes("iso8859-1"); System.out.println(Arrays.toString(b1)); String str1 = new String(b1, "GBK"); System.out.println(str1); } catch (UnsupportedEncodingException e) { e.printStackTrace(); } }
  1. /* * 联通 */ public static void method() { String s = "联通"; try { byte[] bs = s.getBytes("GBK"); for (byte b : bs) { // Integer.toBinaryString将其转换为二进制,&255取出前面多余的1,保留最低8位 // 联通的二进制是110...10...110...10...这个刚好符合utf-8的读取两个字节的编码形式, // 所以当度存在会被默认为utf-8编码来解读,从而造成乱码 /* * '\u0001' 到 '\u007F' 范围内的所有字符都是用单个字节表示的: * * 位值 字节一 0 位 6-0 * * * null 字符 '\u0000' 以及从 '\u0080' 到 '\u07FF' 的范围内的字符用两个字节表示: * * 位值 字节一 1 1 0 位 10-6 * * 字节二 1 0 位 5-0 * * '\u0800' 到 '\uFFFF' 范围内的 char 值用三个字节表示: 字节一 1 1 1 0 位 * 15-12 * * 字节二 1 0 位 11-6 * * 字节三 1 0 位 5-0 */ System.out.println(Integer.toBinaryString(b & 255)); } } catch (UnsupportedEncodingException e) { e.printStackTrace(); } } }

练习:

public class Test {	 public static void main(String[] args) throws IOException {	  String str = "ab你好cd谢谢";//		 str = "ab琲琲cd琲琲";//		 int len = str.getBytes("gbk").length;//		 for(int x=0; x<len; x++){//		 System.out.println("截取"+(x+1)+"个字节结果是:"+cutStringByByte(str, x+1));//		 }	  int len = str.getBytes("utf-8").length;	  for(int x=0; x<len; x++){	   System.out.println("截取"+(x+1)+"个字节结果是:"+cutStringByU8Byte(str, x+1));	  }//		 String str = "琲"; //		 byte[] buf = str.getBytes("gbk");//		 for(byte b : buf){//		 System.out.println(b);//-84  105//		 }	 }

    在java中,字符串“abcd”与字符串“ab你好”的长度是一样,都是四个字符。

  但对应的字节数不同,一个汉字占两个字节。

  定义一个方法,按照最大的字节数来取子串。

  如:对于“ab你好”,如果取三个字节,那么子串就是ab与“你”字的半个,

  那么半个就要舍弃。如果去四个字节就是“ab你”,取五个字节还是“ab你”.

public static String cutStringByU8Byte(String str, int len) throws IOException {	  byte[] buf = str.getBytes("utf-8");	  int count = 0;	  for(int x=len-1; x>=0; x--){	   if(buf[x]<0)	    count++;	   else	    break;	  }	  if(count%3==0)	   return new String(buf,0,len,"utf-8");	  else if(count%3==1)	   return new String(buf,0,len-1,"utf-8");	  else	   return new String(buf,0,len-2,"utf-8");	 }	 public static String cutStringByByte(String str,int len) throws IOException{	  byte[] buf = str.getBytes("gbk");	  int count = 0;	  for(int x=len-1; x>=0; x--){	   if(buf[x]<0)	    count++;	   else	    break;	  }	  if(count%2==0)	   return new String(buf,0,len,"gbk");	  else	   return new String(buf,0,len-1,"gbk");	 	 } }}	

练习

五个学生,每个学生有3门课程的成绩,从键盘输入以上数据(姓名,三门课成绩),

输入格式:如:zahngsan,30,40,60计算出总成绩,并把学生的信息和计算出的总分数高低按顺序存放在磁盘文件stud.txt中

步骤: 1、描述学生对象 2、定义一个可操作学生对象的工具类 思路: 1、通过获取键盘录入一行的数据,并将该行数据的信息取出,封装成学生对象 2、因为学生对象很多,则需要存储,使用集合,因为要对学生总分排序 所以可以使用TreeSet 3、将集合中的信息写入到一个文件中

public class StudentInfoTest {      public static void main(String[] args) {          try {              Comparator<StudentInfo> cmp = Collections.reverseOrder();// 强行反正比较顺序              // Set<StudentInfo> stus = StudentInfoTool.getStudents();//默认比较              Set<StudentInfo> stus = StudentInfoTool.getStudents(cmp);// 自定义比较器              StudentInfoTool.write2File(stus);//调用写方法,将数据写文件中去          } catch (IOException e) {              e.printStackTrace();          }      }  }    class StudentInfo implements Comparable<StudentInfo> {        private String name;// 姓名      private int ma, cn, en;// 数学、语文、英语成绩      private int sum;// 总分        //初始化就需要具备姓名,数学、语文、英语成绩      StudentInfo(String name, int ma, int cn, int en) {          this.name = name;          this.ma = ma;          this.cn = cn;          this.en = en;            sum = ma + cn + en;//计算出总成绩      }        public String getName() {          return name;      }        public void setName(String name) {          this.name = name;      }        public int getMa() {          return ma;      }        public void setMa(int ma) {          this.ma = ma;      }        public int getCn() {          return cn;      }        public void setCn(int cn) {          this.cn = cn;      }        public int getEn() {          return en;      }        public void setEn(int en) {          this.en = en;      }        public int getSum() {          return sum;      }        public void setSum(int sum) {          this.sum = sum;      }        @Override      public int compareTo(StudentInfo o) {//建立自己的比较方法          int num = new Integer(this.sum).compareTo(new Integer(o.sum));//比较分数          if (num == 0) {              return this.name.compareTo(o.name);//主条件相同判定次要条件          }          return num;//返回比较的值,大于正数,相同0,小于负数      }        @Override      public int hashCode() {//重写hashcode方法是因为数据有可能存到hashtable中          return name.hashCode() + sum * 23;//尽可能保证hashcode值不相同,减少比较次数      }        @Override      public boolean equals(Object obj) {//建立自己独有的判断相同方式          if (!(obj instanceof StudentInfo)) {              throw new ClassCastException("类型不匹配");          }            StudentInfo si = (StudentInfo) obj;          return this.name.equals(si.name) && this.sum == si.sum;//姓名和总分相同视为同一人      }        @Override      public String toString() {//建立自己的toString方法          return "studentinfo[" + name + ", " + ma + ", " + cn + ", " + en + "]";      }  }    class StudentInfoTool {      //定义默认比较顺序      public static Set<StudentInfo> getStudents() throws IOException {            return getStudents(null);      }        //定义自定义的比较顺序      public static Set<StudentInfo> getStudents(Comparator<StudentInfo> cmp)              throws IOException {          BufferedReader bfr = new BufferedReader(                  new InputStreamReader(System.in));            Set<StudentInfo> stus = null;          if (cmp == null) {              stus = new TreeSet<StudentInfo>();          } else {              stus = new TreeSet<StudentInfo>(cmp);          }          String line = null;          while ((line = bfr.readLine()) != null) {              if ("over".equals(line)) {                  break;              }              String[] info = line.split(",");//将数据以“,”切开,形成新的数据                StudentInfo si = new StudentInfo(info[0],                      Integer.parseInt(info[1]), Integer.parseInt(info[2]),                      Integer.parseInt(info[3]));                stus.add(si);          }            bfr.close();          return stus;      }        public static void write2File(Set<StudentInfo> stus) throws IOException {          BufferedWriter bfw = new BufferedWriter(new FileWriter("stuinfo.txt"));            for (StudentInfo si : stus) {              bfw.write(si.toString() + "\t");              bfw.write(si.getSum() + "");              bfw.newLine();              bfw.flush();          }            bfw.close();      }  }  

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏积累沉淀

Java IO结构各种流详解

花了两天时间研究了一下Java IO的流,对于各种流,加深了一下理解 首先看我做的思维导图 ? 文件流 public class FileIO { ...

6369
来自专栏Coding迪斯尼

自制Monkey语言编译器:解释执行哈希表对象

我们在上节完成了对哈希表对象的解析,这一节我们给编译器添加执行哈希表对象的功能,完成本节代码后,编译器能执行以下代码:

862
来自专栏刘望舒

Kotlin下的5种单例模式

4521
来自专栏IT可乐

Java IO详解(四)------字符输入输出流

 File 类的介绍:https://cloud.tencent.com/developer/article/1012532 Java IO 流的分类介绍:ht...

30210
来自专栏Java3y

JSP第四篇【EL表达式介绍、获取各类数据、11个内置对象、执行运算、回显数据、自定义函数、fn方法库】

什么是EL表达式? 表达式语言(Expression Language,EL),EL表达式是用"${}"括起来的脚本,用来更方便的读取对象! EL表达式主要用来...

4837
来自专栏Java帮帮-微信公众号-技术文章全总结

Java基础-20(01)总结,递归,IO流

1:递归(理解) (1)方法定义中调用方法本身的现象 举例:老和尚给小和尚讲故事,我们学编程 (2)递归的注意事项; A:要有出口,否则就是死递归 B...

3569
来自专栏pangguoming

Java编程最差实践(常见编程错误典范)

转载自  http://macrochen.iteye.com/blog/1393502

832
来自专栏伪君子的梦呓

华氏温度转摄氏温度~ C++ 做法

题目链接:http://www.dotcpp.com/oj/problem1005.html

1243
来自专栏Jimoer

在Java的反射中,Class.forName和ClassLoader的区别

最近在面试过程中有被问到,在Java反射中Class.forName()加载类和使用ClassLoader加载类的区别。当时没有想出来后来自己研究了一下就写下来...

1512
来自专栏xingoo, 一个梦想做发明家的程序员

20120918 -- 可计算性

程序特性规定: 1 程序开始执行时,自认为一切变元的值为0 2 转向无定义符号,执行程序的最后一条指令 时 自动认为停机 y=x+3 y=x+1 y=x+1 ...

1849

扫码关注云+社区

领取腾讯云代金券