1 字节流处理类
1.1 字节流详细介绍
字节流:处理字节数据的流对象。设备上的数据无论是图片或者dvd,文字,它们都以二进制存储的。二进制的最终都是以一个8位为数据单元进行体现,所以计算机中的最小数据单元就是字节。意味着,字节流可以处理设备上的所有数据,所以字节流一样可以处理字符数据。
那么为什么要有字符流呢?因为字符每个国家都不一样,所以涉及到了字符编码问题,那么GBK编码的中文用unicode编码解析是有问题的,所以需要获取中文字节数据的同时+ 指定的编码表才可以解析正确数据。为了方便于文字的解析,所以将字节流和编码表封装成对象,这个对象就是字符流。只要操作字符数据,优先考虑使用字符流体系。
InputStream和OutputStream的常用类继承架构
InputStream | ByteInputStream |
|
| ObjectInputStream |
|
| FileInputStream | BufferedInputStream |
| FilterInputStream | DataInputStream |
OutputStream | ByteOutputStream |
|
| ObjectOutputStream | BufferedOutputStream |
| FileOutputStream | DataOutputStream |
| FilterOutputStream | PrintStream |
Reader和Writer常用类继承架构
Reader | CharArrayReader |
|
| StringReader |
|
| BufferedReader |
|
| InputStreamReader | FileReader |
Writer | CharArrayWriter |
|
| StringWriter |
|
| BufferedWriter |
|
| OutputStreamWriter | FileWriter |
| PrintWriter |
|
串流处理装饰器:专职用于打包,方便数据操作。
常用的打包器有:
具备缓冲区作用的BufferedInputStream、 BufferedOutputStream(如果没有缓存,每次读写都要定位硬盘,效率显得很低。使用缓存可以减少读写次数,读写效率也大大提高,毕竟内存的读取速度很快)
具备数据转换处理作用的DataInputStream、 DataOutputStream(提供和读取、写入java基本数据类型的方法,像是读写int、double、boolean等方法。这些方法会在指定的类型与字节之间进行转换,不需要你亲自做字节与类型转换的动作)
具备对象串行化能力的ObjectInputStream、 ObjectOutputStream(ObjectInputStream提供readObject()方法将数据读入为对象,而ObjectOutputStream提供writeObject()方法将对象写至目的地,可以被这两个方法处理的对象,必须操作java.io.Serializable接口,这个接口并没有定义任何方法,只是作为标示之用,表示这个对象是可以串行化的(Serializable))
由于这些类本身并没有改变 InputStream、OutputStream 的行为,只不过在 InputStream 取得数据之后,再做一些加工处理,或者是要输出时做一些加工处理,再交由 OutputStream 真正进行输出,因此又称它们为装饰器(Decorator)。就像照片本身装上华丽外框,就可以让照片感觉更为华丽,或有点像小水管衔接大水管,如小水管(InputStream)读入数据,再由大水管(如BufferedInputStream)增加缓冲功能,如图10.5所示。
1.2 控制台的输入输出
System.in与System.out分别是InputStream与PrintStream的实例,分别代表标准输入(Standard input)与标准输出(Standard output),以个人计算机而言,通常对应至文本模式中的输入与输出。【控制台自动将字节数组数据解码为对应数据,类似与记事本打开就已经解码】
以System.in而言,因为文本模式下通常是取得整行用户输入,因此较少直接操作InputStream相关方法,如使java.util.Scanner打包System.in。操作Scanner相关方法时,Scanner会代你操控System.in取得数据,并转换为取得你想要的数据类型。
可以使用System的setIn()方法指定InputStream实例,重新指定标准输入来源。
Scanner常用方法示例:s.hasNextInt();s.nextInt();等
System.out有静态方法print(),println(),可以直接打印出相应字符数据,如果是整形数据或通过String.valueOf(i)转换成字符输出; 以通过System.setOut()重新指定标准输出流串。
System.err也是PrintStream实例,称为标准错误输出流,可以通过System.setErr()重新指定标准错误输出流串。
2 字符流处理类
InputStream、OutputStream是用来读入与写出字节数据,若实际上处理的是字符数据,使用InputStream、OutputStream就得对照编码表,在字符与字节之间进行转换。所幸Java SE API已提供相关输入/输出字符处理类,让你不用亲自进行字节与字符编码转换的枯燥工作。
正如同InputStream、OutputStream有一些装饰器类,可以对InputStream、OutputStream打包增加额外功能,Reader、Writer也有一些装饰器类可供使用。下面介绍常用的字符处理装饰器类。
如果串流处理的字节数据,实际上代表某些字符的编码数据,而你想要将这些字节数据转换为对应的编码字符,可以使用InputStreamReader、OutputStreamWriter对串流数据打包。
在建立InputStreamReader与OutputStreamWriter时,可以指定编码,如果没有指定编码,则以JVM启动时所获取的默认编码来做字符转换。
正如BufferedInputStream、BufferedOutputStream为InputStream、OutputStream提供缓冲区作用,以改进输入/输出的效率,BufferedReader、BufferedWriter可对Reader、Writer提供缓冲区作用,在处理字符输入/输出时,对效率也会有所帮助。
就装饰器的作用而言,InputStreamReader将System.in读入的字节数据做编码转换,而BufferedReader将编码转换后的数据做缓冲处理,以增加读取效率。BufferedReader的readLine()方法,可以读取一行数据(以换行字符为依据)并以字符串返回,返回的字符串不包括换行字符。
代码示例:
BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));
String name = reader.readLine();
System.out.printf("Hello, %s!", name);
PrintWriter与PrintStream使用上极为类似,不过除了可以对OutputStream打包之外,PrintWriter还可以对Writer进行打包,提供print()、println()、format()等方法。
3 RandomAccessFile
Java的RandomAccessFile提供对文件的读写功能,与普通的输入输出流不一样的是RamdomAccessFile可以任意的访问文件的任何地方。这就是“Random”的意义所在。
RandomAccessFile的对象包含一个记录指针,用于标识当前流的读写位置,这个位置可以向前移动,也可以向后移动。RandomAccessFile包含两个方法来操作文件记录指针。
long getFilePoint():记录文件指针的当前位置。
void seek(long pos):将文件记录指针定位到pos位置。
RandomAccessFile包含InputStream的三个read方法,也包含OutputStream的三个write方法。同时RandomAccessFile还包含一系列的readXxx和writeXxx方法完成输入输出。
使用示例:
import java.io.*;
public class RandomAccessFileDemo {
public static void main(String[] args) throws IOException {
RandomAccessFile raf = new RandomAccessFile("e:/mm.txt","rw");
writeFile(raf);
}
public static void writeFile(RandomAccessFile raf) throws IOException
{
raf.write("张三".getBytes());
raf.writeInt(97);
raf.write("李四".getBytes());
raf.writeInt(98);
raf.close();
}
}
4 文件类型
(1)文本文件:每一个字符都被表示成1个或2个字节,具体方式依赖于该系统使用ASCII还是Unicode编码。例如int型值12345写入文件,将在该文件中放入5个字符。直接使用FileInputStream和FileOutputStream就可以读取和写入文本文件。
(2)二进制文件:以相同格式存储同种基本类型数据的值。每个值都被存储为一连串相同个数的字节。例如:int型值12345写入文件,将在该文件中占据4个字节。使用ObjectInputStream和ObjectOutputStream读取和写入二进制文件。(本质上就是个基本类型的包装类处理)具体使用示例:new ObjectInputStream(new FileInputStream(“x.txt”));
此外,类的串行化可将对象直接存入二进制文件。【串行化需要先将类实现Serializable接口】
(3)使用情形:如果想让文本编辑器或者创建一个程序将读取该文件,那么使用文本文件;在其他情况下,考虑使用二进制文件,原因是二进制文件通常占据更少的空间。
5 txt按行处理程序
package txt;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.junit.Test;
/**
*
* 直接通过输入输出流读取文件
* 需要完成:
*
*/
public class JavaConstant {
public static String readDoc(String filePath){
File file = new File(filePath);
FileReader fr = null;
String content = "";
String tempContent = null;
String tempContent1 = null;
try {
fr = new FileReader(file);
} catch (FileNotFoundException e) {
e.printStackTrace();
}
BufferedReader br = new BufferedReader(fr);
while(true){
try {
//对每一行进行读取,然后处理
tempContent = br.readLine();
tempContent1 = "public final static int "+tempContent+"\r\n";
tempContent1 = replace(tempContent1)+";\r\n";
} catch (IOException e) {
e.printStackTrace();
}
if(tempContent!=null){
content +=tempContent1;
System.out.println(content);
}else{
break;
}
}
try {
br.close();
} catch (IOException e) {
e.printStackTrace();
}
try {
fr.close();
} catch (IOException e) {
e.printStackTrace();
}
return content;
}
public static boolean writeDoc(String filePath,String content){
FileWriter fw = null;
try {
fw = new FileWriter(filePath);
} catch (IOException e) {
e.printStackTrace();
}
BufferedWriter bw = new BufferedWriter(fw);
try {
bw.write(content);
} catch (IOException e) {
e.printStackTrace();
}
try {
bw.close();
} catch (IOException e) {
e.printStackTrace();
}
try {
fw.close();
} catch (IOException e) {
e.printStackTrace();
}
return true;
}
public static String replace(String content){
//content="RESP_A_AZS表示0x00010001.";
String result = null;
String regex1="[A-Z][^A-Z]{0,}0x\\d{4}.{0,}";
Pattern p;
Matcher m;
p=Pattern.compile(regex1);
m=p.matcher(content);
while(m.find()){
String x=m.group();
CharSequence target = x.substring(1,x.indexOf("0x"));
System.out.println("要替换的内容:"+target);
String regex2=".{0,}[A-Z][^A-Z]{0,}0x\\d{4}.{0,}";
p = Pattern.compile(regex2);
m=p.matcher(content);
if(m.find()){
x=m.group();
System.out.println("全部内容:"+x);
}
CharSequence replacement = " = ";
result = x.replace(target, replacement);
System.out.println("替换后:"+x);
}
return result;
}
@Test
public void testRead(){
String filePath = "C:\\in.txt";
String content = readDoc(filePath);
System.out.println(content);
if(writeDoc("C:\\out.txt", content)){
System.out.println("文件写入成功!");
}
}
}