Java学习笔记(7)——输入输出

1、File

不同的操作系统对于档案系统路径的设定各有差别,例如在Windows中,一个路径的表示法可能是:

"c:\\Windows\\Fonts\\"

而在Linux下的路径设定可能是:

"/home/justin/"

Windows的路径指定是使用UNC(Universal Naming Convention)路径名,以\\开始表示磁盘根目录,如果没有以\\开始表示相对路径,c是可选的磁盘指定,后面跟随着 : 字符。而UNIX-Like系统的路径指定以 / 开始表示绝对路径,不以 / 开始表示相对路径。

因而在程序中设定路径时会有系统相依性的问题,File类别提供一个抽象的、与系统独立的路径表示,您给它一个路径字符串,它会将它转换为与系统无关的抽象路径表示,这个路径可以指向一个档案、目录或是URI,您可以用以下四种方式来建构File的实例:

File(File parent, String child)
File(String pathname)
File(String parent, String child)
File(URI uri)

一个File的实例被建立时,它就不能再被改变内容;File类别除了用来表示一个档案或目录的抽象表示之外,它还提供了不少相关操作方法,您可以用它来对档案系统作一些查询与设定的动作。

来看个简单的程序:

FileDemo.java
package onlyfun.caterpillar;
import java.io.*;
import java.util.*;
public class FileDemo {public static void main(String[] args) {try {File file = new File(args[0]);if(file.isFile()) { // 是否为档案System.out.println(args[0] + " 档案");System.out.print(file.canRead() ? "可读 " : "不可读 ");System.out.print(file.canWrite() ? "可写 " : "不可写 ");System.out.println(file.length() + "字节");}else {// 列出所有的档案及目录File[] files = file.listFiles();ArrayList fileList =new ArrayList();for(int i = 0; i < files.length; i++) {// 先列出目录if(files[i].isDirectory()) { //是否为目录// 取得路径名System.out.println("[" +files[i].getPath() + "]");}else {// 档案先存入fileList,待会再列出fileList.add(files[i]);}}// 列出档案for(File f: fileList) {System.out.println(f.toString());}System.out.println();}}catch(ArrayIndexOutOfBoundsException e) {System.out.println("using: java FileDemo pathname");}}
}

执行结果:

java onlyfun.caterpillar.FileDemo C:\
[C:\WINDOWS]
[C:\Documents and Settings]
[C:\Program Files]
[C:\System Volume Information]
[C:\Recycled]
C:\A3N_A3L.10
C:\bootfont.bin
C:\ntldr
C:\NTDETECT.COM
C:\boot.ini
C:\CONFIG.SYS
C:\AUTOEXEC.BAT
C:\IO.SYS
C:\MSDOS.SYS
C:\Finish.log
C:\pagefile.sys
C:\VIRTPART.DAT

9、RandomAccessFile

档案存取通常是「循序的」,每在档案中存取一次,读取档案的位置就会相对于目前的位置前进,然而有时候您必须对档案的某个区段进行读取或写入的动作,也就是进行「随机存取」(Random access),也就是说存取档案的位置要能在档案中随意的移动,这时您可以使用RandomAccessFile,使用seek()方法来指定档案存取的位置,指定的单位是字节,藉由它您就可以对档案进行随机存取的动作。

为了方便,通常在随机存取档案时会固定每组资料的长度,例如一组学生个人数据,Java中并没有像C/C++中可以直接写入一个固定长度结构(Structure)的方法,所以在固定每组长度的方面您必须自行设计。

下面这个程序示范了如何使用RandomAccessFile来写入档案,并随机读出一笔您所想读出的资料:

Student.java
package onlyfun.caterpillar;
public class Student {private String name; // 固定 15 字符private int score;public Student() {setName("noname");}public Student(String name, int score) {setName(name);this.score = score;}public void setName(String name) {StringBuilder builder = null;if(name != null)builder = new StringBuilder(name);elsebuilder = new StringBuilder(15);builder.setLength(15);this.name = builder.toString();}public void setScore(int score) {this.score = score;}public String getName() {return name;}public int getScore() {return score;}// 每笔数据固定写入34字节public static int size() {return 34;}
}
RandomAccessFileDemo.java
package onlyfun.caterpillar;
import java.io.*;
import java.util.*;
public class RandomAccessFileDemo {public static void main(String[] args) {Student[] students = {new Student("Justin", 90),new Student("momor", 95),new Student("Bush", 88),new Student("caterpillar", 84)};try {File file = new File(args[0]);// 建立RandomAccessFile实例并以读写模式开启档案RandomAccessFile randomAccessFile =new RandomAccessFile(file, "rw");for(int i = 0; i < students.length; i++) {randomAccessFile.writeChars(students[i].getName());randomAccessFile.writeInt(students[i].getScore());}Scanner scanner = new Scanner(System.in);System.out.print("读取第几笔数据?");int num = scanner.nextInt();randomAccessFile.seek((num-1) * Student.size());Student student = new Student();student.setName(readName(randomAccessFile));student.setScore(randomAccessFile.readInt());System.out.println("姓名:" + student.getName());System.out.println("分数:" + student.getScore());randomAccessFile.close();}catch(ArrayIndexOutOfBoundsException e) {System.out.println("请指定文件名称");}catch(IOException e) {e.printStackTrace();}}private static String readName(RandomAccessFile randomAccessfile)throws IOException {char[] name = new char[15];for(int i = 0; i < name.length; i++)name[i] = randomAccessfile.readChar();return new String(name).replace('\0', ' ');}
}

在实例化一个RandomAccessFile对象时,要设定档案开启的方式,设定"r"表示只供读取,设定"rw"表示可读可写;为了让每组数据长度固 定,在写入name时,我们使用 StringBuilder 并设定其长度固定为15个字符,而读回name时则直接读回15个字符,然后再去掉空格符传回。

10、InputStream与OutputStream

计算机中的数据都是以0与1的方式来储存,如果您要在两个装置之间进行数据的存取,当然也是以0与1位的方式来进行,实际上数据的流动是透过电路,而上面 的数据则是电流,而在程序上来说,将数据目的地与来源之间抽象化为一个串流(Stream),而当中流动的则是位数据。

01010101 Stream -->
来源地 ===================== 目的地

在Java中有两个类别用来作串流的抽象表示:InputStream与OutputStream。

InputStream是所有表示位输入串流的类别之父类别,它是一个抽象类别,子类会重新定义它当中所定义的方法, InputStream用于从装置来源地读取数据的抽象表示,例如System中的标准输入串流 in 对象就是一个 InputStream,在程序开始之后,这个串流对象就会开启,以从标准输入装置中读取数据,这个装置通常是键盘或是其它使用者定义的装置。

OutputStream是所有表示位输出串流的类别之父类别,它是一个抽象类别,子类会重新定义它当中所定义的方法, OutputStream是用于将数据写入目的地的抽象表示,例如System中的标准输出串流对象 out ,out 的类型是PrintStream, 这个类别是OutputStream的子类别(FilterOutputStream继承OutputStream, PrintStream再继承FilterOutputStream),在程序开始之后,这个串流对象就会开启,您可以将数据透过它来写入目的地装置,这 个装置通常是屏幕或其它使用者定义的装置。

下面程序可以读取键盘输入串流,并将资料以10进位方式显示在屏幕上:

StreamDemo.java
package onlyfun.caterpillar;
import java.io.*;
public class StreamDemo {public static void main(String[] args) {try {System.out.print("输入字符: ");System.out.println("输入字符十进制表示: " +System.in.read());System.out.println("换行字符十进制表示: " +System.in.read());}catch(IOException e) {e.printStackTrace();}}
}

执行结果: 

输入字符: A?
输入字符十进制表示: 65 
换行字符十进制表示: 10

字符A输入后被标准输入串流读取,A的位表示以十进制来看就是65,这是A字符的编码(查查ASCII编码表就知道了),在这边要注意的是read()只读取一个字节的数据,而当输入A并按Enter键时,实际上在串流中会有A的位数据与换行字符的位数据,换行字符的位数据以十进制来表示的话就是10。

操作系统之间的换行字符各不相同,Windows 为"\r\n",Linux 为'\n',而 Mac 为'\r'。

11、BufferedInputStream、 BufferedOutputStream

在介绍 FileInputStream、 FileOutputStream的 例子中,您使用了一个数组来作为数据读入的缓冲区,以档案存取为例的话,您知道磁盘存取的速度是远低于内存中的数据存取速度,为了减少对磁盘的存 ,您一次读入一定长度的数据,如上一个主题范例中的1024字节,而写入时也是一次写入一定长度的数据,这可以增加数据存取的效率。

BufferedInputStream与BufferedOutputStream可以为InputStream类的对象增加缓冲区功能,使用它们,您无需自行设计缓冲区。

BufferedInputStream的数据成员buf是个位数组,预设为2048字节大小,当读取数据来源时,例如档案, BufferedInputStream会尽量将buf填满,当使用read()方法时,实际上是先读取buf中的数据,而不是直接对数据来源作读取,当buf中的数据不足时,BufferedInputStream才会再从数据来源中提取数据。

BufferedOutputStream的数据成员buf是个位数组,预设为512个字节,当写入数据时,会先将资料存至buf中,当buf已满时才会一次将数据写至目的地,而不是每次写入都对目的地作写入。

将上一个主题的范例作个改写,这次不用自行设定缓冲区并进行判断了,使用BufferedInputStream、 BufferedOutputStream让程序看来简单一些,也比较有效率:

BufferedStreamDemo.java
package onlyfun.caterpillar;
import java.io.*;
public class BufferedStreamDemo {public static void main(String[] args) {try {byte[] data = new byte[1];File srcFile = new File(args[0]);File desFile = new File(args[1]);BufferedInputStream bufferedInputStream =new BufferedInputStream(new FileInputStream(srcFile));BufferedOutputStream bufferedOutputStream =new BufferedOutputStream(new FileOutputStream(desFile));System.out.println("复制档案:" +srcFile.length() + "字节");while(bufferedInputStream.read(data) != -1) {bufferedOutputStream.write(data);}// 将缓冲区中的数据全部写出bufferedOutputStream.flush();// 关闭串流bufferedInputStream.close();bufferedOutputStream.close();}catch(ArrayIndexOutOfBoundsException e) {System.out.println("using: java UseFileStream src des");e.printStackTrace();}catch(IOException e) {e.printStackTrace();}}
}

为了确保缓冲区中的数据一定被写出,建议最后执行flush()将缓冲区中的数据全部写出目的串流中。

BufferedInputStream、BufferedOutputStream并没有改变来源InputStream或目的 OutputStream的行为,读入或写出时的动作还是InputStream、OutputStream负责, BufferedInputStream、BufferedOutputStream只是在这之前动态的为它们加上一些功能(像是缓冲区功能),在这边是 以档案存取串流为例,实际上您可以在其它串流对象上加上BufferedInputStream、BufferedOutputStream功能。

12、FileInputStream、 FileOutputStream

FileInputStream是InputStream的子类,由名称上就可以知道, FileInputStream主要就是从指定的档案中读取数据至目的地。

FileOutputStream是OutputStream的子类,顾名思义,FileInputStream主要就是从来源地写入数据至指定的档案中。

标准输入输出串流对象在程序一开始就会开启,但只有当您建立一个FileInputStream或FileOutputStream的实例时,实际的串流才会开启,而不使用串流时,也必须自行关闭串流,以释放与串流相依的系统资源。

下面这个程序可以复制档案,程序先从来源档案读取数据至一个位缓冲区中,然后再将位数组的数据写入目的档案:

FileStreamDemo.java
package onlyfun.caterpillar;
import java.io.*;
public class FileStreamDemo {public static void main(String[] args) {try {byte[] buffer = new byte[1024];FileInputStream fileInputStream =new FileInputStream(new File(args[0]));FileOutputStream fileOutputStream =new FileOutputStream(new File(args[1]));System.out.println("复制档案:" +fileInputStream.available() + "字节");while(true) { // 从来源档案读取数据至缓冲区if(fileInputStream.available() < 1024) {int remain;while((remain = fileInputStream.read())!= -1) {fileOutputStream.write(remain);}break;}else {fileInputStream.read(buffer);// 将数组数据写入目的档案fileOutputStream.write(buffer);}}// 关闭串流fileInputStream.close();fileOutputStream.close();System.out.println("复制完成");}catch(ArrayIndexOutOfBoundsException e) {System.out.println("using: java FileStreamDemo src des");e.printStackTrace();}catch(IOException e) {e.printStackTrace();}}
}

这个程序示范了两个 read() 方法,一个可以读入指定长度的数据至数组,一个一次可以读入一个字节,每次读取之后,读取的指标都会往前进,您使用available()方法获得还有多少字节可以读取;除了使用File来建立FileInputStream、FileOutputStream的实例之外,您也可以直接使用字符串指定路径来建立。

不使用串流时,记得使用close()方法自行关闭串流,以释放与串流相依的系统资源。

13、ObjectInputStream、ObjectOutputStream

在Java这样支持对象导向的程序中撰写程序,很多数据都是以对象的方式存在,在程序运行过后,您会希望将这些数据加以储存,以供下次执行程序时使用,这时您可以使用ObjectInputStream、ObjectOutputStream来进行这项工作。

要被储存的对象必须实作Serializable接口,说是实作,其实Serializable中并没有规范任何必须实作的方法,所以这边所谓实作的意义,其实像是对对象贴上一个标志,代表该对象是可以序列化的(Serializable)。

一个实作的例子如下所示:

Student.java
package onlyfun.caterpillar;
import java.io.*;
public class Student implements Serializable {private static final long serialVersionUID = 1L;private String name;private int score;public Student() {name = "N/A";}public Student(String name, int score) {this.name = name;this.score = score;}public void setName(String name) {this.name = name;}public void setScore(int score) {this.score = score;}public String getName() {return name;}public int getScore() {return score;}public void showData() {System.out.println("name: " + name);System.out.println("score: " + score);}
}

您要注意到serialVersionUID,这代表了可序列化对象的版本, 如果您没有提供这个版本讯息,则会自动依类名称、实现的接口、成员等讯息来产生,如果是自动产生的,则下次您更改了Student类,则自动产生的 serialVersionUID也会跟着变更,当反序列化时两个serialVersionUID不相同的话,就会丢出 InvalidClassException,如果您想要维持版本讯息的一致,则要显式宣告serialVersionUID。

ObjectInputStream、ObjectOutputStream为InputStream、OutputStream加上了可以让使用者写入 对象、读出对象的功能,在写入对象时,我们使用writeObject()方法,读出对象时我们使用readObject()方法,被读出的对象都是以 Object的型态传回,您必须将之转换为对象原来的型态,才能正确的操作被读回的对象,下面这个程序示范了如何简单的储存对象至档案中,并将之再度读 回:

ObjectStreamDemo.java
package onlyfun.caterpillar;
import java.io.*;
import java.util.*;
public class ObjectStreamDemo {public static void writeObjectsToFile(Object[] objs, String filename) {File file = new File(filename);try {ObjectOutputStream objOutputStream =new ObjectOutputStream(new FileOutputStream(file));for(Object obj : objs) {objOutputStream.writeObject(obj);}objOutputStream.close();}catch(IOException e) {e.printStackTrace();}}public static Object[] readObjectsFromFile(String filename)throws FileNotFoundException {File file = new File(filename);if(!file.exists())throw new FileNotFoundException();List list = new ArrayList();try {FileInputStream fileInputStream =new FileInputStream(file);ObjectInputStream objInputStream =new ObjectInputStream(fileInputStream);while(fileInputStream.available() > 0) {list.add(objInputStream.readObject());}objInputStream.close();}catch(ClassNotFoundException e) {e.printStackTrace();}catch(IOException e) {e.printStackTrace();}return list.toArray();}public static void appendObjectsToFile(Object[] objs, String filename)throws FileNotFoundException {File file = new File(filename);if(!file.exists())throw new FileNotFoundException();try {ObjectOutputStream objOutputStream =new ObjectOutputStream(new FileOutputStream(file, true)) {protected void writeStreamHeader()throws IOException {}};?for(Object obj : objs) {objOutputStream.writeObject(obj);}objOutputStream.close();}catch(IOException e) {e.printStackTrace();}}public static void main(String[] args) {Student[] students = {new Student("caterpillar", 90),new Student("justin", 85)};// 写入新档writeObjectsToFile(students, "data.dat");try {// 读取档案数据Object[] objs = readObjectsFromFile("data.dat");for(Object obj : objs) {((Student) obj).showData();}System.out.println();students = new Student[2];students[0] = new Student("momor", 100);students[1] = new Student("becky", 100);// 附加至档案appendObjectsToFile(students, "data.dat");// 读取档案数据objs = readObjectsFromFile("data.dat");for(Object obj : objs) {((Student) obj).showData();}}catch(FileNotFoundException e) {e.printStackTrace();}}
}

对象被写出时,会写入对象的类别型态、类别署名(Class signature),static与被标志为transient的成员则不会被写入。

在这边注意到以附加的形式写入数据至档案时,在试图将对象附加至一个先前已写入对象的档案时,由于ObjectOutputStream在 写入数据时,还会加上一个特别的标示头,而读取档案时会检查这个标示头,如果一个档案中被多次附加对象,那么该档案中会有多个标示头,如此读取检查时就会 发现不一致,这会丢出StreamCorrupedException,为此,您重新定义ObjectOutputStream的writeStreamHeader()方法,如果是以附加的方式来写入对象,就不写入标示头:

ObjectOutputStream objOutputStream =
new ObjectOutputStream(
new FileOutputStream(file, true)) {
protected void writeStreamHeader()
throws IOException {}
};

对象写出或读入并不仅限于档案存取,您也可以用于网络的数据传送,例如传送整个对象数据或是影像档案

14、DataInputStream、DataOutputStream

DataInputStream、DataOutputStream可提供一些对Java基本数据型态写入的方法,像是读写int、double、 boolean等的方法,由于Java的数据型态大小是规定好的,在写入或读出这些基本数据型态时,就不用担心不同平台间资料大小不同的问题。

这边还是举档案存取来进行说明,有时候您只是要储存一个对象的成员数据,而不是整个对象的信息,成员数据的型态假设都是Java的基本数据型态,您不必要 使用Object输入、输出相关串流对象,而可以使用DataInputStream、DataOutputStream来写入或读出数据,下面这个程序 是个简单的示范:

Student.java
package onlyfun.caterpillar;
public class Student?{private String name;private int score;public Student() {name = "N/A";}public Student(String name, int score) {this.name = name;this.score = score;}public void setName(String name) {this.name = name;}public void setScore(int score) {this.score = score;}public String getName() {return name;}public int getScore() {return score;}public void showData() {System.out.println("name: " + name);System.out.println("score: " + score);}
}
DataStreamDemo.java
package onlyfun.caterpillar;
import java.io.*;
public class DataStreamDemo {public static void main(String[] args) {Student[] students = {new Student("Justin", 90),new Student("momor", 95),new Student("Bush", 88)};try {DataOutputStream dataOutputStream =new DataOutputStream(new FileOutputStream("data.dat"));for(Student student : students) {dataOutputStream.writeUTF(student.getName());dataOutputStream.writeInt(student.getScore());}dataOutputStream.flush();dataOutputStream.close();DataInputStream dataInputStream =new DataInputStream(new FileInputStream("data.dat"));for(int i = 0; i < students.length; i++) {String name = dataInputStream.readUTF();int score = dataInputStream.readInt();students[i] = new Student(name, score);students[i].showData();}dataInputStream.close();}catch(IOException e) {e.printStackTrace();}}
}

这个程序在写入档案时,只提取对象的成员数据,而在读出时将这些数据读出,并将读回的数据设定给一个实例,是对象数据还原的一种方式。

15、SequenceInputStream

您将一个档案分割为数个档案,接下来要将之再度组合还原为原来的档案,最基本的作法是使用数个 FileInputStream来开启分割后的档案,然后一个一个档案的读取,并连续写入至同一个FileOutputStream中,在这中间,您必须 要自行判断每一个分割档案的读取是否完毕,如果完毕就换读取下一个档案。

如果您使用SequenceInputStream就不用这么麻烦,SequenceInputStream可以看作是数个 InputStream对象的组合,当一个InputStream对象的内容读取完毕后,它就会取出下一个InputStream对象,直到所有的 InputStream对象都读取完毕为止。

下面这个程序是SequenceInputStream的使用示范,它可以将指定的档案进行分割,也可以将分割后的档案还原为一个档案:

SequenceStreamDemo.java
package onlyfun.caterpillar;
import java.util.*;
import java.io.*;
public class SequenceStreamDemo {public static void main(String[] args) {try {// args[0]: 指定分割(s)或连接(c)switch (args[0].charAt(1)) {case 's':// args[1]: 每个分割档案的大小int size = Integer.parseInt(args[1]);// args[2]: 指定要被分割的文件名称seperate(args[2], size);break;case 'c':// args[1]: 指定要被组合的档案个数int number = Integer.parseInt(args[1]);// args[2]: 组合后的文件名称concatenate(args[2], number);break;}}catch(ArrayIndexOutOfBoundsException e) {System.out.println("Using: java UseSequenceStream [-s/-c]" +" (size/number) filename");System.out.println("-s: 分割档案\n-c: 组合档案");}catch(IOException e) {e.printStackTrace();}}// 分割档案public static void seperate(String filename, int size)throws IOException {FileInputStream fileInputStream =new FileInputStream(new File(filename));BufferedInputStream bufInputStream =new BufferedInputStream(fileInputStream);byte[] data = new byte[1];int count = 0;?// 从原档案大小及指定分割的大小// 决定要分割为几个档案if(fileInputStream.available() % size == 0)count = fileInputStream.available() / size;elsecount = fileInputStream.available() / size + 1;// 开始进行分割for(int i = 0; i < count; i++) {int num = 0;// 分割的档案加上底线与编号File file = new File(filename + "_" + (i + 1));BufferedOutputStream bufOutputStream =new BufferedOutputStream(new FileOutputStream(file));while(bufInputStream.read(data) != -1) {bufOutputStream.write(data);num++;if(num == size) { // 分割出一个档案bufOutputStream.flush();bufOutputStream.close();break;}}if(num < size) {bufOutputStream.flush();bufOutputStream.close();}}System.out.println("分割为" + count + "个档案");}// 连接档案public static void concatenate(String filename,int number) throws IOException {// 收集档案用的ListList list =new ArrayList();for(int i = 0; i < number; i++) {// 文件名必须为底线加上编号File file = new File(filename + "_" + (i+1));list.add(i, new FileInputStream(file));}final Iterator iterator = list.iterator();// SequenceInputStream 需要一个Enumeration对象来建构Enumeration enumation =new Enumeration() {public boolean hasMoreElements() {return iterator.hasNext();}public InputStream nextElement() {return iterator.next();}};// 建立SequenceInputStream// 并使用BufferedInputStreamBufferedInputStream bufInputStream =new BufferedInputStream(new SequenceInputStream(enumation),8192);BufferedOutputStream bufOutputStream =new BufferedOutputStream(new FileOutputStream(filename), 8192);byte[] data = new byte[1];// 读取所有档案数据并写入目的地档案while(bufInputStream.read(data) != -1)bufOutputStream.write(data);bufInputStream.close();bufOutputStream.flush();bufOutputStream.close();System.out.println("组合" + number + "个档案 OK!!");}
}

分割档案时的范例如下:

java onlyfun.caterpillar.SequenceStreamDemo -s 1048576 test.zip
分割为6个档案

组合档案时的范例如下:

java onlyfun.caterpillar.SequenceStreamDemo -c 6 test.zip
组合6个档案 OK!!

16、PrintStream

之前所介绍过的Stream输出对象,都是直接将内存中的数据写出至目的地(例如一个档案),举个例子来说,如果您将 int 整数 1 使用之前介绍的Stream对象输出至档案,则档案中所储存的是 int 整数 1 在内存中的值,例如:

FileStream.java
package onlyfun.caterpillar;
import java.io.*;
public class FileStreamDemo {public static void main(String[] args)throws IOException {FileOutputStream file =new FileOutputStream(new File("test.txt"));file.write(1);file.close();}
}

由于您使用write()方法,这会将 1 在内存中的值之低字节0000001写入档案中,所以如果您使用文字编辑软件(像vi或UltraEdit)观看test.txt的16进位表示,其结果会显示 01(16进位表示)。

有时候您所想要储存的结果是转换为字符之后的结果,例如若程序的执行结果是3.14159,您会希望使用字符来储存3.14159,也就是俗称的储存为纯文本文件,如此当您使用简单的纯文字编辑器观看时,就可以直接看到程序执行的结果。

例如您若想使用纯文本文件看到test.txt的显示结果是1,则必须先将内存中的整数1,也就是二进制00000000 00000000 00000000 00000001转换为对应的字符编码,也就是0x31(十进制表示49)并加以储存。

使用PrintStream可以自动为您进行字符转换的动作,它会使用操作系统的预设编码来处理对应的字符转换动作,直接使用下面这个例子来作示范:

PrintStreamDemo.java
package onlyfun.caterpillar;
import java.io.*;
public class PrintStreamDemo {public static void main(String[] args)throws FileNotFoundException {PrintStream printStream = new PrintStream(new FileOutputStream(new File("pi.txt")));printStream.print("PI = ");printStream.println(Math.PI);printStream.close();}
}

执行程序之后使用纯文字编辑器开启pi.txt,其内容会是PI = 3.141592653589793,print()或println()接受int、char、String、double等等数据型态, println()会在输出之后加上换行字符,而print()则不会。

注意在档案储存上实际并没有二进制档案或是纯文本文件的分别,所有的档案所储存的都是二进制的数据,您俗称的纯文本文件,其实正确的说,是指储存的结果是经过字符转换,例如将 int 整数 1转换为字符 '1' 的编码结果并加以储存。

17、Reader、Writer

Reader、Writer支持Unicode标准字符集(Character set)(字节串流则只支持ISO-Latin-1 8-bit),在处理串流时,会根据系统预设的字符编码来进行字符转换,它们是抽象类别,真正您会使用其子类别,子类别通常会重新定义相关的方法。

在 PushbackInputStream 中,您读入一个含BIG5中文字及ASCII字符的文本文件,这边改写一下这个例子,使用Reader的子类别 InputStreamReader来转换读入的两个字节为汉字字符,并显示在屏幕上:

ReaderWriterDemo.java
package onlyfun.caterpillar;
import java.io.*;
public class ReaderDemo {public static void main(String[] args) {try {PushbackInputStream pushbackInputStream =new PushbackInputStream(new FileInputStream(args[0]));byte[] array = new byte[2];ByteArrayInputStream byteArrayStream =new ByteArrayInputStream(array);// reader会从已读的位数组中取出数据InputStreamReader reader =new InputStreamReader(byteArrayStream);int tmp = 0;int count = 0;while((count = pushbackInputStream.read(array))!= -1) {// 两个字节转换为整数tmp = (short)((array[0] << 8) |(array[1] & 0xff));tmp = tmp & 0xFFFF;// 判断是否为BIG5,如果是则显示BIG5中文字if(tmp >= 0xA440 && tmp < 0xFFFF) {System.out.println("BIG5: " +(char)reader.read());// 重置ArrayInputStream的读取光标// 下次reader才会再重头读取数据byteArrayStream.reset();}else {// 将第二个字节推回串流pushbackInputStream.unread(array, 1, 1);// 显示ASCII范围的字符System.out.println("ASCII: " +(char)array[0]);}}pushbackInputStream.close();}catch(ArrayIndexOutOfBoundsException e) {System.out.println("请指定文件名称");}catch(IOException e) {e.printStackTrace();}}
}

假设的文本文件中有以下的文字:"这T是e一s个t测试" ,执行结果会是:

BIG5: 这 
ASCII: T 
BIG5: 是 
ASCII: e 
BIG5: 一 
ASCII: s 
BIG5: 个 
ASCII: t 
BIG5: 测 
BIG5: 试 
ASCII: ! 
EOF?

InputStreamReader可以用字节串流中取出字节数据,并进行字符处理动作,关于Reader、Writer相关子类别,之后会于各相关主题进行介绍。

其余未看:

ByteArrayInputStream、ByteArrayOutputStream;

CharArrayReader、CharArrayWriter;

PushbackReader;

http://www.iteedu.com//plang/java/javadiary/92.php

转载于:https://www.cnblogs.com/CheeseZH/archive/2012/12/10/2811913.html

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/274445.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

全库模式 用户模式 表模式_暗模式,亮模式和用户的故事

全库模式 用户模式 表模式I have been working on designing a UI for an app that has individuals over the age of 60 as its main audience. At some point, I found my design more appealing in dark mode. As a UX designer, I know that my opinions and preferences d…

Rollup 与 Webpack 的 Tree-shaking

大家好&#xff0c;我是若川。我持续组织了近一年的源码共读活动&#xff0c;感兴趣的可以 点此扫码加我微信 lxchuan12 参与&#xff0c;每周大家一起学习200行左右的源码&#xff0c;共同进步。同时极力推荐订阅我写的《学习源码整体架构系列》 包含20余篇源码文章。历史面试…

聚类与分类的主要区别在于:_经验在于细节:分析流服务的用户体验

聚类与分类的主要区别在于&#xff1a;看不见的差异 (The Invisible Difference) When app markets mature the overlap in features and designs grows closer as they catch up and copy each other. The more similar the apps are to one another, the more important the …

asp.net 动态创建TextBox控件 如何加载状态信息

接着上文Asp.net TextBox的TextChanged事件你真的清楚吗&#xff1f; 这里我们来说说状态数据时如何加载的。虽然在Control中有调用状态转存的方法&#xff0c;但是这里有一个判断条件 if (_controlState > ControlState.ViewStateLoaded) 一般的get请求这里的条件是不满足…

从零实现一个迷你 Webpack

大家好&#xff0c;我是若川。我持续组织了近一年的源码共读活动&#xff0c;感兴趣的可以 点此扫码加我微信 lxchuan12 参与&#xff0c;每周大家一起学习200行左右的源码&#xff0c;共同进步。同时极力推荐订阅我写的《学习源码整体架构系列》 包含20余篇源码文章。历史面试…

ios 刷新遮罩遮罩_在Adobe XD中进行遮罩的3种方法

ios 刷新遮罩遮罩Are you new to Adobe XD? Or maybe you’re just stuck on how to create a simple mask? Here are 3 quick tips for how to mask your photos and designs in Adobe XD.您是Adobe XD的新手吗&#xff1f; 或者&#xff0c;也许您只是停留在如何创建简单的…

C#除法运算

C#中进行算术运算容易牵扯到类型的自动转换&#xff0c;这种自动转换称之为隐式转换&#xff0c;当然还可以人为的强制转换 隐式转换要求&#xff1a;不丢失精度&#xff0c;而且转换前后都为数值 强制转换&#xff1a;容易丢失可能会丢失精度 1 namespace 除法运算2 {3 cl…

Vite 4.0 正式发布!

源码共读我新出了&#xff1a;第40期 | vite 是如何解析用户配置的 .env 的链接&#xff1a;https://www.yuque.com/ruochuan12/notice/p40也可以点击文末阅读原文查看&#xff0c;欢迎学习记笔记~12 月 9 日&#xff0c;Vite 4.0 正式发布。下面就来看看 Vite 4.0 有哪些更新吧…

代码复审

我们CodingCook复审的是WWW的代码&#xff0c;他们的项目是时间管理助手&#xff08;TimeLine&#xff09;。只是跟根据自己的经验来看&#xff0c;不一定准 先说一下整体的感觉。WWW的代码用了应该是比较符合面向对象的思想&#xff0c;借口&#xff0c;封装随处可见&#xff…

图像标注技巧_保护互联网上图像的一个简单技巧

图像标注技巧补习 (TUTORIAL) Have you ever worried about sharing your images on the Internet? Anytime you upload something to the web you risk the chance of your work being used (without permission) by another.您是否曾经担心过要在Internet上共享图像&#xf…

【VueConf 2022】尤雨溪:Vue的进化历程

大家好&#xff0c;我是若川。我持续组织了近一年的源码共读活动&#xff0c;感兴趣的可以 点此扫码加我微信 lxchuan12 参与&#xff0c;每周大家一起学习200行左右的源码&#xff0c;共同进步。同时极力推荐订阅我写的《学习源码整体架构系列》 包含20余篇源码文章。历史面试…

WCF netTcpBinding寄宿到IIS7

config配置文件不多说 <?xml version"1.0" encoding"utf-8" ?> <configuration><system.serviceModel><behaviors><serviceBehaviors><behavior name"myBehavior"><serviceMetadata/></behavior…

ar软件测试工具_如何为用户测试制作快速的AR原型

ar软件测试工具We had a project recently with an element of AR-based interaction, which it turned out was impossible to create as a prototype in either Invision or Framer (our current stack). This had a massive impact on our ability to test with users in a …

未来ui设计的发展趋势_2025年的未来UI趋势?

未来ui设计的发展趋势Humans are restless.人类是不安的。 Once we find something that works, we get used to it and we crave the next big thing. The next innovation. When will the future finally arrive? And when it does, how long will it take us to get used …

内存泄露检测 vld

VLD是一款开源检测内存泄露软件的简称&#xff1a;Visual Leak Detector 网站&#xff1a;http://vld.codeplex.com/ 使用&#xff1a; 1. 安装vld 或者 下载相关 .h&#xff0c;lib&#xff0c;dll 文件 2. 方法很简单&#xff0c;只要在包含入口函数的.cpp文件中包含vld.h就可…

Monorepo 在网易的工程改造实践

大家好&#xff0c;我是若川。我持续组织了近一年的源码共读活动&#xff0c;感兴趣的可以 点此扫码加我微信 lxchuan12 参与&#xff0c;每周大家一起学习200行左右的源码&#xff0c;共同进步。同时极力推荐订阅我写的《学习源码整体架构系列》 包含20余篇源码文章。历史面试…

这一年,Vue.js 生态开源之旅带给我很大收获~

大家好&#xff0c;我是若川。我持续组织了近一年的源码共读活动&#xff0c;感兴趣的可以 点此扫码加我微信 lxchuan12 参与&#xff0c;每周大家一起学习200行左右的源码&#xff0c;共同进步。同时极力推荐订阅我写的《学习源码整体架构系列》 包含20余篇源码文章。历史面试…

CSSyphus:烦躁不安的烦恼设计指南。

I’m trapped at home with my website. Or maybe it’s trapped at home with me. While some are using the weird lump of time provided by lockdown to indulge in baking, dancing, painting, singing, I’m using it to play around with code.我 被自己的网站困在家里。…

重构与臭豆腐4

重构要继续&#xff0c;臭豆腐要做。   这个重构中各种提取类&#xff0c;方法&#xff0c;字段&#xff0c;可以方便的理解&#xff0c;如果使用了设置模式就更加逻辑清晰了。切东西也要讲究刀法的。 重构可以方便的使用设计模式。设计模式为重构提供了目标。 比如多个if 可…

你构建的代码为什么这么大?如何优化~

大家好&#xff0c;我是若川。我持续组织了近一年的源码共读活动&#xff0c;感兴趣的可以 点此扫码加我微信 lxchuan12 参与&#xff0c;每周大家一起学习200行左右的源码&#xff0c;共同进步。同时极力推荐订阅我写的《学习源码整体架构系列》 包含20余篇源码文章。历史面试…