这个系列我分四个部分来分别接触四块知识,最后再串起来:
Java爬虫入门(一)——项目介绍
Java爬虫入门(二)——HttpClient请求
Java爬虫入门(三)——正则表达式
Java爬虫入门(四)——线程池和连接池
Java爬虫入门(五)——缓冲流写入
GitHub地址:
https://github.com/jjc123/Java-Crawler/blob/master/README.md
2.18.8.6更新:
爬虫我这次采用的是 缓冲流保存到本地。
为什么?不写入数据库呢?
当然也可以,不过在这次的入门爬虫中没有必要。
缓冲流实际是没有读写作用的 所以需要传入一个流进入 可以传入字节流 也可以传入一个字符流
先来看一下缓冲输出字节流:
BufferedOutputStream出现的目的是为了提高写数据的效率。
内部也是维护了一个8kb的字节数组而已。
BufferedOutputStream 要注意的细节
1. 使用BufferedOutStream写数据的时候,它的write方法是是先把数据写到它内部维护的字节数组中。
2. 使用BufferedOutStream写数据的时候,它的write方法是是先把数据写到它内部维护的字节数组中,如果需要把数据真正的写到硬盘上面,需要 调用flush方法或者是close方法、 或者是内部维护的字节数组已经填满数据的时候。
public static void main(String[] args) throws IOException {
//找到目标文件
File file = new File("F:\\a.txt");
//建立数据的输出通道
FileOutputStream fileOutputStream = new FileOutputStream(file);
//建立缓冲输出字节流对象
BufferedOutputStream bufferedOutputStream = new BufferedOutputStream(fileOutputStream);
//把数据写出
bufferedOutputStream.write("hello world".getBytes());
//把缓冲数组中内部的数据写到硬盘上面。
//bufferedOutputStream.flush();
bufferedOutputStream.close();
}
接下来介绍一下缓冲字节输入流:
BufferedInputStream
缓冲输入字节流的出现主要是为了提高读取文件数据的效率。 其实该类内部只不过是维护了一个8kb的字节数组而已。
//找到目标文件
File file = new File("F:\\a.txt");
FileInputStream fileInputStream= new FileInputStream(file);
BufferedInputStream bufferedInputStream= new BufferedInputStream(fileInputStream);
bufferedInputStream.read();
//读取文件数据
int content = 0 ;
//疑问二:BufferedInputStream出现 的目的是了提高读取文件的效率,但是BufferedInputStream的read方法每次读取一个字节的数据
//而FileInputStreram每次也是读取一个字节的数据,那么BufferedInputStream效率高从何而来?
while((content = fileInputStream.read())!=-1){
System.out.print((char)content);
}
//关闭资源
bufferedInputStream.close();//调用BufferedInputStream的close方法实际上关闭的是FileinputStream.
总结 :缓冲流字节流 和字节流的区别
缓冲流没有读写功能 控制的其实是传入的流
缓冲流对于字节流区别就是
1. 缓冲流内部维护了一个8kb的字节数组是存在内存中 从硬盘中缓冲出来 然后从里面读取 速度比字节流直接读取硬盘要快
2. 但是字节流有一个办法效率比缓冲流高 那就是读取的时候自定义一个缓冲数组 因为缓冲流只是多了一个内部数组而已 如果自定一个数组的话 那么可以减少缓冲流的其他操作
3. 字节流输入的时候用缓冲数组效率比缓冲流高
字节流输出的时候用缓冲流好
4. 如果用缓冲字节流 bufferedOutputStream的时候 要最后flush或者close不然 没法传递到硬盘
5.
//读取文件的时候我们都是使用缓冲数组读取。效率会更加高
public static void readTest() throws IOException{
File file = new File("F:\\a.txt");
//建立数组通道
FileInputStream fileInputStream = new FileInputStream(file);
//建立缓冲数组读取数据
byte[] buf = new byte[1024*8];
int length = 0;
while((length = fileInputStream.read(buf))!=-1){
System.out.print(new String(buf,0,length));
}
//关闭资源
fileInputStream.close();
}
再来看一下缓冲字符流
缓冲字符流输入:
BufferedReader
public static void readTest1() throws IOException{
//找到目标文件
File file = new File("F:\\a.txt");
//建立数据的输入通道。
FileReader fileReader = new FileReader(file);
//建立缓冲输入字符流
BufferedReader bufferedReader = new BufferedReader(fileReader);
//读取数据
/*int content = bufferedReader.read(); //读到了一个字符。 读取到的字符肯定也是从Bufferedreader内部的字符数组中获取的到。所以效率高。
System.out.println((char)content);*/
//使用BUfferedReader拓展的功能,readLine() 一次读取一行文本,如果读到了文件的末尾返回null表示。
String line = null;
while((line = bufferedReader.readLine())!=null){ // 虽然readLine每次读取一行数据,但是的line是不包含\r\n的、
System.out.println(Arrays.toString("aaa".getBytes()));
}
//关闭资源
bufferedReader.close();
}
缓冲字符流输出:
BufferedWriter
public static void main(String[] args) throws IOException {
//找到目标文件
File file = new File("F:\\a.txt");
//建立数据的输出通道
FileWriter fileWriter = new FileWriter(file,true);
//建立缓冲输出流对象
BufferedWriter bufferedWriter = new BufferedWriter(fileWriter);
//写出数据
// bufferedWriter.newLine(); //newLine() 换行。 实际上就是想文件输出\r\n.
bufferedWriter.write("\r\n");
bufferedWriter.write("前两天李克强来萝岗!!");
//关闭资源
bufferedWriter.flush();
// bufferedWriter.close();
}
缓冲字符输出:
BufferedWriter内部只不过是提供了一个8192长度的字符数组作为缓冲区而已,拓展了FileWriter的功能。
filewrite本来就有一个缓冲数组1024 bufferedwriter只不过提供了一个更大的字符数组而已
而且他扩展的newLine其实就是换行 相当于write(“\r\n”);
总结:
1. 缓冲流内部都维护了一个数组 不是缓冲流的只有字符流的writer维护了一个数组
2. 缓冲流中 只有字符流扩展了方法 reader扩展了readLine write扩展了newLine
3. 缓冲流 不管是输出还是输入 都是先将里面的缓冲数组填满或者取完才再添加内容
4.字符流的话 建议缓冲流 因为内部缓冲数组 快 而且扩展了方法 比如 readLine 一次读一行 返回的是string 到末尾返回null
5. 字符流写数据都是写到内存中的数组 然后flush或者满了才写入硬盘
6.字节流 如果是read 缓冲流快 但是如果自定义一个数组传入read的话 那效率还是字节流快 因为 缓冲流就是帮我们省略了创建缓冲数组的步骤而已
7. while里面接受的是字符串就!=null 如果是 一个一个读的话 就是 -1
自己做了一个测试 贴一下测试结果:
long j=System.currentTimeMillis();用来检测程序运行时间
字符 读入:操作 时间
缓冲流 readline 4-5 while((a=bufferedReader.readLine())!=null)
缓冲流 read 单个读入 18-25 while((a=bufferedReader.read())!=-1)
字符 read 单个读入 19-21 while((a=fileReader.read())!=-1)
字符 自定义缓冲数组 0-1 while((x=fileReader.read(a))!=-1)
字节:
缓冲流 read 单个读入 17-21 while((a=bufferedInputStream.read())!=-1)
字节 read 单个读入 20-21 while((a=fileInputStream.read())!=-1)
字节 read 自定义缓冲数组 0-1 while((a=fileInputStream.read(x))!=-1)(x是byte类型)
杂:
缓冲流close也把里面的流也关闭了
close和flush都可以强制将内存的东西写入硬盘
read()如果是一个一个读取的话 返回的是int 里面装着byte或者char 如果字节流是byte 字符流是char
read(数组) 返回的是接收到的长度
字节流接收到int要显示的话要(char)强制转换
while循环输出的时候判断条件得考虑 如果是read()那么就是-1 如果是read(数组)那么就是null
要追加文件内容的话:
如果目标文件已经存在了,那么默认情况会先情况文件中的数据,然后再写入数据 , 如果需要在原来的基础上追加数据,
需要使用“new FileWriter(File , boolean)”的构造方法,第二参数为true。
FileReader默认的时候是GBK格式 读取
存入的格式都是一个字节一个字节的 如果用getbyte可以存入一个字符串
如果用缓冲字节流 bufferedOutputStream的时候 要最后flush或者close不然 没法传递到硬盘
字符存储是1个字节 汉字是2个字节
UTF-8种汉字由三个字节组成
GBK是2个字节组成
读取的时候如果是字节流的read 一个一个读取的话 会出现乱码 因为汉字是2个字节
而read每次都只是读取一个字节
这个时候可以用字符流 字符流的read返回的是int类型 实际是读取的是char类型
返回的是int int是4个字节 装char2个字节 绰绰有余
我们可以 (char)read 或者 read(char数组)
显示的时候 字符串 可以new String(byte数组,0,length) 也可以char数组
写入的话 字节流 字符串.getBytes() 不建议 字符串编码
有个问题得注意一下:
File file = new File(“D:\c.txt”);
FileOutputStream fileOutputStream = new FileOutputStream(file);
fileOutputStream.write(511);
fileOutputStream.close();
FileInputStream fileInputStream=new FileInputStream(file);
System.out.println(fileInputStream.read());
fileInputStream.close();
返回的值是255而不是-1
int byte都是有符号的类型
存储的时候只保留低8位 所以是把11111111存入
取出的时候 是返回无符号的类型 所以是按照权值来 而不是补码
write(-1)答案也是255
为什么InputStream.read()读取一个byte确返回一个int呢?
java 字节读取流的read方法一次读一个byte但返回int的原因
读取二进制数据按字节读取,每次读一个字节(byte)。
read()的底层是由C++实现的,返回的是unsigned byte,取值范围为[0~255],
在java中没有对应的类型,所以只能用int类型接收,由Java接收得到的就是int[0、255]。