IO 流有很多种,按照操作数据的不同,可以分为字节流和字符流,按照数组传输方向的不同又可分为输入流和输出流。字节流的输入输出流分别用 java.io.InputStream 和 java.io.OutputStream表示,字符流的输入输出流分别用 java.io.Reader 和 java.io.Writer 表示。
一、字节流
1、字节流的概念
在计算机中,无论文本、图片、音频还是视频,所有的文件都是以二进制(字节)的形式存在,IO 流中针对自己的输入和输出提供了一系列的流,统称为字节流。
字节流是程序中最常用的流,根据数据的传输方向可将其分为字节输入流和字节输出流,在 JDK 中提供了两个抽象类 InputStreanm 和 OutputStream ,他们是字节流的顶级父类,所有的字节输入流都继承自 InputStream,所有的字节输出流都是继承自 OutputStream。
InputStream 的常用方法:
OutputStream 的常用方法:
InputStream 和 OutputStream 这两个类虽然提供了一系列和读写数据有关的方法,但是这两个类是抽象类,不能被实例化,因此针对不同的功能,InputStream 和 OutputStream 提供了不同的子类。
2、字节流读取文件
针对文件的读写,JDK 专门提供了两个类,分别是 FileInputStream 和 FileInputStream。FileInputStream 是 InputStream 的子类,它是操作文件的字节输入流,专门用于读取文件中的数据。
[java] view plain copy
- import java.io.*;
- class StreamDemo1{
- public static void main(String[] args)throws Exception{
- FileInputStream in = new FileInputStream("test.txt");
- int b = 0;
- while(true){
- b = in.read();
- if(b == -1){
- break;
- }
- System.out.println(b);
- }
- in.close();
- }
- }
需要注意的是,在读取文件数据时,必须保证文件是存在并且可读的,否则会抛出文件找不到异常 FileNotFoundException。
与 FileInputStream 对应的是 FileOutputStream ,它是操作文件的字节输出流,专门用于把数据写入文件。
[java] view plain copy
- import java.io.*;
- class StreamDemo2{
- public static void main(String[] args)throws Exception{
- FileOutputStream out = new FileOutputStream("test.txt");
- String str = "传智播客";
- byte[] b = str.getBytes();
- for(int x=0;x<b.length;x++){
- out.write(b[x]);
- }
- out.close();
- }
- }
需要注意的是,如果是通过 FileOutputStream 向一个已经存在的文件中写入数据,那么该文件中的数据首先被清空,再写入新的数据。如果希望在已存在的文件内容之后追加新内容,则可以使用 FileOutputStream 的构造函数 FileOutputStream(String fileName,boolean append) 来创建文件输出流对象,并把 append 参数的值设置为 true。
[java] view plain copy
- import java.io.*;
- class StreamDemo3{
- public static void main(String[] args)throws Exception{
- FileOutputStream out = new FileOutputStream("test.txt",true);
- String str = "欢迎你";
- byte[] b = str.getBytes();
- for(int x=0;x<b.length;x++){
- out.write(b[x]);
- }
- out.close();
- }
- }
为了保证 IO 流的 close() 方法将无法得到执行,流对象所占用的系统资源将得不到释放,因此为了保证 IO 流的 close() 方法必须执行,通常将关闭流的操作写在 finally 代码块中。
[java] view plain copy
- FileInputStream in = null;
- FileOutputStream out = null;
- try{
- in = new FileInputStream("test.txt");
- out = new FileOutputStream("copy.txt");
- }catch(Exception e){
- throw new RuntimeException("读写失败");
- }finally{
- try{
- if(in != null)
- in.close();
- }catch(Exception e){
- e.printStackTrace();
- }
- try{
- if(out != null)
- out.close();
- }catch(Exception e){
- e.printStackTrace();
- }
- }
3、文件拷贝
[java] view plain copy
- class StreamDemo4{
- public static void main(String[] args){
- //创建一个字节输入流,读取c盘下的mp3文件
- InputStream in = new FileInputStream("c:\\新不了情.mp3");
- //创建一个字节输出流,将读取的数据写入到d盘中
- OutputStream out = new FileOutputStream("d:\\xinbuliaoqing.mp3");
- int len;
- while((len=in.read())!=-1){
- out.write(len);
- }
- in.close();
- out.close();
- }
- }
当通过流的方式拷贝文件时,为了提高效率可以定义一个字节数组作为缓冲区,一次性读取多个字节的数据,并保存在字节数组中,然后将字节数组中的数据一次性写入文件。
[java] view plain copy
- class StreamDemo4{
- public static void main(String[] args){
- //创建一个字节输入流,读取c盘下的mp3文件
- InputStream in = new FileInputStream("c:\\新不了情.mp3");
- //创建一个字节输出流,将读取的数据写入到d盘中
- OutputStream out = new FileOutputStream("d:\\xinbuliaoqing.mp3");
- byte[] buff = new byte[1024];
- int len;
- while((len=in.read(buff))!=-1){
- out.write(buff,0,len);
- }
- in.close();
- out.close();
- }
- }
4、装饰者模式
装饰者模式就是通过包装一个类,动态地为它增加一种设计模式。
[java] view plain copy
- //比如买了一辆车,香味新车装一个倒车雷达,这就相当于为这辆汽车增加了新的功能。
- class Car{
- private String carName;
- public Car(String carName){
- this.carName = carName;
- }
- public void show(){
- System.out.println("我是"+carName+",具备基本功能。");
- }
- }
- class RadarCar{
- public Car myCar;
- public RadarCar(Car myCar){
- this.myCar = myCar;
- }
- public void show(){
- myCar.show();
- System.out.println("具备雷达功能");
- }
- }
5、字节流缓冲区
在 IO 包中提供了两个带缓冲区的字节流,他们分别是 BufferedInputStream 和 BufferedOutputStream ,这两个流使用了装饰者设计模式。他们的构造方法分别接受 InputStream 和 OutputStream 类型的参数作为被包装的对象,在读写数据时提供缓冲功能。
[java] view plain copy
- class StreamDemo5{
- public static void main(String[] args){
- BufferedInputStream buffin = new BufferedInputStream(new FileInputStream("src.txt"));
- BufferedOutputStream buffout = new BufferedOutputStream(new FileOutputStream("dex.txt"));
- int len;
- while((len=buffin.read())!=-1){
- buffout.write(len);
- }
- buffin.close();
- buffout.close();
- }
- }
二、字符流
1、字符流简介
字符流也有两个抽象的顶级父类 Reader 和 Writer,其中 Reader 是字符输入流,用于从某个原设备中读取字符,Writer 是字符输出流,用于向某个目标设备写入字符。
2、字节流操作文件
经常需要对文本文件的内容进行读取,如果从文件中直接读取字符便可以使用输入流FileReader ,通过此流可以从关联的文件周总读取一个或一组字符。
[java] view plain copy
- //使用FileReader读取reader.txt文件中的内容
- class ReaderDemo{
- public static void main(String[] args){
- FileReader reader = new FileReader("reader.txt");
- int ch;
- while((ch=reader.read())!=-1){
- System.out.println((char)ch);
- }
- reader.close();
- }
- }
FileWriter 同 FileOutputStream 一样,如果指定的文件不存在,就会先创建文件,再写入数据,如果文件存在,则会首先清空文件中的内容荣,再进行写入。如果想要在我呢间末尾追加数据,同样需要调用重载的构造方法。
[java] view plain copy
- //使用FileWriter将字符串写入到writer.txt文件
- class WriterDemo{
- public static void main(String[] args){
- FileWriter writer = new FileWriter("writer.txt");
- String str = "传智播客";
- writer.write(str);
- writer.close();
- }
- }
字符流同样提供了带缓冲区的包装流,分别是 BufferedReader 和 BufferedWriter,需要注意的是,在 BufferedReader 中有一个重要的方法 readeLine() ,该方法用于一次读取一行文本。
[java] view plain copy
- class BufferedReaderDemo{
- public static void main(String[] args){
- BufferedReader buffin = new BufferedReader(new FileReader("src.txt"));
- BufferedWriter buffout = new BufferedWriter(new FileWriter("des.txt"));
- String str;
- while((str=buffin.read())!=null){
- buffout.write(str);
- buffout.newLine();
- }
- buufin.close();
- buffer.close();
- }
- }
3、LineNumberReader
Java 程序在编译或运行时期经常会出现一些错误,在错误中通常会报告出错的行号,为了方便查找错误,需要在代码中加入行号。
[java] view plain copy
- class LineNumberReaderDemo{
- public static void main(String[] args){
- FileReader fr = new FileReader("src.txt");
- FileWriter fw = new FileWriter("dex.txt");
- LineNumberReader lr = new LineNumberReader(fr);
- lr.setLineNumber(0);
- String line = null;
- while((line=lr.readLine())!=null){
- fw.write(lr.getLineNumber()+":"+line);
- }
- lr.close();
- fw.close();
- }
- }
4、转换流
有时候字节流和字符流之间也需要进行转换,在 JDK 中提供了两个类可以将字节流转换为字符流,它们分别是 InputStreamReader 和 OutputStreamWriter。
转换流也是一种包装流。
[java] view plain copy
- class InputStreamReaderDemo{
- public static void main(String[] args){
- BufferedReader br = new BufferedReader(new InputStreamReader(new FileInputStream("src.txt")));
- BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(new FileInputStream("dex.txt")));
- String line;
- while((line=br.readLine())!=null){
- bw.write(line);
- }
- br.close();
- bw.close();
- }
- }
三、其他 IO 流
1、ObjectInputStream 和 ObjectOutputStream
程序运行时会在内存中创建多个对象,然后程序结束后,这些对象便被当做垃圾回收了。如果希望永久保存这些对象,则可以将对象转为字节数组写入到硬盘中,这个过程被称为对象序列化。
JDK 提供了 ObjectOutputStream(对象输出流)来实现对象的实例化,当对象进行实例化时,必须保证该对象是想 Serializable 接口,否则程序会出现 NotSerializableException 异常。
[java] view plain copy
- class ObjectOutputStreamDemo{
- public static void main(String[] args){
- Person p = new Person("0001","zhangsan",26);
- FileOutputStream fos = new FileOutputStream("object.txt");
- ObjectOutputStream oos = new ObjectOutputStream(fos);
- oos.writeObject(p);
- }
- }
- class Person implements Serializable{
- private String id;
- private String name;
- private int age;
- public Person(String id,String name,int age){
- this.id = id;
- this.name = name;
- this.age = age;
- }
- public String getId(){
- return id;
- }
- public String getName(){
- return name;
- }
- public int getAge(){
- return age;
- }
- }
Person 对象被序列化后会生成二进制数据保存在“object.txt”文件中,通过这些二进制数据可以恢复序列化之前的 Java 对象,此过程称为反序列化。
JDK 提供了 ObjectInputStream(对象输入流),它可以实现对象的反序列化。
[java] view plain copy
- class ObjectInputStreamDemo{
- public static void main(String[] args){
- FileInputStream fis = new FileInputStream("object.txt");
- ObjectOutputStream ois = new ObjectOutputStream(fis);
- Person p = (Person)ois.readObject();
- System.out.println(p.getId()+","+p.getName()+","+p.getAge());
- }
- }
- class Person implements Serializable{
- private String id;
- private String name;
- private int age;
- public Person(String id,String name,int age){
- this.id = id;
- this.name = name;
- this.age = age;
- }
- public String getId(){
- return id;
- }
- public String getName(){
- return name;
- }
- public int getAge(){
- return age;
- }
- }
2、DataInputStream 和 DataOutputStream
有的时候并不需要存储整个对象的信息,而只需要存储对象的成员数据,这些成员数据的类型又都是基本数据类型,这时不必使用对象 Object 相关的流,可以使用 IO 包中提供的另外两个操作流:DataInputStream 和 DataOuputStream。
DataInputStream 和 DataOutputStream 是两个与平台无关的数据操作流,他们不仅提供了读写各种基本类型数据的方法,而且还提供了 readUTF() 和 writeUTF() 方法,DatinputStream 的 readUTF() 方法能够从输入流中读取采用 UTF-8 字符编码的字符串,DataOutputStream 的 writeUTF() 方法则可向输出流中写入采用 UTF-8 字符编码的字符串。
[java] view plain copy
class DataInputStreamDemo{
public static void main(String[] args){
BufferedOutputStream bos = new BufferedInputStream(new FileOutputStream("d:\data.txt"));
DataOutputStream dos = new DataOutputStream(bos);
dos.writeByte(12);
dos.writeChar("1");
dos.writeBoolean(true);
dos.writeUTF("你好");
dos.close();
BufferedInputStream bis = new BufferedInputStream(new FileInputStream("d:\data.txt"));
DataInputStream dis = new DataInputStream(bis);
System.out.println(dis.readByte());
System.out.println(dis.readChar());
System.out.println(dis.readBoolean());
System.out.println(dis.readUTF());
dis.close();
}
}
需要注意的是,只有读取数据的顺序与写入的顺序保持一致,才能保证最终数据的正确性。
3、PrintStream
PrintStream 提供了一系列用于打印数据的 print() 和 println() 方法,被称作打印流,可以实现将基本数据类型或引用数据类型的对象格式化成字符串后再输出。
[java] view plain copy
- class PrintStreamDemo{
- public static void main(String[] args){
- PrintStream ps = new PrintStream(new FileOutputStream("print.txt"),true);
- Student sru = new Student();
- ps.print("这是一个数字:");
- ps.println(19);
- ps.println(stu);
- }
- }
- class Studnet{
- public String toString(){
- return "我是一个学生";
- }
- }
4、标准输入输出流
在 System 类中定义了三个常量:in、out 和 err,它们被习惯性地称为标准输入输出流。其中,in 为 InputStream 类型,它是标准输入流,默认情况下用于读取键盘输入的数据;out 为 PrintStream 类型,它是标准输出流,默认将数据输出到命令行窗口;err 也是 PrintStream 类型,它是标准错误流,它和 out 一样也是将数据输出到控制台,不同的是 err 通常输出的是应用程序运行时的错误信息。
[java] view plain copy
- class InOutDemo{
- public static void main(String[] args){
- StringBuffer sb = new StringBuffer();
- int ch;
- while((ch=System.in.read())!=-1){
- if(ch=="\r" || ch=="\n")
- break;
- sb.append((char)ch);
- }
- System.out.println(sb);
- }
- }
由于程序向命令行窗口输出大量的数据,,可以将标准输出流重新定向到其他的输出设备。
[java] view plain copy
- class SetInOutDemo{
- public static void main(String[] args){
- System.setIn(new FileInputStream("src.txt"));
- System.setOut(new FileOutputStream("dex.txt"));
- BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
- String line;
- while((line=br.readLine())!=null){
- System.out.println(line);
- }
- }
- }
5、PipedInputStream 和 PipedOutputStream
多个线程之间也可以通过 IO 流实现数据的传输,为此 JDK 提供了一种管道流,管道流分为管道输入流(PipedInputStream)和管道输出流(PipedOutputStream),它是一种比较特殊的流,必须先建立连接才能进行彼此间通信。
[java] view plain copy
class PipedStreamDemo{
public static void main(String[] args)throws Exception{
final PipedInputStream pis = new PipedInputStream();
final PipedOutputStream pos = new PipedOutputStream();
pis.connect(pos);
new Thread(new Runnable(){
public void run(){
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
PrintStream ps = new PrintStream(pos);
while(true){
try{
System.out.print(Thread.currentThread().getName()+"要输入的内容:");
ps.println(br.readLine());
Thread.sleep(1000);
}catch(Exception e){
e.printStackTrace();
}
}
}
},"发送数据的线程").start();
new Thread(new Runnable(){
public void run(){
BufferedReader br = new BufferedReader(new InputStream(pis));
while(true){
try{
System.out.print(Thread.currentThread().getName()+"收到的内容:");
}catch(Exception e){
e.printStackTrace();
}
}
}
},"接收数据的线程").start();
}
}
6、ByteArrayInputStream 和 ByteArrayOutputStream
为了将文件临时存储到缓冲区,方便以后读取,ByteArrayOuputStream 类会在创建对象时就创建一个 byte 型数组的缓冲区,当想数组中写数据时,该对象会把所有的数据线写入缓冲区,最后一次性写入文件。
[java] view plain copy
- class ByteOutputStreamStreamDemo{
- public static void main(String[] args){
- FileInputStream fis = new FileInputStream("src.txt");
- ByteArrayOutputStream bos = new ByteArrayOutputStream();
- FileOutputStream fos = new FileOutputStream("target.txt");
- int b;
- while((b=fis.read())!=-1){
- bos.write(b);
- }
- in.xlose();
- bos.close();
- fos.write(bos.toByteArray());
- fos.close();
- }
- }
需要注意的是,如果读取的文件非常大,就不能使用这个类,否则会造成内存溢出。
与 ByteArrayOutputStream 类似,ByteArrayInputStream 是从缓冲区读取数据。
[java] view plain copy
- class ByteInputStreamStreamDemo{
- public static void main(String[] args){
- byte[] buff = new byte[]{97,98,99,100};
- ByteArrayInputStream bis = new ByteArrayInputStream(buff);
- int b;
- while((b=bis.read())!=-1){
- System.out.println((char)b);
- }
- }
- }
7、CharArrayReader 和 CharArrayWriter
要想将字符型数据临时存入缓冲区中,还可以使用 JDK 提供的 CharArrayReader 和 CharArrayWriter,CharArrayReader 是从字符数组中读取数据,CharArrayWriter 是在内存中创建一个字符数组缓冲区,他们的功能与 ByteArrayInputStream 和 ByteArrayOutputStream 类似,只不过操作的数据是字符。
[java] view plain copy
class ByteInputStreamStreamDemo{
public static void main(String[] args){
FileReader fr = new FileReader("src.txt");
CharArrayWriter cw = new CharArrayWriter();
int b;
while((b=fr.read())!=-1){
cw.write(b);
}
fr.close();
cw.close();
char[] c = cw.toCharArray();
CharArrayReader cr = new CharArrayReader(c);
int i;
while((i=cr.read())!=-1){
System.out.println((char)i);
}
}
}
8、SequenceInputStream
如果希望多个流处理数据,这时需要将这些流合并,SquenceInputStream 类可以讲几个输入流传亮在一起,合并为一个输入流。
- 合并两个流文件
[java] view plain copy
class SequenceInputStreamStreamDemo1{
public static void main(String[] args){
FileInputStream in1 = new FileInputStream("src1.txt");
FileInputStream in2 = new FileInputStream("src2.txt");
SequenceInputStream sis = new SequenceInputStream(in1,in2);
FileOutputStream out = new FileOutputStream("dex.txt");
int len;
byte[] buf = new byte[1024];
while((len=sis.read(buf))!=-1){
out.write(buf,0,len);
out.write("\r\n".getBytes());
}
sis.close();
out.close();
}
}
- 合并多个流文件
[java] view plain copy
class SequenceInputStreamStreamDemo2{
public static void main(String[] args){
FileInputStream in1 = new FileInputStream("src1.txt");
FileInputStream in2 = new FileInputStream("src2.txt");
FileInputStream in3 = new FileInputStream("src3.txt");
Vector vector = new Vector();
vector.addElement(in1);
vector.addElement(in2);
vector.addElement(in3);
Enumeration en = vector.elements();
SequenceInputStream sis = new SequenceInputStream(en);
FileOutputStream out = new FileOutputStream("dex.txt");
int len;
byte[] buf = new byte[1024];
while((len=sis.read(buf))!=-1){
out.write(buf,0,len);
}
sis.close();
out.close();
}
}
四、File 类
1、File 类的常用方法
File 类用于封装一个路径,这个路径可以是从系统盘符开始的绝对路径,也可以是当前目录而言的相对路径。File 类内部封装的路径可以指向一个文件,也可以指向一个目录,在 File 类中提供了针对这些文件或目录的一些常规操作。
File 类常用的构造方法:
File 类常用方法:
[java] view plain copy
- class FileDemo{
- public static void main(String[] args){
- File file = new File("example.txt");
- System.out.println("文件名称:"+file.getName());
- System.out.println("文件的相对路径:"+file.getPath());
- System.out.println("文件的绝对路径:"+file.getAbsolutePath());
- System.out.println("文件的父路径:"+file.getParent());
- System.out.println(file.canRead()?"文件可读":"文件不可读");
- System.out.println(file.canWrite()?"文件可写":"文件不可写");
- System.out.println(file.isFile()?"是一个文件":"不是一个文件");
- System.out.println(file.isDirectory()?"是一个目录":"不是一个目录");
- System.out.println(file.isAbsolute()?"是绝对路径":"不是绝对路径");
- System.out.println("最后修改的时间为:"+file.lastModified());
- System.out.println("文件大小为:"+file.length()+"KB");
- System.out.println("是否成功删除文件"+file.delete());
- }
- }
2、遍历目录下的文件
File 类中有一个 list() 方法,该方法用于遍历某个指定目录下的所有文件的名称。
[java] view plain copy
- class FileDemo{
- public static void main(String[] args){
- File file = new File("d:\\java\\bin");
- if(file.isDirectory()){
- String[] names = file.list();
- for(String name:names)
- System.out.println(name);
- }
- }
- }
有时候程序只需要指定类型的文件,针对这种需求,File 类提供了一个重载的 list(FilenameFilter filter) 方法。FilenameFilter 是一个接口,被称作文件过滤器,其中定义了一个抽象方法 accept(File dir,String name) ,在调用 list() 方法时,需要实现文件过滤器,在 accept() 方法中做出判断,从而获得指定类型的文件。
[java] view plain copy
- class FileDemo{
- public static void main(String[] args){
- File file = new File("d:\\java\\bin");
- FilenameFilter filter = new FilenameFileter(){
- public boolean accept(File dir,String name){
- File currFile = new File(dir,name);
- if(currFile.isFile() && name.endsWith(".java"))
- return true;
- else
- return false;
- }
- };
- if(file.exists()){
- String[] lists = file.list(filter);
- for(String name:lists){
- System.out.println(name);
- }
- }
- }
- }
在一个目录下,除了文件,还有子目录,如果想得到所有子目录下的 File 类型对象,需要使用 listFiles() 方法,该方法返回一个 File 对象数组。当对 数组中的元素进行遍历时,如果元素中还有子目录需要遍历,则需要使用递归。
[java] view plain copy
- class FileDemo{
- public static void main(String[] args){
- File file = new File("d:\\java\\bin");
- fileDir(file);
- }
- public static void fileDir(File file){
- File[] files = file.listFiles();
- for(File file:files){
- if(file.isDirectory()){
- fileDir(file);
- }
- System.out.println(file.getAbsolutePath());
- }
- }
- }
3、删除文件及目录
在操作文件时,经常需要删除一个目录下的某个我呢间或者删除整个目录,这时可以使用 File 类的 delete() 方法。
[java] view plain copy
- class FileDemo{
- public static void main(String[] args){
- File file = new File("d:\\java\\bin");
- if(file.exists())
- System.out.println(file.delete());
- }
- }
为什么会输出 false 呢? 那是因为 File 类的 delete() 方法只是删除一个指定的文件,加入 File 对象嗲表目录,并且目录下包含子目录或文件,则 File 类的 delete() 方法不允许对这个目录直接删除。
这种情况下,需要通过递归的方式将整个目录以及其中的文件全部删除。
[java] view plain copy
- import java.io.*;
- class FileDemo{
- public static void main(String[] args){
- File file = new File("f:\\java\\bin");
- deleteDir(file);
- }
- public static void deleteDir(file){
- if(dir.exists()){
- File[] files = dir.listFiles();
- for(File file:files){
- if(file.isDirectory()){
- deleteDir(file);
- }else{
- file.delete();
- }
- }
- dir.delete();
- }
- }
- }
需要注意的是,在 Java 中删除目录时从虚拟机直接删除而不走回收站,文件将无法恢复,因此在进行删除操作的时候需要格外小心。
五、RandomAccessFile
RandomAccessFile 不属于流类,但是具有读写文件数据的功能,可以随机从文件的任何位置开始执行读写数据的操作。
RandomAccessFile 的构造方法:
file或name指定关联的文件,mode参数指定访问文件的模式,mode有四个之,最常用的有两个,分别是r和rw,其中r表示以只读的方式打开文件,rw表示以读写的方式打开文件。
RandomAccessFile类针对文件的随机操作,提供了一系列用于定位文件位置的方法:
[java] view plain copy
- class RandomAccessFileDemo{
- public static void main(String[] args){
- RandomAccessFile raf = new RandomAccessFile("time.txt","rw");
- int times = 0;
- times = Integer.parseInt(raf.readLine());
- if(times<0){
- System.out.pritnln("您还可以试用"+ times-- +"次");
- raf.seek(0);
- raf.writeBytes(times+"");
- }else{
- System.out.println("软件试用已达到次数");
- }
- raf.close();
- }
- }
六、字符编码
1、常用字符集
字符码表是一种可以方便计算机识别的特殊字符集,它是将每一个字符和一个唯一的数字对应而形成的一张表。
2、字符编码和解码
在Java中经常会出现字符转换为字节或字节转换为字符的操作,这两种操作设计到两个概念,编码(Encode)和解码(Decode)。一般来说,把字符串转换成计算机识别的自己序列称为编码,而把字节序列转换成普通人能够看懂的字符串称为解码。
- 字节数组转字符串:String(byte[] bytes,String charsetName)
- 字符串转字节数组:String类的getBytes(String charsetName)方法
[java] view plain copy
class RandomAccessFileDemo{
public static void main(String[] args){
//解码
String str = "传智播客";
byte[] b1 = str.getBytes();
byte[] b2 = str.getBytes("GBK");
System.out.println(Arrays.toString(b1));
System.out.println(Arrays.toString(b2));
//编码
byte[] b3 = str.getBytes("UTF-8");
String result1 = new String(b1,"GBK");
System.out.println(result1);
String result2 = new String(b2,"GBK");
System.out.println(result2);
String result3 = new String(b3,"UTF-8");
System.out.println(result3);
String result4 = new String(b1,"ISO8859-1");
System.out.println(result4);
}
}
在上述的例子中尝试使用ISO8859-1码表对GBK编码的数组进行解码,出现了乱码问题,那么乱码问题该如何解决呢?
为了解决这种乱码问题,可以逆向思维,把乱码字符按照ISO8859-1进行编码,得到与第一次编码相同的字节,然后按照正确的码表GBK对字符进行解码。
[java] view plain copy
- class RandomAccessFileDemo{
- public static void main(String[] args){
- String str = "传智";
- byte[] b = str.getBytes("GBK");
- //通过ISO8859-1解码,得到的字符串时乱码
- String temp = new String(b,"ISO8859-1");
- System.out.println(temp);
- //试用ISO8859-1对乱码进行编码
- byte[] b1 = temp.getBytes("ISO8859-1");
- //使用GBK将得到的字节数组解码得到正确的字符串
- String result = new String(b1,"GBK");
- System.out.rpitnln(result);
- }
- }
3、字符传输
通过构造函数InputStreamReader(InputStream in,String charsetName)和OuputStreamWriter(OutputStream out,String charsetName)创建流对象时,可以对需要读写的数据指定编码格式。
[java] view plain copy
- class RandomAccessFileDemo{
- public static void main(String[] args){
- Reader reader = new InputStreamReader(new InputStream("d:\\1.txt"),"GBK");
- Writer writer = new OutputStreamWriter(new OutputStream("f:\\2.txt"),"ISO8859-1");
- char[] chs = new char[100];
- int len;
- len = reader.read(chs);
- String str = new String(chs,0.len);
- writer.write(str);
- reader.close();
- writer.close();
- }
- }