计算机如何存储中文的?
当前平台默认编码集 :GBK 一个中文两个字节
第一个字节:一定是负数
第二个字节:一般是负数,可能也会是正数,不会影响的结果.
public class StringDemo {
public static void main(String[] args) {
//定义一个字符串
String str = "你好" ;//[-60, -29, -70, -61]
//转成字节数组
byte[] bys = str.getBytes() ;
//输出
System.out.println(Arrays.toString(bys));
}
}
“你好”的第一个字节为负数。
在前面I/O流的学习中,我们知道:一次读取一个字节数组的方式要比一次读取一个字节方式高效.
一次读取一个字节数组,相当于构造一个缓冲区,实际上字节缓冲流比一次读取一个字节数组还要高效,我们接下来就来学习一下字节缓冲流。
1.字节缓冲输出流:
构造方式:
public BufferedOutputStream(OutputStream out):采用的默认的缓冲区大小(足够大了) ,来构造一个字节缓冲输出流对象
public BufferedOutputStream(OutputStream out,int size):指定size缓冲区大小构造缓冲输出流对象
如果 size <= 0,会出现IllegalArgumentException异常
写数据的方式:
一次写一个字节
write(int by)
一次写一个字节数组的一部分
write(byte[] b, int off, int len)
方法:
void flush() ;刷新缓冲区的流
面试题:
字节缓冲输出流它的构造方法为什么不能直接传递路径/文件?
缓冲输入流/缓冲输出流,它只是在底层内部提供一个缓冲区的数组,底层实现文件的复制/读取/写入这些操作都依赖于基本流对象来操作(InputStream/OutputStream/FileInputStream/FileOutputstream)
public class BufferedOutputStreamDemo {
public static void main(String[] args) throws Exception {
//构造一个字节缓冲输出流对象
//public BufferedOutputStream(OutputStream out)
/*OutputStream out = new FileOutputStream("out.txt") ;
BufferedOutputStream bos = new BufferedOutputStream(out) ;*/
//符合Java一种设计模式:装饰者设计模式(过滤器:Filter)
BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream("bos.txt")) ;
//写数据
bos.write("hello".getBytes());
//释放资源
bos.close();
}
}
2.字节缓冲输入流
public BufferedInputStream(InputStream in):默认缓冲区大小构造缓冲输入流对象
public BufferedInputStream(InputStream in,int size):指定缓冲区大小构造缓冲输入流对象
public int read()
public int read(byte[] b,int off,int len)
在使输入流的时候,两种方式读取(一次读取一个字节/一次读取一个字节数在),只能用一种方式,否则,会出现错误!
public class BufferedInputStreamDemo {
public static void main(String[] args) throws Exception {
//构造一个字节缓冲输入流对象
BufferedInputStream bis = new BufferedInputStream(new FileInputStream("bos.txt"));
//读数据
//一次读取一个字节
/*int by = 0 ;
while((by=bis.read())!=-1) {
System.out.print((char)by);
}*/
//一次读取一个字节数组
byte[] bys = new byte[1024] ;
int len = 0 ;
while((len=bis.read(bys))!=-1) {
System.out.println(new String(bys, 0, len));
}
//释放资源
bis.close();
}
}
接下来我们就来操作一个视频文件,来测试速度问题
public class Test {
public static void main(String[] args) throws Exception {
long start = System.currentTimeMillis() ;
// method1("e:\\abc.mp4","copy1.mp4") ;
// method2("e:\\abc.mp4","copy2.mp4") ;
// method3("e:\\abc.mp4","copy3.mp4") ;
method4("e:\\abc.mp4","copy4.mp4") ;
long end = System.currentTimeMillis() ;
System.out.println("共耗时:"+(end-start)+"毫秒");
}
//高效的流一次读取一个字节数组
private static void method4(String src, String dest) throws Exception {
BufferedInputStream bis = new BufferedInputStream(new FileInputStream(src)) ;
BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(dest)) ;
//一次读取一个字节数组
byte[] bys = new byte[1024] ;
int len = 0 ;
while((len=bis.read(bys))!=-1) {
bos.write(bys, 0, len);
}
bis.close();
bos.close();
}
//高效的字节流一次读取一个字节
private static void method3(String src, String dest) throws Exception {
BufferedInputStream bis = new BufferedInputStream(new FileInputStream(src)) ;
BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(dest)) ;
//一次读个字节
int by = 0 ;
while((by=bis.read())!=-1) {
bos.write(by);
}
//释放资源
bis.close();
bos.close();
}
//基本的字节流一次读取一个字节数组
private static void method2(String src, String dest) throws Exception {
//封装文件
FileInputStream fis = new FileInputStream(src) ;
FileOutputStream fos = new FileOutputStream(dest) ;
//读写操作
byte[] bys = new byte[1024] ;//相当于一个缓冲区
int len = 0 ;
while((len=fis.read(bys))!=-1) {
fos.write(bys, 0, len);
}
//释放资源
fis.close();
fos.close();
}
//基本的字节流一次读取一个字节
private static void method1(String src, String dest) throws Exception {
//封装源文件和目标文件
FileInputStream fis = new FileInputStream(src) ;
FileOutputStream fos = new FileOutputStream(dest) ;
//读写操作
int by = 0 ;
while((by=fis.read())!=-1) {
//写
fos.write(by);
}
//释放资源
fis.close();
fos.close();
}
}
逐一运行method1~4方法,(一次只运行一个方法),得出每种方法的运行时间:
基本的字节流一次读取一个字节:共耗时:85772毫秒
基本的字节流一次读取一个字节数组:共耗时:216毫秒
高效的字节流(缓冲流)一次读取一个字节:共耗时:682毫秒
高效的字节流(缓冲流)一次读取一个字节数组:共耗时:49毫秒
可以看出,一次读取一个字节数组的方式要比一次读取一个字节的方式耗时要少,同等读取方式下,缓冲流要比基本的字节流耗时要少。
编码和解码:前后的编码格式要一致!
编码:简单理解:将能看懂的东西--->看不懂的东西
解码:
看不懂的东西---能看懂的东西
举例: 谍战片
今天天气不错...
编码:
今---->字节---->二进制数据
解码:
二进制数据-->字节---->今
编码: 将字符串--->字节数组
public byte[] getBytes() :平台默认编码集(默认的是GBK)
public byte[] getBytes(Charset charset) :指定编码格式
解码: 将字节数组--->字符串
public String(byte[] bytes) :使用平台默认编码集(GBK)
public String(byte[] bytes,Charset charset):用指定的编码格式来解码
public class StringDemo {
public static void main(String[] args) throws Exception {
//定义一个字符串
String str ="你好" ;
//编码和解码:前后必须一致
//编码
//byte[] bys = str.getBytes() ;
byte[] bys = str.getBytes("utf-8") ;
System.out.println(Arrays.toString(bys));//[-60, -29, -70, -61]
System.out.println("------------------");
//解码
//public String(byte[] bytes) :使用平台默认编码集(gbk)
//String s = new String(bys) ;
//public String(byte[] bytes,Charset charset)
String s = new String(bys,"utf-8") ;//一个中文对应三个字节
System.out.println(s);
}
}
使用字节流一次读取一个字节的方式,会造成中文乱码--->Java提供了一个字符流(专门用来解决中文乱码问题)
public class FileInputStreamDemo {
public static void main(String[] args) throws Exception {
//封装文件
//一次读取一个字节的方式
FileInputStream fis = new FileInputStream("a.txt") ;
//读数据
int by = 0 ;
while((by=fis.read())!=-1) {
System.out.print((char)by);
}
//释放资源
fis.close();
}
}
由于a.txt中包含中文,利用此方法读取出来的全是乱码,我们下面来学习一下字符流。
字符流:
字符输入流:Reader
字符输出流:Writer
字符输出流/字符输入流:都是抽象类,使用一个子类:转换流
字符输出流的构造方法
public OutputStreamWriter(OutputStream out):使用默认的编码格式构造一个字符转换输出流对象
public OutputStreamWriter(OutputStream out, Charset cs):使用指定编码格式构造一个字符转换输出流对象
转换流的构成=字节流+编码格式(平台默认/指定)
转换流的对象的创建,格式比较长,非常麻烦,Java--->转换流的便捷类
Reader:抽象类:字符输入流
inputStreamReader(字符转换输入流 :inputStream+编码格式)
便捷类:FileReader,这个类可以直接对文件进行操作
Writer:抽象类:字符输出流outputStreamWriter(字符转换输出流:outputStream+编码格式)
便捷类:FileWriter,这个类可以直接对文件进行操作
public class WriterDemo {
public static void main(String[] args) throws Exception {
//创建一个字符输出流对象
//Writer writer = new OutputStreamWriter(new FileOutputStream("w.txt")) ;//多态
OutputStreamWriter osw =
new OutputStreamWriter(
new FileOutputStream("osw.txt"), "gbk"
);
//写数据
osw.write("中国");
//释放资源
osw.close();
}
}
字符转换输入流:InputStreamReader
InputStreamReader(InputStream in) :构造一个字符转换输入流,默认编码
public InputStreamReader(InputStream in,Charset cs) :构造一个字符转换输入流,指定编码
字符转换输入流=字节流+编码格式
public class ReaderDemo {
public static void main(String[] args) throws Exception{
//需求:要读取当前项目下的osw.txt,将内容输出在控制台上
InputStreamReader isr = new InputStreamReader(new FileInputStream("osw.txt"),"gbk") ;
//读数据
//一次读取一个字符数组
char[] chs = new char[1024] ;
int len = 0 ;
while((len=isr.read(chs))!=-1) {
System.out.println(new String(chs,0,len));
}
//关闭资源
isr.close();
}
}
需求:将a.txt文件中的内容进行复制,复制到当前项目下(b.txt)
文本文件:优先采用字符流
源文件:a.txt---->Reader---->InputStreamReader---->FileReader
目的的文件:b.txt--->Writer-->OutputStreamWriter---->FileWriter
public class CopyDemo {
public static void main(String[] args) throws Exception {
//方式1:转换流的方式
//封装源文件和目的地文件
/*InputStreamReader isr = new InputStreamReader(
new FileInputStream("a.txt")) ;
OutputStreamWriter osw = new OutputStreamWriter(
new FileOutputStream("b.txt")) ;
//复制操作
//一次读取一个字符数组
char[] chs = new char[1024] ;
int len = 0 ;
while((len=isr.read(chs))!=-1) {
//写
osw.write(chs, 0, len);
}
//释放资源
osw.close();
isr.close();
System.out.println("-----------------------");*/
//方式2:使用便捷类进行操作
FileReader fr = new FileReader("a.txt") ;
FileWriter fw = new FileWriter("b.txt") ;
//一次读取一个字符数组
char[] chs2 = new char[1024] ;
int len2 = 0 ;
while((len2=fr.read(chs2))!=-1) {
//写
fw.write(chs2, 0, len2);
}
fr.close();
fw.close();
}
}
字符输入流读数据的方法:
int read(char[] chs):读取一个字符数组
int read():读取单个字符
public class ReaderDemo {
public static void main(String[] args) throws Exception {
//读取当前项目下的StringDemo.java文件
FileReader fr = new FileReader("StringDemo.java") ;
//一次读取一个字符
/*int ch = 0 ;
while((ch=fr.read())!=-1) {
System.out.print((char)ch);
}*/
//一次读取一个字符数组
char[] chs = new char[1024] ;
int len = 0 ;
while((len=fr.read(chs))!=-1) {
System.out.println(new String(chs,0,len));
}
//释放资源
fr.close();
}
}
字符输出流写数据的功能:
public void write(int c):写单个字符
public void write(char[] cbuf):写字符数组
public abstract void write(char[] cbuf, int off, int len):写字符数组的一部分
public void write(String str):写字符串
public void write(String str,int off, int len):写字符串的某一部分
flush和close方法的区别?
close:关闭该流,关闭该流对象以及它关联的资源文件,关闭之后,不能再对流对象进行操作了,否则会有异常
flush:刷新该流,为了防止一些文件(图片文件/音频文件),缺失,或者没有加载到流对象中,刷新了该流,还是可以流对象进行操作
字符缓冲输入流/字符缓冲输出流
public class WriterDemo {
public static void main(String[] args) throws Exception {
//创建字符输出流对象
FileWriter fw = new FileWriter("fw.txt") ;
//写一个字符
/*fw.write('a');
//一个字符:对应两个字节
//针对文本文件进行操作,单位:字节
fw.write(97);
//刷新流
fw.flush();
fw.write(98);*/
//写一个字符数组
/*char[] chs = {'a','b','c','d','e'} ;
fw.write(chs);
//写一个字符数组的一部分
fw.write(chs, 1, 3);*/
//写字符串
/*String str = "我爱高圆圆" ;
fw.write(str);*/ //写字符串的方法
fw.write("我爱高圆圆", 0, 3);
fw.flush();
//字符流中的close:关闭该流的同时,之前一定要刷新流
fw.close();
}
}
在字符流中提供了一个更高效的流-->字符缓冲流
字符缓冲输入流字符缓冲输出流
BufferedWriter:文本写入字符输出流,缓冲各个字符,从而提供单个字符、数组和字符串的高效写入
构造方法
BufferedWriter(Writer out) :默认缓冲区大小构造字符缓冲输出流对象
BufferedWriter(Writer out,int size):指定缓冲区大小
public class BufferedWriterdemo {
public static void main(String[] args) throws Exception {
//创建一个字符缓冲输出流对象
BufferedWriter bw = new BufferedWriter(new FileWriter("bw.txt")) ;
//写数据
bw.write("hello");
bw.write("world");
//刷新流
bw.flush();
//关闭资源
bw.close();
}
}
BufferedReader:字符缓冲输入流
构造方法
public BufferedReader(Reader in)创建一个使用默认大小输入缓冲区的缓冲字符输入流。
public BufferedReader(Reader in, int sz)创建一个使用指定大小输入缓冲区的缓冲字符输入流。
需求:读取当前项目下的bw.txt这个文件
public class BufferedReaderDemo {
public static void main(String[] args) throws Exception {
//创建字符缓冲输入流对象
BufferedReader br = new BufferedReader(new FileReader("bw.txt"));
//读数据
//一次读取一个字符数组
char[] chs = new char[1024] ;
int len = 0 ;
while((len=br.read(chs))!=-1) {
System.out.println(new String(chs,0,len));
}
//释放资源
br.close();
}
}
先使用字符缓冲输出流写数据,再使用字符缓冲输入读数据,显示控制台上
字符缓冲输出流:
特有功能:public void newLine():写入一个行的分隔符号
字符缓冲输入流:
特有功能:public String readLine():一次读取一行
public class BufferedDemo {
public static void main(String[] args) throws Exception {
//读数据
BufferedReader br = new BufferedReader(new FileReader("bw2.txt")) ;
//一次读取一行
String line = null ;
while((line=br.readLine())!=null) {
System.out.println(line);
}
//释放资源
br.close();
}
private static void write() throws IOException {
//字符缓冲输出流读数据
BufferedWriter bw = new BufferedWriter(new FileWriter("bw2.txt")) ;
//写数据
for(int x = 0 ; x <10 ; x ++) {
//写数据
bw.write("hello"+x);
//换行
bw.newLine();
//刷新
bw.flush();
}
//关闭流
bw.close();
}
}
需求:使用字符缓冲流进行复制操作
分别使用两种方式
1)一次读取一个字符数组
2)一次读取一行
public class CopyDemo {
public static void main(String[] args) throws Exception {
//封装源文件
BufferedReader br = new BufferedReader(new FileReader("StringDemo.java")) ;
//封装目的地文件
BufferedWriter bw = new BufferedWriter(new FileWriter("copy.java")) ;
//一次读取一个字符数组
/*char[] chs = new char[1024] ;
int len = 0 ;
while((len=br.read(chs))!=-1) {
bw.write(chs, 0, len);
bw.flush();
}*/
//一次读取一行
String line = null ;
while((line=br.readLine())!=null) {
//写
bw.write(line);
bw.newLine();
bw.flush();
}
//关闭资源
bw.close();
br.close();
}
}
复制文本文件(5种方式分别完成)
文本文件:字符流
基本的字符流 一次读取一个字符
一次读取一个字符数组
字符缓冲流 一次读取一个字符
一次读取一个字符数组
一次读取一行
public class CopyTest {
public static void main(String[] args) throws Exception {
// method1("StringDemo.java","copy.java") ;
method2();
}
private static void method2() throws FileNotFoundException, IOException {
BufferedReader br = new BufferedReader(new FileReader("StringDemo.java")) ;
BufferedWriter bw = new BufferedWriter(new FileWriter("copy.java")) ;
//一次读取一行
String line = null;
while((line=br.readLine())!=null) {
bw.write(line);
bw.newLine();
bw.flush();
}
br.close();
bw.close();
}
private static void method1(String src, String dest) throws Exception {
BufferedReader br = new BufferedReader(new FileReader(src)) ;
BufferedWriter bw = new BufferedWriter(new FileWriter(dest)) ;
//字符数组
char[] chs = new char[1024] ;
int len = 0 ;
while((len=br.read(chs))!=-1) {
bw.write(chs, 0, len);
bw.newLine();
bw.flush();
}
br.close();
bw.close();
}
}
需求:有一个文本文本,需要将文本文件中的内容放到ArrayList集合中,遍历集合获取元素
源文件:b.txt----->读取---->BuffferedReader
目的地:ArrayList<String>
public class FileToArrayListTest {
public static void main(String[] args) throws Exception {
//封装源文件
BufferedReader br = new BufferedReader(new FileReader("b.txt")) ;
//创建一个ArrayList集合
ArrayList<String> list = new ArrayList<String>() ;
//读b.txt文件的内容
String line = null ;
while((line=br.readLine())!=null) {
//将数据添加到集合中
list.add(line) ;
}
//遍历集合
for(String s:list) {
System.out.println(s);
}
//关闭流
br.close();
}
}
2. 需求:把ArrayList集合中的字符串数据存储到文本文件
ArrayList集合存储的元素String,可以存储一些字符串
使用增强for遍历ArrayList
使用BufferedWriter(文本文件)
源文件:ArrayList<String>
目的地:BufferedWriter输出文本文件,给文件中写入字符
public class ArrayListToFileTest {
public static void main(String[] args) throws Exception {
//创建一个ArrayList集合
ArrayList<String> list = new ArrayList<String>() ;
//添加元素
list.add("hello") ;
list.add("world") ;
list.add("java") ;
list.add("hello") ;
//创建一个字符缓冲输出流
BufferedWriter bw = new BufferedWriter(new FileWriter("a.txt")) ;
//遍历
for(String s:list) {
//将集合中元素写入到流中
bw.write(s);
bw.newLine();
bw.flush();
}
//关闭资源
bw.close();
}
}
3.需求:我有一个文本文件中存储了几个名称,请大家写一个程序实现随机获取一个人的名字。
1)封装一个文本文件:使用字符缓冲输入流读文件
2)创建一个ArrayList<String>
3)使用字符缓冲输入流readLine(),一次读取一行,就将该行数据添加到集合中
4)创建Random类对象
5)Random类对象.nextInt(集合对象.size()) ;
6)通过角标get(int index ):获取内容
public class Test {
public static void main(String[] args) throws Exception {
//封装文件
BufferedReader br = new BufferedReader(new FileReader("b.txt")) ;
//创建一个集合
ArrayList<String> array = new ArrayList<String>() ;
//一次读取一行
String line = null ;
while((line=br.readLine())!=null) {
//将数据添加到集合
array.add(line) ;
}
//释放资源
br.close();
//创建Random类对象
Random r = new Random();
//获取随机数
int index = r.nextInt(array.size()) ;
//通过角标查找集合中元素
String name = array.get(index) ;
System.out.println("幸运的人是:"+name);
}
}