一、编码
- GBK编码:中文占用2个字节,英文占用1个字节
- UTF-8编码:中文占3个字节,英文占用1个字节
- UTF-16be编码:中文占2个字节,英文占用2个字节
- 当你的字节序列是某种编码时,这个时候想把字节序列变成字符串,也需要用这种编码方式,否则会出现乱码。
- 文本文件里面放的时字节序列,可以是任意编码的字节序列
如果我们在中文机器上直接创建文本文件,那么该文本文件只认识ansi编码
二、File
- File
只用于存储文件对应的信息(名称大小之类),不能用于文件内容的访问
File常用方法:
方法 | 说明 |
---|---|
File.createNewfile | 创建文本 |
File.mkdir | 创建目录 |
File.tostring | 方法打印文件全路径名称 |
File.list | 列出文件直接子目录或文件的名字 |
File.listFile | 列出直接子File对象 |
/**
* 递归打印文件夹下的文件
*/
public static void printFiles(File path){
File[] files = path.listFiles();
for(File file: files){
if(file.isDirectory()){
printFiles(file);
}else{
System.out.println(file);
}
}
}
- RandomAccessFile
RandomAccessFile提供对文件内容的访问,既可以读文件,也可以写文件;
支持随机访问文件,可以访问文件的任意位置
2.1 java文件模型
在硬盘上的文件是byte byte byte存储的,是数据的集合
2.2 打开文件
有两种模式“rw”(读写)“r”(只读)
RandomAccessFile raf = new RandomAccessFile(file,"rw");
文件指针,打开文件时指针在开头pointer = 0;
2.3 写方法
raf.write(int)--->只写一个字节(后8位),同时指针指向下一个位置,准备再次写入
2.4 读方法
int b = raf.read();--->读一个字节,然后把字节转换成整数
2.5 文件读写完成以后一定要关闭(Oracle官方说明)
import java.io.File;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.util.Arrays;
public class RafDemo {
public static void main(String[] args) throws IOException {
File demo = new File("demo");
if(!demo.exists()){
demo.mkdir();
}
File file = new File("demo","raf.dat");
if(!file.exists()){
file.createNewFile();
}
RandomAccessFile ra = new RandomAccessFile(file,"rw");
ra.write('A');//写出了A的低八位
System.out.println(ra.getFilePointer());
ra.write('B');//写出了B的低八位
System.out.println(ra.getFilePointer());
int i = 0x7fffffff;//write只能写八位,写一个int需要4次,每次八位
ra.write(i >>> 24);
ra.write(i >>> 16);
ra.write(i >>> 8);
ra.write(i);
System.out.println(ra.getFilePointer());
//直接写int
ra.writeInt(i);
String s = "中";
byte[] b = s.getBytes("gbk");
ra.write(b);
System.out.println(ra.getFilePointer());
int biggest = 2000;//11111010000
ra.write(biggest);//如果不做位移操作,只能写进低8位,11010000
System.out.println(ra.getFilePointer());
ra.seek(0);//移到文件的开始
byte[] buf = new byte[(int) ra.length()];
ra.read(buf);//读到字节数组里
System.out.println(Arrays.toString(buf));
//[65, 66, 127, -1, -1, -1, 127, -1, -1, -1, -42, -48, -48]
for(byte by:buf){
System.out.print(Integer.toHexString(by & 0xff) + " ");
//41 42 7f ff ff ff 7f ff ff ff d6 d0 d0
}
ra.close();
}
}
三、字节流
- InputStream、 OutPutStream
InputStream 抽象类,抽象了应用程序读取数据的方式
OutPutStream 抽象类,抽象了应用程序写出数据的方式 - EOF = End of File = -1,读到-1就结束
- 输入流基本方法
int b = in.read() 读取一个字节无符号填充到int的低8位
in.read(byte[] buf]
in.read(byte[] buf,int start, int size] - 输出流基本方法
out.write(int b) 写出一个byte到流,b的低8位
out.write(byte[] buf) 将buf字节数组写入到流
out.wrrite(byte[] buf,int start, int size) 字节数组buf从start位置开始写size长度的字节到流 - FileInputStream/FileOutputStream
实现类,具体实现了在文件上读写byte数据;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
public class IOUtil {
/**
* 打印文件内容
* @param fileName 文件路径
* @throws IOException
*/
public static void fileRead(String fileName) throws IOException{
FileInputStream in = new FileInputStream(new File(fileName));
byte[] buf = new byte[24*1024];
int bytes = 0;
//in.read(buf,0,buf.length)) 返回读入的总字节数,如果到文件尾,返回-1
//0表示写入数组的起点位置,buf.length表示写入数组的结束位置
while((bytes = in.read(buf,0,buf.length)) != -1){
for(int i =0;i < bytes; i++){
//System.out.print(buf[i] + " "); //打印出来是十进制数字
//65 66 127 -1 -1 -1 127 -1 -1 -1 -42 -48 -48
System.out.print(Integer.toHexString(buf[i] & 0xff) + " ");
//byte是8位,int是32位,为避免数据转换错误,通过& 0xff将高24位清0
//41 42 7f ff ff ff 7f ff ff ff d6 d0 d0
}
}
}
public static void main(String[] args) throws IOException {
fileRead("demo\\raf.dat");
}
}
- DataInputStream/DataOutputStream(装饰模式)
对流功能的扩展,可以更加方便的读取int、long、字符等类型数据
DataInputStream ips=new DataInputStream (文件输出流(FileInputStream));
DataOutputStream ops=new DataIOutputStream (文件输入流(FileOutputStream));
ips.readInt/readDouble/readLong,readUTF //以int/double/long等方式读取文件内容
ops.writeInt/writeDouble/writeLong,writeUTF //以int/double/long等方式向文件内写入数据
- BufferedInputStream & BufferedOutputStream
提供了带缓存区的操作,一般打开文件进行写入或读取操作时,都会加上缓冲,这种流模式提高了IO的性能 - 从应用程序中把输入放入文件,相当于将一罐水倒入到另外一个罐中:
FileOutputStream-->write()方法相当于一滴一滴地把水“转移”过去
DataOutputStream-->writeXxx()方法会方便一些,相当于一瓢一瓢把水“转移”
BuffereOutputStream-->write()方法更方便,相当于一瓢一瓢先放入 桶中,在从桶中倒入到另外一个罐中 - 文件拷贝方式对比:
单字节读取
字节批量读取(定一个数组大小)
字节流带缓冲区读取
/**
* 单字节读取
* @param src
* @param dest
*/
public static void copyFileByByte(File src, File dest) throws Exception {
if(!src.exists()){
throw new IllegalArgumentException("src不存在");
}
if(!src.isFile()){
throw new IllegalArgumentException("src不是文件");
}
FileInputStream input = new FileInputStream(src);
FileOutputStream output = new FileOutputStream(dest,true);
int data;
while((data = input.read()) != -1){
output.write(data);
}
output.flush();
input.close();
output.close();
}
/**
* 字节批量拷贝文件
* @param src
* @param dest
*/
public static void copyFileByBatch(File src, File dest) throws Exception {
if(!src.exists()){
throw new IllegalArgumentException("src不存在");
}
if(!src.isFile()){
throw new IllegalArgumentException("src不是文件");
}
FileInputStream input = new FileInputStream(src);
FileOutputStream output = new FileOutputStream(dest,true);
byte buf[] = new byte[8 * 1024];
int length;
while((length = input.read(buf, 0, buf.length)) != -1){
output.write(buf);
}
output.flush();
input.close();
output.close();
}
/**
* 利用字节缓冲流拷贝文件
* @param src
* @param dest
*/
public static void copyFileByBuffer(File src, File dest) throws Exception{
if(!src.exists()){
throw new IllegalArgumentException("src不存在");
}
if(!src.isFile()){
throw new IllegalArgumentException("src不是文件");
}
BufferedInputStream input = new BufferedInputStream(new FileInputStream(src));
BufferedOutputStream output = new BufferedOutputStream(new FileOutputStream(dest,true));
int data;
while((data=input.read())!=-1){
output.write(data);
}
output.flush();
input.close();
output.close();
}
三种拷贝方式测试结果:
单个字节考本文件,文件大小2381KB,耗时22000ms
批量字节考本文件,文件大小2381KB,设置字节数组8K,耗时39ms,这种方式需要开辟额外的空间
字节缓冲区拷贝文件,文件大小2381KB, 耗时120ms