第27章 java I/O输入输出流
java I/O输入输出流
1.编码问题
import java.io.UnsupportedEncodingException;
/**
* java涉及的编码
*/
public class EncodeDemo {
public static void main(String[] args) throws UnsupportedEncodingException {
String s = "百度ABC";
//字符串转化为byte,使用getBytes方法
//转换字节序列用的是项目默认的编码utf-8
byte[] byte1 = s.getBytes();
for (byte b : byte1) {
//把字节以16进制的方式显示
//utf-8编码中文占用3个字节,英文占用1个字节
System.out.print(Integer.toHexString(b & 0xff)+" ");//e7 99 be e5 ba a6 41 42 43
}
System.out.println();
//显式的重新设置编码
byte[] byte2 = s.getBytes("gbk");
for (byte b : byte2){
//gbk编码中文占用2个字节,英文占用1个字节
System.out.print(Integer.toHexString(b & 0xff)+" ");//b0 d9 b6 c8 41 42 43
}
//java是双字节编码:utf-16be
System.out.println();
//显式的重新设置编码
byte[] byte3 = s.getBytes("utf-16be");
for (byte b : byte3){
//utf-16be编码中文占用2个字节,英文占用2个字节
System.out.print(Integer.toHexString(b & 0xff)+" ");//b0 d9 b6 c8 41 42 43
}
/**
* 当你的字节序列是用某种编码的,这个时候想把字节序列变成字符串
* 也需要用这种编码方式,否则会出现乱码
*/
String str1 = new String(byte3);
System.out.println(str1);//v~^� A B C
//String可以指定编码格式
String str2 = new String(byte3,"utf-16be");
System.out.println(str2);//百度ABC
/**
* 文本文件 就是字节序列
* 可以是任意编码的字节序列
* 如果我们在中文机器上直接创建文本文件,那么该文本文件只认识ansi编码
*/
}
}
2.File类的使用
java.io.File类用于表示文件(目录)
File类只用于表示文件(目录)的信息(名称,大小等),但不不能用于文件内容的访问
2.1.File类常用API介绍
import java.io.File;
import java.io.IOException;
/**
* 测试File类的方法
*/
public class FileDemo {
public static void main(String[] args) throws IOException {
//了解构造函数的情况
//创建一个File实例,构造方法
File file = new File("/home/cenyu/test");
File file1 = new File("/home/cenyu/note");
File file2 = new File("/home/cenyu","note");
//exists()检查文件是否存在
System.out.println(file.exists());
//mkdir()创建指定目录
if (!file.exists()) {
file.mkdir();
}else {
//delete删除路径目录
file.delete();
}
//文件
if (!file1.exists()){
//创建一个文件
file1.createNewFile();
}else {
//也可删除文件
file1.delete();
}
//是否是一个目录
System.out.println(file.isDirectory());
//是否是一个文件
System.out.println(file.isFile());
//常用的File对象的API
System.out.println(file);//file.toString()的内容,这里显示的是目录名
//显示抽象文件路径名
System.out.println(file.getAbsolutePath());
//获取文件名字
System.out.println(file.getName());
//返回父目录路径
System.out.println(file.getParent());
}
}
2.1.File类方法
遍历
import java.io.File;
/**
* 列出File类的一些常用操作
* 过滤,遍历等
*/
public class FileUtils {
/**
* 列出指定目录下(包括其子目录)的所有文件
*/
public static void listDirectory(File dir) throws IllegalAccessException {
if (!dir.exists()){
throw new IllegalAccessException("目录:"+dir+"不存在");
}
if (!dir.isDirectory()){
throw new IllegalAccessException(dir+"不是目录");
}
//此处只能遍历指定目录下的一层目录
/* String[] filnames = dir.list();//返回的是字符串数组,直接是子目录的名称,没有子目录下的内容
for (String string : filnames){
System.out.println(string);
}
*/
//如果需要遍历目录下的内容,就需要构造成File对象做递归操作,File提供了直接返回File对象的API
//此处遍历指定目录下的所有层级的文件
File[] files = dir.listFiles();//返回的是直接子目录(文件)的抽象
if (files!=null && files.length>0){
for (File file : files){
if (file.isDirectory()){
//递归
listDirectory(file);
}else {
System.out.println(file);
}
}
}
}
//测试
public static void main(String[] args) throws IllegalAccessException {
FileUtils.listDirectory(new File("/home/cenyu/"));
}
}
3.RandomAccessFile的使用
RandomAccessFile java提供的对文件内容的访问,既可以读文件,也可以写文件
RandomAccessFile支持随机访问文件,可以访问文件的任意位置
java文件的一些知识
1.java文件模型:
在硬盘上的文件是byte byte byte存储的,是数据的集合
2.打开文件:
有两种模式"rw"(读写),“人”(只读),在创建RandomAccessFile实例的时候,需要指定使用什么方法来打开文件
RandomAccessFile raf = new RandomAccessFile(file, "rw")
文件中存在文件指针,在打开文件时指针在开头,pointer=0;在读和写的过程中,这个指针会乡下移动
3.写方法
raf.write(int)--->只写一个字节(后8位),这个字节是最后一个字节,所以如果要写好几个字节的字符,需要分别写进去,可以使用移位的方法,同时指针指向下一个位置,准备再次写入
4.读方法
int b = raf.read()--->读一个字节
5.文件写完以后一定要关闭
如果不关闭,会产生不可预料的问题
import java.io.File;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.util.Arrays;
/**
* RandomAccessFile类对文件的读写操作
*/
public class RafDemo {
public static void main(String[] args) throws IOException {
//创建一个目录,如果目录不存在,就创建这个目录
File demo = new File("demo");//如果不指定位置,就默认是当前目录下
if (!demo.exists()){
demo.mkdir();
}
//创在demo下的一个文件,如果文件不存在,就创建这个文件
File file = new File(demo,"raf.dat");
if (!file.exists()){
file.createNewFile();
}
//
RandomAccessFile raf = new RandomAccessFile(file,"rw");
//指针位置
System.out.println(raf.getFilePointer());//0
raf.write('A');//只写了一个字节
System.out.println(raf.getFilePointer());//1,已经写了一个字节
raf.write('B');
System.out.println(raf.getFilePointer());//2,已经写了两个字节
int i = 0x7fffffff;
//用write方法每次只能写一个字节,如果把i写进去就得写4次
raf.write(i>>>24);//高8位
raf.write(i>>>14);
raf.write(i>>>8);
raf.write(i);//低8位
System.out.println(raf.getFilePointer());//6
//可以直接使用writInt方法,一次性把找个int写进去
raf.writeInt(i);//其实底层调用的还是上面的移位方法
//写入一个字符
String s = "中";
byte[] gbk = s.getBytes("gbk");
raf.write(gbk);
System.out.println(raf.getFilePointer());//12
System.out.println(raf.length());//12
//读文件,必须把指针移到头部
raf.seek(0);//设置文件指针位置
//一次性读取,把文件中的内容都读到字节数组中
byte[] buf = new byte[(int)raf.length()];
raf.read(buf);//不加buf参数的话,一次只能读取一个
System.out.println(Arrays.toString(buf));//[65, 66, 127, -1, -1, -1, 127, -1, -1, -1, -42, -48]
String s1 = new String(buf);
//此处乱码的原因是我们的s使用的是gdk编码格式,跟其他内容的格式是不一样。
System.out.println(s1);//AB��������
}
}
4.字节流的使用
IO流作为内容的传输作用,分为输入流和输出流,输入流就是程序获取了信息,输出流就是程序向外传送了信息
传输方式一般有字节流和字符流
1.字节流
1.有两个抽象的父类,分别是:
InputStream:抽象了应用程序读取(获得)数据的方法
OutputStream:抽象了应用程序写出(传出)数据的方法
2.数据读取到结尾的时候就是EOF=End
,也是读到-1.这两个就代表读到了结尾
3.输入流基本方法:
int b = in.read();读取一个字节(byte类型),无符号填充到int低八位,-1是EOF
in.read(byte[] buf)读取数据填充到字节数组buf
in.read(byte[] buf, int start, int size)读取数据到字节数组buf,从buf的start位置开始存放size长度的数据
4.输出流基本方法
out.read(int b)写出一个byte到流,写的是b的低8位,而int是4个字节的
out.read(byte[] buf)将buf字节数组都写入到流
out.read(byte[] buf, int start, int size)字节数组buf从start位置开始写size长度的字节到流
5.FileInputStream--->集成InputStream,具体实现了文件上的读取数据
注意:读取的时候单字节读取不适合读取大文件,大文件可以使用批量读取,效率更高
使用输入流读取数据实例:
import java.io.FileInputStream;
import java.io.IOException;
/**
* 读取文件内容的两种不同方法
*/
public class IOUtil {
/**
* 读取指定文件内容,按照16进制输出到控制台
* 并且每输出10个byte换行
* @param fileName
*/
//单字节读取
public static void printHex(String fileName) throws IOException {
//把文件作为字节流操作
FileInputStream in = new FileInputStream(fileName);
int b;
int i =1;
while ((b=in.read())!=-1){
if (b<0xf){
//如果是一位,单位数前面补0
System.out.print("0");
}
System.out.print(Integer.toHexString(b)+" ");
if (i++%10==0){
System.out.println();
}
}
in.close();
}
//批量读取
public static void printHexByByteArray(String fileName) throws IOException {
FileInputStream in = new FileInputStream(fileName);
byte[] buf = new byte[20*1024];
/**
* 从in中批量读取字节,放入到buf这个字节数组中,
* 从第0个位置开始放,最多放buf.length个
* 返回的是多的字节的个数
*/
/* int bytes = in.read(buf, 0, buf.length);//一次性读完,此要求字节数组足够大
int j=1;
for (int i = 0; i < bytes; i++) {
if (buf[i]<=0xf){
System.out.print("0");
}
System.out.print(Integer.toHexString(buf[i])+" ");
if (j++%10==0){
System.out.println();
}
}
in.close();
*/
//上面的方法需要字节数组足够大,如果文件草果字节数组比较麻烦,所以一般更推荐使用下面的方法
int bytes = 0;
int j = 1;
while ((bytes = in.read(buf, 0, buf.length))!=-1){
for (int i = 0; i < bytes; i++) {
if (buf[i]<=0xf){
System.out.print("0");
}
//byte类型8位,int类型32位,为了避免数据转换错误,通过&0xff将高24位清零
System.out.print(Integer.toHexString(buf[i] & 0xff)+" ");
if (j++%10==0){
System.out.println();
}
}
}
}
public static void main(String[] args) {
try {
IOUtil.printHex("/home/cenyu/aa");
} catch (IOException e) {
e.printStackTrace();
}
try {
IOUtil.printHexByByteArray("/home/cenyu/aa");
} catch (IOException e) {
e.printStackTrace();
}
}
}
6.FileOutStream--->继承OutputStream 实现了向文件中写入byte数据的方法
mport java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
/**
* 测试FileOutputStream类写入文件
*/
public class FileOutDemo {
public static void main(String[] args) throws IOException {
//如果该文件不存在,则直接创建该文件。如果文件存在,要看后面有没有true参数,
//如果没有true参数,则删除文件后再重新创建,如果有true参数,则在文件后追加内容
FileOutputStream out = new FileOutputStream("demo/out.dat",true);
out.write('A');//写出了'A'的低八位
out.write('B');//写出了'B'的低八位
int a =10;//write只能写八位,那么写一个int整数,需要写4此,每次8位
out.write(a>>>24);
out.write(a>>>16);
out.write(a>>>8);
out.write(a);
//写字符串
byte[] gdk = "中国".getBytes("gbk");
out.write(gdk);
out.close();
//测试copyFile
FileOutDemo.copyFile(new File("/home/cenyu/aa"), new File("/home/cenyu/bb"));
}
//copy源文件
public static void copyFile(File srcFile, File destFile) throws IOException{
if (!srcFile.exists()){
throw new IllegalArgumentException("文件:"+srcFile+"不存在");
}
if (!srcFile.isFile()){
throw new IllegalArgumentException(srcFile+"不是文件");
}
FileInputStream in = new FileInputStream(srcFile);
FileOutputStream out = new FileOutputStream(destFile);
byte[] buf = new byte[8*1024];
int b;
while ((b=in.read(buf, 0, buf.length))!=-1){
out.write(buf, 0, b);
out.flush();
}
in.close();
out.close();
}
}
7.DataOutputStream/DataInputStream类是对“流”功能的扩展,可以更加方便读取int,long,字符等类型数据
DataOutputStream:
writeInt()/writeDouble()/writeUTF()
写入代码示例:
public class DosDemo {
public static void main(String[] args) throws IOException {
String file = "demo/dos.dat";
DataOutputStream dos = new DataOutputStream(new FileOutputStream(file));
dos.writeInt(10);
dos.writeInt(-10);
dos.writeLong(10L);
dos.writeDouble(10.5);
//采用utf-8编码写出
dos.writeUTF("中国");
//采用utf-16编码写出
dos.writeChars("中国");
dos.close();
IOUtil.printHex(file);
}
}
读取代码示例:
public class DisDemo {
public static void main(String[] args) throws IOException {
String file = "demo/dos.dat";
DataInputStream dis = new DataInputStream(new FileInputStream(file));
int i = dis.readInt();
System.out.println(i);
i = dis.readInt();
System.out.println(i);//10
long l = dis.readLong();
System.out.println(l);//-10
double d = dis.readDouble();//10
System.out.println(d);//10.5
String s = dis.readUTF();
System.out.println(s);//中国
dis.close();
}
}
8.BufferedInputStream / BufferedOutputStream
这两个流类为IO提供了缓冲区的操作,一般打开文件进行写入或者读取操作,都会加上缓冲,这种流模式提高了IO的性能
从应用程序中把输入放入文件,就相当于将一缸水倒入另一个缸中:
FileOutputStream--->write()方法相当于一滴一滴把水“转移”过去
DataOutputStream--->writeXXX()方法会更方便一些,相当于一瓢一瓢把水“转移”过去
BufferedOutputStream--->write()方法更方便,相当于一瓢一瓢把水先放入桶中,再从桶中倒入到另一个缸里。
//进行文件的拷贝,利用带缓冲的字节流
public static void copyFileByBuffer(File srcFile, File destFile) throws IOException {
if (!srcFile.exists()){
throw new IllegalArgumentException("文件:"+srcFile+"不存在");
}
if (!srcFile.isFile()){
throw new IllegalArgumentException(srcFile+"不是文件");
}
BufferedInputStream bis = new BufferedInputStream(new FileInputStream(srcFile));
BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(destFile));
int c;
while ((c = bis.read())!=-1){
bos.write(c);
bos.flush();//刷新缓冲区
}
bis.close();
bos.close();
}
5.字符流的使用
1.编码的问题上面讲过了
2.认识文本和文本文件
java中的文本(Char)是一个16位无符号整数,是字符unicode(双字节)编码。就是说java中的文本就是char,而这里的char表示unicode中的所有能表示的单个字符,注意必须是单个的。而在java的语法中,定义一个char的时候必须用单引号来引起来,不能用双引号
文件是byte byte byte ……的数字序列
文本文件是文本(char)序列按照某种编码方法(utf-8, utf-16be, gbk)序列化为byte的存储结果。
3.字符流也分为输出流(Write)抽象类和输入流(Read)抽象类,都是操作文本文件,文本文件是把文本(char)按照一定的编码序列化成byte
字符的处理,一次处理一个字符
字符的底层依然是基本的字节序列
1.字符流的基本实现:
InputStreamReader 完成byte流解析为char流,按照编码解析
OutputStreamReader 提供char流到byte流,按照编码处理
此类主要对文件内的文本进行操作
/**
* 字符流的读(InputStreamReader)和写操作(OutputStreamReader)
*/
public class IsrAndOswDemo {
public static void main(String[] args) throws IOException {
FileInputStream in = new FileInputStream("demo/dos.dat");
InputStreamReader isr = new InputStreamReader(in, "utf-8");//utf-8是当前调用文件的默认编码,如果以后遇到不是这个编码的,要在这里修改
//单个读取
int c;
while ((c=isr.read())!=-1){
//char类型是一个16位的无符号整数,代表一个单个的字符
System.out.print((char) c);//强制转换为char来打印,结果就是字符
}
//按照char[]数组大小批量读取
char[] buffer = new char[8*1024];
int c1;
//批量读取,放入buffer这个字符数组,从第0个位置开始放置,最多放buffer.length个字符
while ((c1 = isr.read(buffer,0,buffer.length))!=-1){
String s = new String(buffer,0,c1);
System.out.print(s);
}
//写操作
FileOutputStream out = new FileOutputStream("demo/osw.dat");
OutputStreamWriter osw = new OutputStreamWriter(out, "utf-8");//调用文件的编码方式
osw.write("aaa11");
osw.flush();
isr.close();
osw.close();
}
}
字符流的第二种操作方式FileReader和FileWriter
这一类重要是对文件进行操作
/**
* 字符流的第二种操作方式FileReader和FileWriter
*/
public class FrAndFwDemo {
public static void main(String[] args) throws IOException {
//实例化,不可指定编码格式
FileReader fr = new FileReader("demo/dos.dat");
//有true是对文件追加内容,没有true,则直接覆盖
FileWriter fw = new FileWriter("demo/osw.dat",true);
char[] buffer = new char[2056];
int c;
while ((c = fr.read(buffer, 0, buffer.length))!=-1) {
fw.write(buffer, 0, c);
fw.flush();
}
fr.close();
fw.close();
}
}
字符流的过滤器
BufferReader --->readLine 一次读一行
BufferWriter/PrintWriter --->写一行
/**
* 字符流的过滤器
*/
public class BrAndBwPwDemo {
public static void main(String[] args) throws IOException {
//对文件进行读写操作
BufferedReader br = new BufferedReader(new InputStreamReader(new FileInputStream("demo/dos.dat")));
BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(new FileOutputStream("demo/out.dat")));
String line;
while ((line=br.readLine())!=null){
System.out.println(line);//一次读一行,并不能识别换行符
bw.write(line);//一次写一行,但是不会自动换行
bw.newLine();//换行操作
bw.flush();
}
br.close();
bw.close();
}
}
6.对象的序列化和反序列化
对象序列化:就是将Object转换成byte序列,反之叫对象的反序列化
序列化流(ObjectOutputStream),是过滤流---witeObject
反序列化流(ObjectInputStream)--->read Object
对象要进行序列化有一个接口(Serializable)
对象必须实现序列化接口,才能进行序列化,否则将出现异常,这个接口,没有任何方法,只是一个标准
序列号化和反序列号的操作
创建一个Student类,并实现Serializable接口
transient关键字
transient关键字限定这个元素不会进行JVM的序列号操作。但是并不是不能再对这个元素进行序列化,而是可以通过自己来做序列化
public class Student implements Serializable{
private String stuno;
private String stuname;
private transient int stuage;//transient关键字限定这个元素不会进行JVM的序列号操作
public Student(String stuno, String stuname, int stuage) {
super();
this.stuno = stuno;
this.stuname = stuname;
this.stuage = stuage;
}
public void setStuno(String stuno) {
this.stuno = stuno;
}
public String getStuno() {
return stuno;
}
public void setStuname(String stuname) {
this.stuname = stuname;
}
public String getStuname() {
return stuname;
}
public void setStuage(int stuage) {
this.stuage = stuage;
}
public int getStuage() {
return stuage;
}
@Override
public String toString() {
return "Student[stuno="+stuno+", stuname="+stuname+",stuage="+stuage+"]";
}
}
序列化和反序列化操作
public class ObjectSeriaDemo1 {
public static void main(String[] args) throws IOException, ClassNotFoundException {
String file = "demo/obj11.dat";
//1.对象序列化
/*
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream(file));
Student stu = new Student("10001","张三", 20);
oos.writeObject(stu);
oos.flush();
oos.close();
*/
//2.反序列化操作
ObjectInputStream ois = new ObjectInputStream(new FileInputStream(file));
Student stu = (Student)ois.readObject();
System.out.println(stu);
ois.close();
}
}
作者:岑宇 出处:http://www.cnblogs.com/cenyu/ 本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。 如果文中有什么错误,欢迎指出。以免更多的人被误导。 |
>
|