io编码
在io中分为字节、字符操作,面向字节的文件工作在8位编码上,面向字符的工作在16位的unicode编码上,其中每一个字符(unicode)是由两个字节数据组成。
InputStream的read方法
read()这个方法是用来读取数据,每次读取一个字节,返回的是下一个数据字节,说明文件指针不是指向第一个字节,而是第一个字节前面的一个,如果读到末尾返回-1。
byte[] b = new byte[3];
len = in.read(b)
read(byte[] b)这个方法是每次读取b.length长度的字节,返回是读入缓冲区的字节总数。
available
这个方法是返回不受阻塞地从此输入流中读取(或跳过)的估计剩余字节数,如果是本地文件,我们通常使用这个方法来获取文件总长度,如果在网络,你已经打开输出流,但是没有数据传过来,就阻塞,所以返回为0.在某些情况下,非阻塞的读取(或跳过)操作在执行很慢时看起来受阻塞,例如,在网速缓慢的网络上读取大文件时。
InputStream的write方法
write(int len)将指定字节写入到输出流中。
write(byte[] b)将 b.length 个字节从指定 byte 数组写入此文件输出流中。
flush()这个方法刷新缓冲区的内容到输出流。(这个方法是非常有用的,在多用户的情况下,不同的线程或用户之间要维持数据额的一致性,立即刷新就变得很重要)
正常如果使用这种读写,是配合使用,要么全是一个字节读写,要么就是字节数组读写。
InputReader(字符流)的read方法
read()返回读取的字符编码,如果已到达流的末尾,则返回 -1
OutputStream的write方法
write()返回 指定要写入字符的 int。
BufferReader和BufferWriter
在前面,我们为了方便读写,我们都自己申明了缓冲区。而这两个类内置了java的缓冲区,请记住,不管你是读取单个字符,还是512个字节,均需要相同数量的I/O操作,因此,使用缓冲区将更加高效。
newLine() 写入一个行分隔符。行分隔符字符串由系统属性 line.separator 定义,并且不一定是单个新行 (‘\n’) 符。
readLine() 读取一个文本行。通过下列字符之一即可认为某行已终止:换行 (‘\n’)、回车 (‘\r’) 或回车后直接跟着换行,包含该行内容的字符串,不包含任何行终止符,如果已到达流末尾,则返回 null 。
flush(),在缓冲流中,它会在文件写完后,关闭输出流的时候,自动关闭,而不需要手动,如果你着急,在没有关闭流,之前强制写到文件中,就需要此方法。
字节流与字符流
使用字符流的好处:
- 他们处理unicode字符集中的任何字符,而字节流金限于ISO Latin8位字节。
- 使用字符流的程序可以很容易进行国际化。
- 字符流使用内部缓存,本质比字节流高效。
通常情况下,使用InputStream/OutputStream类针对图像文件,声音文件、视频等写入二进制数据、ascll文件;要读写基于unicode文本文件,还是使用字符流。
nio包下的一些常用方法
遍历当前文件夹下的文件夹和文件,如果有子文件夹不会遍历,并且它不会遍历文件,会报不是文件目录异常。( 注意啦如果是文件夹就直接文件名,没有后缀格式名)
DirectoryStream<Path> d = null;
Path ps = Paths.get("c:\\lib");
try {
d = Files.newDirectoryStream(ps);
for(Path p:d) {
System.out.println(p.getFileName());
}
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}finally {
if(d!=null) {
try {
d.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
文件的过滤
Path p = Paths.get("c:\\hello");
DirectoryStream<Path> ds = null;
try {
ds = Files.newDirectoryStream(p, "*.{txt}");
//这个是文件列举的时候过滤文件格式
for(Path p1:ds) {
System.out.println(p1.getFileName());
}
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}finally {
if(ds!=null) {
try {
ds.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
//这种方式局限就是只是过滤了后缀文件格式
下面就是自定义过滤文件条件:
Path p = Paths.get("c:\\hello");
DirectoryStream.Filter<Path> ds = null;
DirectoryStream<Path> ds1 = null;
ds = new DirectoryStream.Filter<Path>() {//后者必须指定泛型,会影响方法参数类型
@Override
public boolean accept(Path entry) throws IOException {
// TODO Auto-generated method stub
return Files.size(entry)==9l;
//Files这个方法是用来计算文件大小,单位字节,返回是长整型,所以9后面要加表示为长整型
//该方法显示接受返回结果为true的文件。
}
};
try {
ds1 = Files.newDirectoryStream(p, ds);
//注意DirectoryStream.Filter这个是没有迭代器可以遍历,必须通过DirectoryStream来遍历
for(Path p1:ds1) {
System.out.println(p1.getFileName());
}
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}finally {
if(ds1!=null) {
try {
ds1.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
读写对象OutputObject、InputObject
先创建对象
public class Test77 implements Serializable {
private String name;
private int age;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public Test77(String name, int age) {
super();
this.name = name;
this.age = age;
}
@Override
public String toString() {
// TODO Auto-generated method stub
return name + " " + age;
}
}
//需要注意的事,对象创建一定要实现Serializable,不然后面写入,会报异常,没有实现该接口。
public void writeObject() {
ObjectOutputStream oos = null;
try {
oos = new ObjectOutputStream(new FileOutputStream(new File("c:\\student.dat")));
Test77 t = new Test77("laoqiang", 12);
oos.writeObject(t);//这里需要注意一个一个对象的写。
Test77 t1 = new Test77("laoqiang1", 22);
oos.writeObject(t1);
} catch (FileNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}finally {
if(oos!=null) {
try {
oos.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
public void readObject() {
ObjectInputStream ois = null;
try {
ois =new ObjectInputStream(new FileInputStream(new File("c:\\student.dat")));
for(int i = 0;i<2;i++) {
Object o = ois.readObject();
System.out.println(o.toString());
}
} catch (FileNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (ClassNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
finally {
if(ois!=null) {
try {
ois.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
I/O数据处理
在Java很多时候,还是注重的是数据的处理,数据很多都是以字节数组的形式出现,为了提高读写效率,有了缓冲区,其实缓冲区就是字节数组。数据以原始的二进制传输是最节省带宽。ByteArrayOutputStream、ByteArratInputStream只处理二进制数据。而DataOutputStream和DataInputStream是主要针对数据对象类。
ByteArrayOutputStream、ByteArratInputStream内部自带缓冲区的。
DataOutputStream和DataInputStream是成对使用的,如果用其他流写进的数据是不可以用DataInputStream
读取。
DataOutputStream和DataInputStream:数据输入、输出流允许应用程序以与机器无关方式从底层输入、输出流中读取基本 Java 数据类型。
特别注意,使用DataOutputStream和DataInputStream,以什么顺序写入,就必须按照什么顺序读取。否测会出现乱码或者异常。
FileOutputStream fos = null;
try {
fos = new FileOutputStream(new File("c:\\java.txt"));
} catch (FileNotFoundException e2) {
// TODO Auto-generated catch block
e2.printStackTrace();
}
DataOutputStream dos = new DataOutputStream(fos);
try {
dos.writeInt(12);
dos.writeDouble(2.909089);
dos.writeLong(124566778);
dos.writeUTF("你好");
dos.writeByte(97);
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
FileInputStream fis = null;
try {
fis = new FileInputStream(new File("c:\\java.txt"));
} catch (FileNotFoundException e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
}
DataInputStream dis = new DataInputStream(fis);
try {
System.out.println(dis.readInt());
System.out.println((Double)dis.readDouble());
long l = dis.readLong();
System.out.println(l);
System.out.println((String)dis.readUTF());
System.out.println((Byte)dis.readByte());
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
PushbackInputStream
对于这个流,大家可能接触不是太多。它主要在读取的时候,添加一个功能就是取消读取,将字节推回到缓冲区中。而这就给了我们的机会,去对想要处理的字节和不想要处理的字节,提供了方便,话不多少,下面两个例子:
public static void main(String[] args) {
//我们的需求就是在输入eclise控制台输入字节,用*代替你的字节中的.并输出
PushbackInputStream pis= new PushbackInputStream(System.in,3);
char c = 0;
char c1 = 0;
try {
while((c = (char) pis.read())!='q') {
//www.bai.com
System.out.print(c);
if((c1=(char) pis.read())=='.') {//这里的读,是下一个字节的读取
System.out.print("*");
}else {
pis.unread(c1);//如果不是你想要的推回到缓冲区,下次开始读的还是这个推回的字节
}
}
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
/*
* 这个例子,就是键盘录入字节,如果小数点后面联系两个零,就换成**。
*/
char c = 0;
char c1 = 0;
char c2 = 0;
PushbackInputStream pis= new PushbackInputStream(System.in,3);
try {
while((c= (char) pis.read())!='e') {
if(c=='.') {
System.out.print(".");
if((c1=(char) pis.read())=='0') {
if((c2=(char) pis.read())=='0') {
System.out.println("**");
}else {
pis.unread(c2);
pis.unread(c1);
}
}else {
pis.unread(c1);
}
}else {
System.out.print(c);
}
}
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
//在这个例子中,我们需要注意,就是unread方法只能推回1个字节,如果要推回几个,需要在PushbackInputStream 指定回退缓冲区size,否则会报出io异常。
Vector
在这里补充将一个知识,之前也没有怎么使用。Vector中文意思:向量。它和数组一样都是用来存储。但是不同的是,它可以动态自增或者缩减长度。下面介绍它的简单使用:
在默认情况下:Vector初始化的时候size是0,这个是表示Vector的元素个数,一开始你没有,当你增加的话,这个数会跟着变化。
构造函数
Vector() 构造一个空向量,使其内部数据数组的大小(初始容量)为 10,其标准容量增量为零。
Vector(int initialCapacity, int capacityIncrement) 使用指定的初始容量(比如是100,就是到了100的时候才自动增量)和容量增量构造一个空的向量。
- capacityIncrement 容量增量
- setsize()可以用来设置Vector中的容量,在这里需要说明就是你用add(int index,E e)如果index超过size()方法值,会报错的。
- capacity() 这个是获取容量的值
- 在构造函数中,如果自己设置增量,那么当数组不够的时候就可以进行按设定的增量加,默认标准增量10.
SequenceInputStream
这个输入流提供了逻辑连接,可以把多个输入流合并起来,转化为单个输入流。下面举的例子,就是通过从多个文件中获取流,通过转化为单个流,然后写到一个文件中。
Enumeration其实是多个条目的索引列表,有兴趣的可以自己看,这里只是简单使用。
static Vector filename = new Vector();
static Vector fileininputstream = new Vector();
public static void main(String[] args) {
getFilesName();
getFilesInputStream();
WriteToFile();
}
/*
* 将多个文件流写到一个文件中
*/
private static void WriteToFile() {
// TODO Auto-generated method stub
try {
OutputStream out = new FileOutputStream("c:\\hello8.txt");
SequenceInputStream sis = new SequenceInputStream(fileininputstream.elements());
byte[] b = new byte[4096];
int len =0;
while((len=sis.read(b))!=-1) {
out.write(b, 0, len);
}
System.out.println(b.length);
} catch (FileNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
/*
* 获取对应的多个文件输入流
*/
private static void getFilesInputStream() {
// TODO Auto-generated method stub
Enumeration e = filename.elements();
while(e.hasMoreElements()) {
InputStream in = null;
try {
in= new FileInputStream((String)e.nextElement());
} catch (FileNotFoundException e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
}
fileininputstream.addElement(in);
}
}
/*
* 用来从键盘获取文件名
*/
private static void getFilesName() {
// TODO Auto-generated method stub
String content = null;
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
try {
while((content=br.readLine())!=null) {
if(content.equals("over")) {
break;
}
System.out.println("每次添加的文件名"+content);
filename.add("c:\\"+content+".txt");
}
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
tip
Java的字符集是采用的是unioncode编码,一个字符是占两个字节。
PrintStream
PrintStream 为其他输出流添加了功能,使它们能够方便地打印各种数据值表示形式,打印的所有字符都使用平台的默认字符编码转换为字节
String s = "laoqiang";
try {
PrintStream ps = new PrintStream("f:\\testjava\\test1.txt");
ps.print('a');
ps.print(12);
ps.print("laoqiang");
ps.print(true);
ps.printf("你好%s",s);//这个就类似c语言格式化输出列表
boolean b= ps.checkError();//PrintStream 永远不会抛出 IOException;而是,
//异常情况仅设置可通过 checkError 方法测试的内部标志
System.out.println(b);
ps.flush();
CharArrayWriter
此类实现一个可用作 Writer 的字符缓冲区。缓冲区会随向流中写入数据而自动增长。
CharArrayWriter caw = new CharArrayWriter(50);
System.out.println(caw.size());
caw.write(97);
try {
caw.write("laoqiang");
String s = caw.toString();//将输出流中的数据转化为字符串。
System.out.println(s);
char[] c = caw.toCharArray();//将输出流中的数据转变成字符数组
for(char c1:c) {
System.out.println(c1);
}
//上面这些只是将东西写到缓冲区中,并不是真正的写在文件上,那么这个存在的
//意义是,通过其他的流去封装,提高效率。
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println(caw.size());
//通过上面测试我们发现size打印出来的并不是我们指定的
//缓冲区的大小,而是缓冲区中实际的字符个数