14:IO之字符字节流
字节流:
第一 IO流概述
一、概述:
IO流是来处理设备间的数据传输
1、特点:
1)流操作按照数据可分为字节流(处理所有的数据)和字符流(处理文字,其中包含编码表,可以指定编码表防止了编码表不同而产生乱码的现象)
2)按照流向分可以分为输出流和输入流。
字节流的抽象基类:InputStream(读)、OutputStream(写)
字符流的抽象基类:Reader(读)、Writer(写)
注:此四个类派生出来的子类名称都是以父类名作为子类名的后缀,以前缀为其功能;
第二 字符流
一、简述:
1、字符流中的对象融合了编码表。使用的是默认的编码,即当前系统的编码。
2、字符流只用于处理文字数据,而字节流可以任何数据。
3、既然IO流是用于操作数据的,那么数据的最常见体现形式是文件。专门用于操作文件的子类对象:FileWriter、FileReader
二、写入字符流
- 数据的续写是通过构造函数 FileWriter(String s,boolean append),根据给定文件名及指示是否附加写入数据的boolean值来构造FileWriter对象。为true时就是续写,为false就是不续写。
- 调用write()方法,将字符串写入到流中。这里他本身没有特定的写方法都是继承自父类的方法有写单个字符:
write(int c),写入字符数组:
write(char[] cbuf)这里的数组一般定义成1024的整数倍,不宜过大,过大容易造成内存溢出。写入字符数组的某一部分:write(char[] cbuf, int off, int len),写入字符串:
write(String str),写入字符串的某一部分:write(String str, int off, int len)
- *
- * 需求:在硬盘上,创建一个文件并写入一些文字数据。
- * 找到一个专门用于操作文件的Writer子类对象。FileWriter。 后缀名是父类名。 前缀名是该流对象的功能。
- */
- public class FileWriterDemo {
- public static void main(String[] args) {
- method();//写内容
- method2();//续写内容
- }
- /*
- * 写数据
- */
- public static void method(){
- FileWriter fw = null;
- try {
- //创建一个FileWriter对象。该对象一被初始化就必须要明确被操作的文件。
- //该文件会被存放在指定的目录下,如果该文件已经存在,会被覆盖
- //其实该步就是在明确数据要存放的目的地。
- fw = new FileWriter("testwrite.txt");// C:\\testwrite.txt
- //调用write方法,将字符串写入到流中
- fw.write("asdsadafsd");
- //刷新流对象中的缓冲中的数据。
- //将数据刷到目的地中。
- // fw.flush();
- //关闭流资源,但是关闭之前会刷新一次内部的缓冲中的数据。
- //将数据刷到目的地中。
- //和flush区别:flush刷新后,流可以继续使用,close刷新后,会将流关闭。
- // fw.close();
- } catch (IOException e) {
- e.printStackTrace();
- }finally{
- try {
- if(fw != null)
- fw.close();
- } catch (IOException e) {
- e.printStackTrace();
- }
- }
- }
- /*
- * 在文件原有内容的基础上续写内容
- */
- public static void method2(){
- FileWriter fw = null;
- try {
- fw = new FileWriter("testwrite.txt", true);
- fw.write("\r\nnihao\r\nxiexie");//\r\n是换行符\n是linux下的换行
- } catch (IOException e) {
- e.printStackTrace();
- }finally{
- if(fw != null){
- try {
- fw.close();
- } catch (IOException e) {
- e.printStackTrace();
- }
- }
- }
- }
- }
三、读取字符流
- 创建一个文件读取流对象,和指定名称的文件相关联。要保证该文件已经存在,若不存在,将会发生异常FileNotFoundException。
- 调用读取流对象的read()方法。read():一次读一个字符,且会继续往下读。(方法1)
read()读取单个字符。read(char[] cbuf)将字符读入数组。(方法2)
其实都是按照每次只读取一个字符的方式读取的,只是读到数组中会把读取到的数据存放在数组中,起到一个临时缓存的作用,提高了读取效率。
演示1:
public static void main(String[] args) throws IOException {
FileReader fr = new FileReader("demo.txt");
int ch = 0;
while((ch=fr.read())!=-1){// abcde读5次
System.out.println((char)ch);
fr.close();
}
演示2:
public class FileReaderDemo2 {
public static void main(String[] args) throws IOException {
FileReader fr = new FileReader("demo.txt");
/*
* 使用read(char[])读取文本文件数据。
*
* 先创建字符数组。
*/
char[] buf = new char[1024];
int len = 0;
while((len=fr.read(buf))!=-1){ // abcde读1次,提高效率,是一个数组一个数组的读
System.out.println(new String(buf,0,len));
}
/*
int num = fr.read(buf);//将读取到的字符存储到数组中。读到几个几个变成数组
System.out.println(num+":"+new String(buf,0,num));
int num1 = fr.read(buf);//将读取到的字符存储到数组中。
System.out.println(num1+":"+new String(buf,0,num1));
int num2 = fr.read(buf);//将读取到的字符存储到数组中。
System.out.println(num2+":"+new String(buf));
*/
fr.close();
- }
四、文件的拷贝:
原理:其实就是将磁盘下的文件数据读取出来,然后写入磁盘的一个文件中
步骤:
1、在硬盘上创建一个文件,用于存储读取出来的文件数据
2、定义读取流和文件关联
3、通过不断读写完成数据存储
方式一:读取一个字符,存入一个字符
方式二:先将读取的数据存入到内存中,再将存入的字符取出写入硬盘
4、关闭流资源:输入流资源和输出流资源。
方法1:
/*
* 思路:
* 1,需要读取源,
* 2,将读到的源数据写入到目的地。
* 3,既然是操作文本数据,使用字符流。
*
*/
public class CopyTextTest {
public static void main(String[] args) throws IOException {
//1,读取一个已有的文本文件,使用字符读取流和文件相关联。
FileReader fr = new FileReader("IO流_2.txt");
//2,创建一个目的,用于存储读到数据。
FileWriter fw = new FileWriter("copytext_1.txt");
//3,频繁的读写操作。
int ch = 0;
while((ch=fr.read())!=-1){
fw.write(ch);
}
//4,关闭流资源。先关写再关读
fw.close();
fr.close();
}
}
方法2:
public class CopyTextTest_2 {
private static final int BUFFER_SIZE = 1024;
public static void main(String[] args) {
FileReader fr = null;
FileWriter fw = null;
try {
fr = new FileReader("IO流_2.txt");
fw = new FileWriter("copytest_2.txt");
//创建一个临时容器,用于缓存读取到的字符。
char[] buf = new char[BUFFER_SIZE];//这就是缓冲区。
//定义一个变量记录读取到的字符数,(其实就是往数组里装的字符个数 )
int len = 0;
while((len=fr.read(buf))!=-1){
fw.write(buf, 0, len);
}
} catch (Exception e) {
// System.out.println("读写失败");
throw new RuntimeException("读写失败");
}finally{
if(fw!=null)
try {
fw.close();
} catch (IOException e) {
e.printStackTrace();
}
if(fr!=null)
try {
fr.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
第三 字符缓冲区
一、写入缓冲区BufferedWriter
/
* 缓冲区的出现就是为了提高操作流的效率
* 所以在创建缓冲区之前必须要先有流对象
*
* 缓冲区提供了一个跨平台的换行符 new Line();
*/
public class BufferedWriterDemo {
public static void main(String[] args) {
method();
}
public static void method() {
// 创建一个字符写入流对象
FileWriter fw = null;
BufferedWriter bfw = null;
try {
fw = new FileWriter("buf.txt");
// 为了提高字符写入流效率。加入了缓冲技术。其实缓冲区就是封装了数组,不用自己定义数组,用起来更方便
// 只要将需要被提高效率的流对象作为参数传递给缓冲区的构造函数即可。
bfw = new BufferedWriter(fw);
for (int x = 0; x < 10; x++) {
bfw.write("abcds" + x);
bfw.newLine();// 换行在windows中相当于\r\n,在linux中相当于\n
}
// 记住,只要用到缓冲区,就要记得刷新。
// bufw.flush();
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
bfw.close();// 关闭缓冲区就是在关闭缓冲区中的流对象,关闭前刷新
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
private static final String LINE_SEPARATOR = System.getProperty("line.separator");
//使用缓冲区的写入方法将数据先写入到缓冲区中。
// bufw.write("abcdefq"+LINE_SEPARATOR+"hahahha");
// bufw.write("xixiixii");
// bufw.newLine(); 行分隔,就不需要LINE_SEPARATOR,不过只在这里有效
// bufw.write("heheheheh");
二、读取流缓冲区BufferedReader
该缓冲区提供了一个一次读一行的方法readLine(),方便与对文本数据的获取,当返回null时,表示读到文件末尾。
readLine()方法返回的时只返回回车符之前的数据内容,并不返回回车符,即读取的内容中不包含任何行终止符(回车符和换行符)。
--->readLine()方法原理:无论是读一行,或读取多个字符,其实最终都是在硬盘上一个一个读取。所以最终使用的还是read方法一次读一个
public class BufferedReaderDemo {
public static void main(String[] args) {
method();
}
public static void method(){
//创建一个读取流对象
FileReader fr = null;
BufferedReader bfr = null;
try {
fr = new FileReader("buf.txt");
//为了提高效率。加入缓冲技术。将字符读取流对象作为参数传递给缓冲对象的构造函数。
bfr = new BufferedReader(fr);
// char[] buf = new char[1024];
// bfr.read(buf);
// int len = 0;
// while((len = bfr.read(buf)) != -1){
// System.out.println(new String(buf,0,len));
// }
String line = null;
while((line = bfr.readLine()) != null){
System.out.println(line);
}
} catch (IOException e) {
e.printStackTrace();
}finally{
try {
bfr.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
三、通过缓冲技术提高效率复制文件:
public class CopyTextByBufTest {
//
不处理异常,抛出去
public static void main(String[] args) throws IOException {
FileReader fr = new FileReader("buf.txt");
BufferedReader bufr = new BufferedReader(fr);
FileWriter fw = new FileWriter("buf_copy.txt");
BufferedWriter bufw = new BufferedWriter(fw);
String line = null;
while ((line = bufr.readLine()) != null) {
bufw.write(line);
bufw.newLine();
bufw.flush();
}
/*
* int ch = 0;
*
* while((ch=bufr.read())!=-1){
*
* bufw.write(ch); }
*/
bufw.close();
bufr.close();
}
}
处理异常try...catch
public class CopyByBuffer {
public static void main(String[] args) {
copyByBuf();
}
public static void copyByBuf() {
FileWriter fw = null;
FileReader fr = null;
BufferedWriter bfw = null;
BufferedReader bfr = null;
try {
fw = new FileWriter("C:\\buffertest.txt");//创建写文件对象
fr = new FileReader("buffertest.txt");//创建读文件对象
bfw = new BufferedWriter(fw);//使用缓冲区关联读写对象
bfr = new BufferedReader(fr);
String line = null;
while ((line = bfr.readLine()) != null) {//通过读一行的方式提高效率
bfw.write(line);
bfw.newLine();//换行,夸平台
}
} catch (IOException e) {
e.printStackTrace();
} finally {
if (bfw != null) {
try {
bfw.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if (bfr != null) {
try {
bfr.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
四、自定义缓冲区
其实就是模拟一个BufferedReader.
* 分析:
* 缓冲区中无非就是封装了一个数组,并对外提供了更多的方法对数组进行访问。 其实这些方法最终操作的都是数组的角标。
*
* 缓冲的原理:
* 其实就是从源中获取一批数据装进缓冲区中。在从缓冲区中不断的取出一个一个数据。
* 在此次取完后,在从源中继续取一批数据进缓冲区。 当源中的数据取光时,用-1作为结束标记。
public class MyBufferedReader extends Reader {
private Reader r;
//定义一个数组作为缓冲区。
private char[] buf = new char[1024];
//定义一个指针用于操作这个数组中的元素。当操作到最后一个元素后,指针应该归零。
private int pos = 0;
//定义一个计数器用于记录缓冲区中的数据个数。 当该数据减到0,就从源中继续获取数据到缓冲区中。
private int count = 0;
MyBufferedReader(Reader r){
this.r = r;
}
/**
* 该方法从缓冲区中一次取一个字符。
* @return
* @throws IOException
*/
/*
public int myRead() throws IOException{
//从源中获取一批数据到缓冲区中。需要先做判断,只有计数器为0时,才需要从源中获取数据。
if(count==0){
count = r.read(buf); //这是在底层
if(count<0)//没有了
return -1;
//每次获取数据到缓冲区后,角标归零(第一个read,底层)创建了一个数组。
pos = 0;
char ch = buf[pos];
// 然后调用一次(第二个)read,返回一个a,再调用返回b,角标取一个,count减一个
pos++;
count--;
return ch;
}
else if(count>0){//第二次调用的时候count就不为0了, 不需要count = r.read(buf)这一步了
char ch = buf[pos];
pos++;
count--;
return ch;
}*/
优化后
public int myRead() throws IOException{
if(count==0){
count = r.read(buf);
pos = 0;
}
if(count<0)
return -1;
char ch = buf[pos++];
count--;
return ch;
}
五、模拟ReadLine
public String myReadLine() throws IOException{
// 定义一个临时容器,原BufferedReader封装的是字符数组,这里定义StringBuilder演示原理
StringBuilder sb = new StringBuilder();
int ch = 0;
while((ch = myRead())!=-1){
if(ch=='\r') //如是'\r'继续往下读
continue;
if(ch=='\n') //读到'\n'就停止读,然后将这些数据返回
return sb.toString();
//将从缓冲区中读到的字符,存储到缓存行数据的缓冲区中。
sb.append((char)ch);
}
if(sb.length()!=0) //最后没有空格,说明有东西,返回,
//这里是防止最后一行没有回车符的情况
return sb.toString();
return null;
}
public void myClose() throws IOException {
r.close();
}
@Override
public int read(char[] cbuf, int off, int len) throws IOException {
return 0;
}
@Override
public void close() throws IOException {
}
}
运行 ReadLine
public class MyBufferedReaderDemo {
public static void main(String[] args) throws IOException {
FileReader fr = new FileReader("buf.txt");
MyBufferedReader bufr = new MyBufferedReader(fr);
String line = null;
while((line=bufr.myReadLine())!=null){
System.out.println(line);
}
bufr.myClose();
Collections.reverseOrder();
HashMap map = null;
map.values();
}
}
六、LineNumberReader(功能增强的读)
在BufferedReader中有个直接的子类LineNumberReader,其中有特有的方法获取和设置行号:
setLineNumber()和getLineNumber()
public class LineNumberReaderDemo {
public static void main(String[] args) throws IOException {
FileReader fr = new FileReader("IO流_2.txt");
LineNumberReader lnr = new LineNumberReader(fr);
String line = null;
lnr.setLineNumber(100);// 默认是0
while ((line = lnr.readLine()) != null) {
System.out.println(lnr.getLineNumber() + ":" + line);
}
lnr.close();
}
}
第四 装饰设计模式
装饰设计模式: (buffer就是装饰设计模式)
对一组对象的功能进行增强时,就可以使用该模式进行问题的解决。
装饰和继承都能实现一样的特点:进行功能的扩展增强。
有什么区别呢?
首先有一个继承体系。
Writer (抽取了一个写的)
|--TextWriter (随便写的):用于操作文本
|--MediaWriter:用于操作媒体。
想要对操作的动作进行效率的提高。
按照面向对象,可以通过继承对具体的进行功能的扩展。
效率提高需要加入缓冲技术。
Writer
|--TextWriter:用于操作文本
|--BufferTextWriter:加入了缓冲技术的操作文本的对象。
|--MediaWriter:用于操作媒体。
|--BufferMediaWriter:
到这里就哦了。但是这样做好像并不理想。
如果这个体系进行功能扩展,有多了流对象。
那么这个流要提高效率,是不是也要产生子类呢?是。这时就会发现只为提高功能,进行的继承,
导致继承体系越来越臃肿。不够灵活。
重新思考这个问题?
既然加入的都是同一种技术--缓冲。
前一种是让缓冲和具体的对象相结合。
可不可以将缓冲进行单独的封装,哪个对象需要缓冲就将哪个对象和缓冲关联。
class Buffer {
Buffer(TextWriter w) // 要缓冲谁就加入进来
{
}
Buffer(MediaWirter w) // 要缓冲谁就加入进来
{
}
} // 但是这样做,来新对象还得加入,比较麻烦
// 都能写入,为了名字有意义,写成BufferWriter
class BufferWriter extends Writer {
BufferWriter(Writer w) {
}
}
Writer
|--TextWriter:用于操作文本
|--MediaWriter:用于操作媒体。
|--BufferWriter:用于提高效率。
装饰比继承灵活。
特点:装饰类和被装饰类都必须所属同一个接口或者父类。
有个类想要增强,可以用装饰设计模式,把被装饰的类往里传进来就可以
第五 字节流
一、概述:
1、字节流和字符流的原理是相似的,而字符流是基于字节流的,字节流可以操作如媒体等其他数据,如媒体(MP3,图片,视频)等
2、由于媒体数据中都是以字节存储的,所以,字节流对象可直接对媒体进行操作,而不用再进行刷流动作。
3、InputStream ---> 输入流(读) OutputStream ---> 输出流(写)
4、为何不用进行刷流动作:因为字节流操作的是字节,即数据的最小单位,不需要像字符流一样要进行转换为字节。可直接将字节写入到指定文件中,但是需要在写代码的时候,如果有字符串,要将字符串转为字节数组再进行操作。
5、FileInputStream特有方法:int available() ---> 返回数据字节的长度,包含终止符
在定义字节数组长度的时候,可以用到这个方法:byte[] = new byte[fos.available()] (fos为字节流对象)但是,对于这个方法要慎用,如果字节过大,超过jvm所承受的大小(一般内存为64M),就会内存溢出。
实例:
/
* 字节流操作
* InputStream OutputStream
*
*/
public class FileStreamDemo {
public static void main(String[] args) {
// writeFile();
// readFile_1();
// readFile_2();
readFile_3();
}
/*
* 对字节流读操作的第一种方式 通过read()方法读取一个字节
*/
public static void readFile_1() {
FileInputStream fis = null;// 定义字节输入流
try {
fis = new FileInputStream("fos.txt");// 指定输入流和文件关联
int ch = 0;
while ((ch = fis.read()) != -1) {// 读取操作
System.out.println((char) ch);
}
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
if (fis != null)// 判空,提高程序的健壮性
fis.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
/*
* 对字节流读操作的第二种方式 通过read(byte[])方法读取字节数组
*/
public static void readFile_2() {
FileInputStream fis = null;
try {
fis = new FileInputStream("fos.txt");
byte[] byf = new byte[1024];
int len = 0;
while ((len = fis.read(byf)) != -1) {
System.out.println(new String(byf, 0, len));
}
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
if (fis != null)
fis.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
/*
* 对字节流读操作的第三种方式 通过字节流的available()方法获取到文件大小,定义一个大小刚刚好的数组,无需循环 但是,这种方式操作较大数据时容易内存溢出,所以要慎用 首选的还是定义1024的整数倍数组
*/
public static void readFile_3() {
FileInputStream fis = null;
try {
fis = new FileInputStream("fos.txt");
// 通过这样的方式定义一个刚刚好的数组
// 这种方法慎用,如果文件不大,可以用这个方法
byte[] byf = new byte[fis.available()];// fis.available()获取文件大小
fis.read(byf);
System.out.println(new String(byf));
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
fis.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
/*
* 对字节流进行写操作
*/
public static void writeFile() {
FileOutputStream fos = null;
try {
fos = new FileOutputStream("fos.txt");//定义字节写出流和文件关联
fos.write("abcds".getBytes());// str.getBytes()将String转化成字节数组
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} finally {
if (fos != null)
try {
fos.close();// 因为字节流没有用到缓冲区,所以不需要刷新,但必须关闭资源
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
二、通过字节流复制媒体文件
思路:
1、用字节流读取流对象和媒体文件相关联
2、用字节写入流对象,创建一个媒体文件,用于存储获取到的媒体文件数据
3、通过循环读写,完成数据的存储
4、关闭资源
* 通过字节流拷贝图片
*/
public class CopyPicture {
public static void main(String[] args) {
copyPic();
}
public static void copyPic() {
FileInputStream fis = null;
FileOutputStream fos = null;
try {
fis = new FileInputStream("1.jpg");
fos = new FileOutputStream("C:\\1.jpg");
byte[] byf = new byte[1024];
int len = 0;
while ((len = fis.read(byf)) != -1) {// 将数据读取到数组中
fos.write(byf, 0, len);// 写入数组中的有效数据
}
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} finally {
// 有多个流不能一起关闭,要分别关闭
if (fis != null) {
try {
fis.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if (fos != null) {
try {
fos.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
// 千万不要用,效率没有!
public static void copy_4() throws IOException {
FileInputStream fis = new FileInputStream("c:\\0.mp3");
FileOutputStream fos = new FileOutputStream("c:\\4.mp3");
int ch = 0;
while((ch =fis.read())!=-1){
fos.write(ch);
}
fos.close();
fis.close();
}
//不建议。
public static void copy_3() throws IOException {
FileInputStream fis = new FileInputStream("c:\\0.mp3");
FileOutputStream fos = new FileOutputStream("c:\\3.mp3");
byte[] buf = new byte[fis.available()];
fis.read(buf);
fos.write(buf);
fos.close();
fis.close();
}
//提高效率
public static void copy_2() throws IOException {
FileInputStream fis = new FileInputStream("c:\\0.mp3");
BufferedInputStream bufis = new BufferedInputStream(fis);
FileOutputStream fos = new FileOutputStream("c:\\2.mp3");
BufferedOutputStream bufos = new BufferedOutputStream(fos);
int ch = 0;
while((ch=bufis.read())!=-1){
bufos.write(ch);
}
bufos.close();
bufis.close();
}
三、字节流缓冲区
1、缓冲区的出现无非就是提高了流的读写效率,当然也就是因为这样,所以缓冲区的使用频率也是相当高的,所以要做必要性的掌握。
2、字节流缓冲区读写的特点:
这里没有什么特殊的读写方法,就是read()读一个字节,read(byte[])读数组的方法。写write(int b)写一个自己,write(byte[])写数组的方法。
注:需要注意个地方,write方法只写出二进制的最后八位。
原理:将数据拷贝一部分,读取一部分,循环,直到数据全部读取完毕。
1)先从数据中抓取固定数组长度的字节,存入定义的数组中,再通过然后再通过read()方法读取数组中的元素,存入缓冲区
2)循环这个动作,知道最后取出一组数据存入数组,可能数组并未填满,同样也取出包含的元素
3)每次取出的时候,都有一个指针在移动,取到数组结尾就自动回到数组头部,这样指针在自增
4)取出的时候,数组中的元素再减少,取出一个,就减少一个,直到减到0即数组取完
5)到了文件的结尾处,存入最后一组数据,当取完数组中的元素,就会减少到0,这是全部数据就取完了
3、自定义字节缓冲区:
* 自定义字节流缓冲区
*/
public class MyBufferedInputStream {
private InputStream in;
private byte[] buf = new byte[1024];
private int pos;// 定义数组的指针
private int count;// 定义计数器
MyBufferedInputStream(InputStream in) {
this.in = in;
}
// 一次读一个字节,从缓冲区(数组)中取得
public int myRead() throws IOException {
// 通过in对象读取数据并存放在buf数组中
if (count == 0) {
count = in.read(buf);
if (count < 0) {
return -1;
}
pos = 0;
byte b = buf[pos];
count--;
pos++;
return b & 255;// &255是因为存放的是二进制,也就是可能前八位是11111111,提升为int还是-1,就直接return了,
// &255就是让前面补0
/*
* 11111111 11111111 11111111 11111111
* &00000000 00000000 00000000 11111111
* ------------------------------------
* 00000000 00000000 00000000 11111111
*/
}else if(count>0){
byte b = buf[pos];
count--;
pos++;
return b&255;//&0xff
}
return -1;
}
public void myClose() throws IOException{
in.close();
}
}
注:取出的是byte型,返回的是int型,这里存在提升的动作,
当byte中的八位全为1的时候是byte的-1,提升为int类型,就变为int型的-1,为-1时程序就停止循环了,read循环条件就结束了,变为-1的原因是由于在提升时,将byte的八位前都补的是1,即32位的数都是1,即为int型的-1了。如何保证提升后的最后八位仍为1呢?就需要将前24位补0,就可以保留原字节数据不变,又可以避免转为int型出现-1的情况;
那么要如何做呢?
这就需要将提升为int的数据和前24位为0,后八位仍为原字节数据的这个值做与运算。即和255做与运算即可。说到了这里应该也明白了为什么Read方法返回值为int类型了。
第六 转换流
转换流:转换流可以实现字节数据和字符数据的相互转换方便与操作,而且在转换的时候可以指定编码,这也是该流最具特色的地方。:
InputStreamReader 是字节流通向字符流的桥梁
OutputStreamWriter 是字符流通向字节流的桥梁
转换流的子类和转换流的区别?
InputStreamReader 字节-->字符
|--FileReader : 字节流+本地默认码表。
OutputStreamWriter 字符-->字节
|--FileWriter
什么时候使用转换流呢?
1,源或者目的对应的设备是字节流,但是操作的却是文本数据,可以使用转换作为桥梁。
提高对文本操作的便捷。
2,一旦操作文本涉及到具体的指定编码表时,必须使用转换流 。
BufferedWriter bufw = new BufferedWriter(new OutputStreamWriter(new FileOutputStream("a.txt"),charsetName));
- //获取键盘录入对象
InputStream in = System.in;
//将字节流对象转换成字符流对象
InputStreamReader isr = new InputStreamReader(in);
//将字符流对象用缓冲技术高效处理
BufferedReader bufr = new BufferedReader(isr);
// 键盘录入最常见的写法
BufferedReader bufr = new BufferedReader(new InputStreamReader(System.in));
字符流转字节流:
-
// 操作输出
// 获取输出流
OutputStream out = System.out;
//将字符流转换成字节流,OutputStreamWriter字符流通向字节流的桥梁
OutputStreamWriter osw = new OutputStreamWriter(out);
BufferedWriter bfw = new BufferedWriter(osw);//高效缓冲区
获取键盘输入:键盘输入都是字节,所以用到字节流与标准输入流相关联就可以把输入的数据获取到流中,以达到数据的操作效果。
* 获取键盘录入
public class ReadIn {
public static void main(String[] args) {
// method_1();
// method_2();
InputStreamReaderDemo();
}
/*
* 获取键盘录入
*/
public static void method_1() {
InputStream in = System.in;// 定义输入流与键盘输入流相关联
int ch = 0;
try {
while ((ch = in.read()) != -1) {
System.out.println(ch);
}
} catch (IOException e) {
e.printStackTrace();
}
}
/*
* 需求:通过键盘录入数据。 当录入一行数据后,就将该行数据进行打印。 如果录入的数据是over,那么停止录入。
* 1,因为键盘录入只读取一个字节,要判断是否是over,需要将读取到的字节拼成字符串。
* 2,那就需要一个容器。StringBuilder.
* 3,在用户回车之前将录入的数据变成字符串判断即可。
*/
public static void method_2() {
InputStream in = System.in;
StringBuilder sb = new StringBuilder();// 定义一个临时容器
while (true) {
int ch = 0;
try {
ch = in.read();
if (ch == '\r')// windows中换行符为\r\n
continue;
if (ch == '\n') {
String s = sb.toString();// 当读到一行的结束标记时,把改行数据变成字符串
if ("over".equals(s))
break;
System.out.println(s.toUpperCase()); //变成大写
sb.delete(0, sb.length());// 清空容器 ,不清空会将上次输入的和这次输入的都输出
} else {
sb.append((char) ch); //将读取到的字节存储到StringBuilder中。
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
// **************************************************************************
/*
* 通过上面录入一行数据,发现其方法类似于readLine方法 但是readLine方法是字符流缓冲区的方法,所以需要把字节流转换成字符流进行操作
* 需要用到InputStreamReader方法进行转换,InputStreamReader字节通向字符的桥梁
*
* 转换流
*/
public static void InputStreamReaderDemo() {
BufferedReader bufr = null;
try {
// 源为文件
bufr = new BufferedReader(new InputStreamReader(
new FileInputStream("buf.txt")));
} catch (FileNotFoundException e2) {
e2.printStackTrace();
}
// 操作输出
// 目的地是控制台
BufferedWriter bfw = new BufferedWriter(new OutputStreamWriter(
System.out));
// 目的地是文件
// BufferedWriter bfw = null;
// try {
// bfw = new BufferedWriter(new OutputStreamWriter(new
// FileOutputStream("out.txt")));
// } catch (FileNotFoundException e1) {
// e1.printStackTrace();
// }
String line = null;
try {
while ((line = bufr.readLine()) != null) {
if ("over".equals(line)) {
break;
}
bfw.write(line.toUpperCase());
bfw.newLine();// 换行
bfw.flush();// 数据存放在缓冲区,所以需要刷新
}
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
bufr.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
不处理异常
public class TransStreamDemo2 {
public static void main(String[] args) throws IOException {
/*
* 1,需求:将键盘录入的数据写入到一个文件中。
*
* 2,需求:将一个文本文件内容显示在控制台上。
*
* 3,需求:将一个文件文件中的内容复制到的另一个文件中。
*/
BufferedReader bufr = new BufferedReader(new InputStreamReader(System.in));
BufferedWriter bufw = new BufferedWriter(new OutputStreamWriter(new FileOutputStream("b.txt")));
String line = null;
while((line=bufr.readLine())!=null){
if("over".equals(line))
break;
bufw.write(line.toUpperCase()); //变成大写
bufw.newLine();
bufw.flush();
}
}
}
第七 流的操作规律
之所以要弄清楚这个规律,是因为流对象太多,开发时不知道用哪个对象合适。
想要知道开发时用到哪些对象。只要通过四个明确即可。
1,明确源和目的(汇)
源:InputStream Reader
目的:OutputStream Writer
2,明确数据是否是纯文本数据。
源:是纯文本:Reader
否:InputStream
目的:是纯文本 Writer
否:OutputStream
到这里,就可以明确需求中具体要使用哪个体系。
3,明确具体的设备。
源设备:
硬盘:File
键盘:System.in
内存:数组
网络:Socket流
目的设备:
硬盘:File
控制台:System.out
内存:数组
网络:Socket流
4,是否需要其他额外功能。
1,是否需要高效(缓冲区);
是,就加上buffer.
2,转换。
需求1:复制一个文本文件。
1,明确源和目的。
源:InputStream Reader
目的:OutputStream Writer
2,是否是纯文本?
是!
源:Reader
目的:Writer
3,明确具体设备。
源:
硬盘:File
目的:
硬盘:File
FileReader fr = new FileReader("a.txt");
FileWriter fw = new FileWriter("b.txt");
4,需要额外功能吗?
需要,需要高效。
BufferedReader bufr = new BufferedReader(new FileReader("a.txt"));
BufferedWriter bufw = new BufferedWriter(new FileWriter("b.txt"));
================================================
需求2:读取键盘录入信息,并写入到一个文件中。
1,明确源和目的。
源:InputStream Reader
目的:OutputStream Writer
2,是否是纯文本呢?
是,
源:Reader
目的:Writer
3,明确设备
源:
键盘。System.in
目的:
硬盘。File
InputStream in = System.in;
FileWriter fw = new FileWriter("b.txt");
这样做可以完成,但是麻烦。将读取的字节数据转成字符串。再由字符流操作。
4,需要额外功能吗?
需要。转换。 将字节流转成字符流。因为名确的源是Reader,这样操作文本数据做便捷。
所以要将已有的字节流转成字符流。使用字节-->字符 。InputStreamReader
InputStreamReader isr = new InputStreamReader(System.in);
FileWriter fw = new FileWriter("b.txt");
还需要功能吗?
需要:想高效。
BufferedReader bufr = new BufferedReader(new InputStreamReader(System.in));
BufferedWriter bufw = new BufferedWriter(new FileWriter("b.txt"));
===================================================
需求3:将一个文本文件数据显示在控制台上。
1,明确源和目的。
源:InputStream Reader
目的:OutputStream Writer
2,是否是纯文本呢?
是,
源:Reader
目的:Writer
3,明确具体设备
源:
硬盘:File
目的:
控制台:System.out
FileReader fr = new FileReader("a.txt");
OutputStream out = System.out;//PrintStream
4,需要额外功能吗?
需要,转换。
FileReader fr= new FileReader("a.txt");
OutputStreamWriter osw = new OutputStreamWriter(System.out);
需要,高效。
BufferedReader bufr = new BufferedReader(new FileReader("a.txt"));
BufferedWriter bufw = new BufferedWriter(new OutputStreamWriter(System.out));
================================================================
需求4:读取键盘录入数据,显示在控制台上。
1,明确源和目的。
源:InputStream Reader
目的:OutputStream Writer
2,是否是纯文本呢?
是,
源:Reader
目的:Writer
3,明确设备。
源:
键盘:System.in
目的:
控制台:System.out
InputStream in = System.in;
OutputStream out = System.out;
4,明确额外功能?
需要转换,因为都是字节流,但是操作的却是文本数据。
所以使用字符流操作起来更为便捷。
InputStreamReader isr = new InputStreamReader(System.in);
OutputStreamWriter osw = new OutputStreamWriter(System.out);
为了将其高效。
BufferedReader bufr = new BufferedReader(new InputStreamReader(System.in));
BufferedWriter bufw = new BufferedWriter(new OutputStreamWriter(System.out));
============================================================
5,将一个中文字符串数据按照指定的编码表写入到一个文本文件中.
1,目的。OutputStream,Writer
2,是纯文本,Writer。
3,设备:硬盘File
FileWriter fw = new FileWriter("a.txt");
fw.write("你好");
注意:既然需求中已经明确了指定编码表的动作。
那就不可以使用FileWriter,因为FileWriter内部是使用默认的本地码表。
只能使用其父类。OutputStreamWriter.
OutputStreamWriter接收一个字节输出流对象,既然是操作文件,那么该对象应该是FileOutputStream
OutputStreamWriter osw = new OutputStreamWriter(new FileOutputStream("a.txt"),charsetName(编码));
需要高效吗?
BufferedWriter bufw = new BufferedWriter(new OutputStreamWriter(new FileOutputStream("a.txt"),charsetName));
演示:将一个中文字符串数据按照指定的编码表写入到一个文本文件中.
写:
public static void writeText_2() throws IOException {
OutputStreamWriter osw = new OutputStreamWriter(new FileOutputStream(
"gbk_3.txt"), "GBK");
// OutputStreamWriter osw = new OutputStreamWriter(new
// FileOutputStream("gbk_3.txt"),"GBK");
// FileWriter fw = new FileWriter("gbk_1.txt");
/*
* 这两句代码的功能是等同的。 FileWriter:其实就是转换流指定了本机默认码表的体现。而且这个转换流的子类对象,可以方便操作文本文件。
* 简单说:操作文件的字节流+本机默认的编码表。 这是按照默认码表来操作文件的便捷类。
*
* 如果操作文本文件需要明确具体的编码。FileWriter就不行了。必须用转换流
*/
osw.write("你好");
osw.close();
}
public static void writeText_3() throws IOException {
OutputStreamWriter osw = new OutputStreamWriter(new FileOutputStream(
"u8_1.txt"), "UTF-8");
osw.write("你好");
osw.close();
}
读:
public static void readText_1() throws IOException {
FileReader fr = new FileReader("gbk_1.txt");
char[] buf = new char[10];
int len = fr.read(buf);
String str = new String(buf,0,len);
System.out.println(str);
fr.close();
}
//用utf-8读默认的编码文件
public static void readText_2() throws IOException, FileNotFoundException {
InputStreamReader isr = new InputStreamReader(new FileInputStream("gbk_1.txt"),"utf-8");
char[] buf = new char[10];
int len = isr.read(buf);
String str = new String(buf,0,len);
System.out.println(str);
isr.close();
}
}
}
14:IO之字符字节流的更多相关文章
- JavaSE学习笔记(14)---File类和IO流(字节流和字符流)
JavaSE学习笔记(14)---File类和IO流(字节流和字符流) File类 概述 java.io.File 类是文件和目录路径名的抽象表示,主要用于文件和目录的创建.查找和删除等操作. 构造方 ...
- Java基础:IO流之字节流和字符流
1. 流的概念 流(stream)的概念源于UNIX中管道(pipe)的概念.在UNIX中,管道是一条不间断的字节流,用来实现程序或进程间的通信,或读写外围设备.外部文件等. 一个流,必有源端和目的端 ...
- IO流输入 输出流 字符字节流
一.流 1.流的概念 流是一组有顺序的,有起点和终点的字节集合,是对数据传输的总称或抽象.即数据在两设备间的传输称为流,流的本质是数据传输,根据数据传输特性将流抽象为各种类,方便更直观的进行数据操作. ...
- Java IO流字符流简介及基本使用
Java IO流字符流简介及常用字符流的基本使用 字符流分为输入字符流(Writer)和输出字符流(Reader),这两种字符流及其子类字符流都有自己专门的功能.在编码中我们常用的输出字符流有File ...
- java io流(字节流)复制文件
java io流(字节流) 复制文件 //复制文件 //使用字节流 //复制文本文件用字符流,复制其它格式文件用字节流 import java.io.*; public class Index{ pu ...
- io系列之字节流
java中io流系统庞大,知识点众多,作为小白通过五天的视频书籍学习后,总结了io系列的随笔,以便将来复习查看. 本篇为此系列随笔的第一篇:io系列之字节流. 一.字节流的File读写操作. Inpu ...
- Java IO: 其他字符流(下)
作者: Jakob Jenkov 译者: 李璟(jlee381344197@gmail.com) 本小节会简要概括Java IO中的PushbackReader,LineNumberReader,St ...
- IO流(字节流复制)01
package ioDemo; import java.io.*; /** * IO流(字节流复制) * Created by lcj on 2017/11/2. */ public class bu ...
- Java笔记(二十六)……IO流上 字节流与字符流
概述 IO流用来处理设备之间的数据传输 Java对数据的操作时通过流的方式 Java用于操作流的对象都在IO包中 流按操作的数据分为:字节流和字符流 流按流向不同分为:输入流和输出流 IO流常用基类 ...
随机推荐
- kill all java php rm.sh
#!/bin/sh#根据进程名杀死进程#FileName: killjavaphprm.sh #查看php进程IDecho "php进程ID:"pgrep php #杀死所有php ...
- SQLite在Android程序中的使用方法,SQLite的增删查改方法
Sqlite: 1.一款用来实现本地数据存储的轻量级数据管理工具,是众多用来实现数据库管理的工具之一. 2.Android已经将SQLite的代码功能吸收在它的系统中,我们可以直接在Android程序 ...
- Java6及以上版本对synchronized的优化
目录 1.概述 2.实现同步的基础 3.实现方式 4.Java对象头(存储锁类型) 5.优化后synchronized锁的分类 6.锁的升级(进化) 6-1.偏向锁 6-2.轻量级锁 6-3.锁的比较 ...
- linux学习资料收藏
http://blog.chinaunix.net/uid/10167808/abstract/1.html?year=2008 http://linux.linuxidc.com/ind ...
- java.util.HashSet, java.util.LinkedHashMap, java.util.IdentityHashMap 源码阅读 (JDK 1.8.0_111)
一.java.util.HashSet 1.1 HashSet集成结构 1.2 java.util.HashSet属性 private transient HashMap<E,Object> ...
- 【Django】django.core.exceptions.ImproperlyConfigured: Error loading MySQLdb module.
最近学习Django的过程中,在cmd打算使用python manage.py shell来测试数据的时候,当我一导入自己写的model类,就发现报了这个错误django.core.exception ...
- idea导入eclipse项目的配置
idea导入eclipse项目需要的配置 1.配置jdk,这两处选择一样就可以,也可以根据自己需求选择,上边的比下边的版本高就行 2.这里会默认和配置jdk一样 3.添加lib依赖,选择到项目的lib ...
- pythonj基础(五)元组和集合
一,什么是元祖 Python的元组与列表类似,不同之处在于元组的元素不能修改. 元组使用小括号,列表使用方括号. 元组创建很简单,只需要在括号中添加元素,并使用逗号隔开即可. 1.创建一个空元组 tu ...
- Failed to connect to 127.0.0.1 port 1080: Connection refused package 问题解决方法
错误: fatal: unable to access 'https://github.com/******': Failed to connect to 127.0.0.1 port 1080: C ...
- js控制style样式
1.行内样式获取打印出来 2.内嵌和外链的获取不了 <div style="width:200px;height:200px; background: red;">&l ...
一、概述:
IO流是来处理设备间的数据传输
1、特点:
1)流操作按照数据可分为字节流(处理所有的数据)和字符流(处理文字,其中包含编码表,可以指定编码表防止了编码表不同而产生乱码的现象)
2)按照流向分可以分为输出流和输入流。
字节流的抽象基类:InputStream(读)、OutputStream(写)
字符流的抽象基类:Reader(读)、Writer(写)
注:此四个类派生出来的子类名称都是以父类名作为子类名的后缀,以前缀为其功能;
第二 字符流
一、简述:
1、字符流中的对象融合了编码表。使用的是默认的编码,即当前系统的编码。
2、字符流只用于处理文字数据,而字节流可以任何数据。
3、既然IO流是用于操作数据的,那么数据的最常见体现形式是文件。专门用于操作文件的子类对象:FileWriter、FileReader
二、写入字符流
- 数据的续写是通过构造函数 FileWriter(String s,boolean append),根据给定文件名及指示是否附加写入数据的boolean值来构造FileWriter对象。为true时就是续写,为false就是不续写。
- 调用write()方法,将字符串写入到流中。这里他本身没有特定的写方法都是继承自父类的方法有写单个字符:
write(int c),写入字符数组:
write(char[] cbuf)这里的数组一般定义成1024的整数倍,不宜过大,过大容易造成内存溢出。写入字符数组的某一部分:write(char[] cbuf, int off, int len),写入字符串:
write(String str),写入字符串的某一部分:write(String str, int off, int len)
- *
- * 需求:在硬盘上,创建一个文件并写入一些文字数据。
- * 找到一个专门用于操作文件的Writer子类对象。FileWriter。 后缀名是父类名。 前缀名是该流对象的功能。
- */
- public class FileWriterDemo {
- public static void main(String[] args) {
- method();//写内容
- method2();//续写内容
- }
- /*
- * 写数据
- */
- public static void method(){
- FileWriter fw = null;
- try {
- //创建一个FileWriter对象。该对象一被初始化就必须要明确被操作的文件。
- //该文件会被存放在指定的目录下,如果该文件已经存在,会被覆盖
- //其实该步就是在明确数据要存放的目的地。
- fw = new FileWriter("testwrite.txt");// C:\\testwrite.txt
- //调用write方法,将字符串写入到流中
- fw.write("asdsadafsd");
- //刷新流对象中的缓冲中的数据。
- //将数据刷到目的地中。
- // fw.flush();
- //关闭流资源,但是关闭之前会刷新一次内部的缓冲中的数据。
- //将数据刷到目的地中。
- //和flush区别:flush刷新后,流可以继续使用,close刷新后,会将流关闭。
- // fw.close();
- } catch (IOException e) {
- e.printStackTrace();
- }finally{
- try {
- if(fw != null)
- fw.close();
- } catch (IOException e) {
- e.printStackTrace();
- }
- }
- }
- /*
- * 在文件原有内容的基础上续写内容
- */
- public static void method2(){
- FileWriter fw = null;
- try {
- fw = new FileWriter("testwrite.txt", true);
- fw.write("\r\nnihao\r\nxiexie");//\r\n是换行符\n是linux下的换行
- } catch (IOException e) {
- e.printStackTrace();
- }finally{
- if(fw != null){
- try {
- fw.close();
- } catch (IOException e) {
- e.printStackTrace();
- }
- }
- }
- }
- }
三、读取字符流
- 创建一个文件读取流对象,和指定名称的文件相关联。要保证该文件已经存在,若不存在,将会发生异常FileNotFoundException。
- 调用读取流对象的read()方法。read():一次读一个字符,且会继续往下读。(方法1)
read()读取单个字符。read(char[] cbuf)将字符读入数组。(方法2)
其实都是按照每次只读取一个字符的方式读取的,只是读到数组中会把读取到的数据存放在数组中,起到一个临时缓存的作用,提高了读取效率。
演示1:
public static void main(String[] args) throws IOException {
FileReader fr = new FileReader("demo.txt");
int ch = 0;
while((ch=fr.read())!=-1){// abcde读5次
System.out.println((char)ch);
fr.close();
}
演示2:
public class FileReaderDemo2 {
public static void main(String[] args) throws IOException {
FileReader fr = new FileReader("demo.txt");
/*
* 使用read(char[])读取文本文件数据。
*
* 先创建字符数组。
*/
char[] buf = new char[1024];
int len = 0;
while((len=fr.read(buf))!=-1){ // abcde读1次,提高效率,是一个数组一个数组的读
System.out.println(new String(buf,0,len));
}
/*
int num = fr.read(buf);//将读取到的字符存储到数组中。读到几个几个变成数组
System.out.println(num+":"+new String(buf,0,num));
int num1 = fr.read(buf);//将读取到的字符存储到数组中。
System.out.println(num1+":"+new String(buf,0,num1));
int num2 = fr.read(buf);//将读取到的字符存储到数组中。
System.out.println(num2+":"+new String(buf));
*/
fr.close();
- }
四、文件的拷贝:
原理:其实就是将磁盘下的文件数据读取出来,然后写入磁盘的一个文件中
步骤:
1、在硬盘上创建一个文件,用于存储读取出来的文件数据
2、定义读取流和文件关联
3、通过不断读写完成数据存储
方式一:读取一个字符,存入一个字符
方式二:先将读取的数据存入到内存中,再将存入的字符取出写入硬盘
4、关闭流资源:输入流资源和输出流资源。
方法1:
/*
* 思路:
* 1,需要读取源,
* 2,将读到的源数据写入到目的地。
* 3,既然是操作文本数据,使用字符流。
*
*/
public class CopyTextTest {
public static void main(String[] args) throws IOException {
//1,读取一个已有的文本文件,使用字符读取流和文件相关联。
FileReader fr = new FileReader("IO流_2.txt");
//2,创建一个目的,用于存储读到数据。
FileWriter fw = new FileWriter("copytext_1.txt");
//3,频繁的读写操作。
int ch = 0;
while((ch=fr.read())!=-1){
fw.write(ch);
}
//4,关闭流资源。先关写再关读
fw.close();
fr.close();
}
}
方法2:
public class CopyTextTest_2 {
private static final int BUFFER_SIZE = 1024;
public static void main(String[] args) {
FileReader fr = null;
FileWriter fw = null;
try {
fr = new FileReader("IO流_2.txt");
fw = new FileWriter("copytest_2.txt");
//创建一个临时容器,用于缓存读取到的字符。
char[] buf = new char[BUFFER_SIZE];//这就是缓冲区。
//定义一个变量记录读取到的字符数,(其实就是往数组里装的字符个数 )
int len = 0;
while((len=fr.read(buf))!=-1){
fw.write(buf, 0, len);
}
} catch (Exception e) {
// System.out.println("读写失败");
throw new RuntimeException("读写失败");
}finally{
if(fw!=null)
try {
fw.close();
} catch (IOException e) {
e.printStackTrace();
}
if(fr!=null)
try {
fr.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
第三 字符缓冲区
一、写入缓冲区BufferedWriter
/
* 缓冲区的出现就是为了提高操作流的效率
* 所以在创建缓冲区之前必须要先有流对象
*
* 缓冲区提供了一个跨平台的换行符 new Line();
*/
public class BufferedWriterDemo {
public static void main(String[] args) {
method();
}
public static void method() {
// 创建一个字符写入流对象
FileWriter fw = null;
BufferedWriter bfw = null;
try {
fw = new FileWriter("buf.txt");
// 为了提高字符写入流效率。加入了缓冲技术。其实缓冲区就是封装了数组,不用自己定义数组,用起来更方便
// 只要将需要被提高效率的流对象作为参数传递给缓冲区的构造函数即可。
bfw = new BufferedWriter(fw);
for (int x = 0; x < 10; x++) {
bfw.write("abcds" + x);
bfw.newLine();// 换行在windows中相当于\r\n,在linux中相当于\n
}
// 记住,只要用到缓冲区,就要记得刷新。
// bufw.flush();
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
bfw.close();// 关闭缓冲区就是在关闭缓冲区中的流对象,关闭前刷新
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
private static final String LINE_SEPARATOR = System.getProperty("line.separator");
//使用缓冲区的写入方法将数据先写入到缓冲区中。
// bufw.write("abcdefq"+LINE_SEPARATOR+"hahahha");
// bufw.write("xixiixii");
// bufw.newLine(); 行分隔,就不需要LINE_SEPARATOR,不过只在这里有效
// bufw.write("heheheheh");
二、读取流缓冲区BufferedReader
该缓冲区提供了一个一次读一行的方法readLine(),方便与对文本数据的获取,当返回null时,表示读到文件末尾。
readLine()方法返回的时只返回回车符之前的数据内容,并不返回回车符,即读取的内容中不包含任何行终止符(回车符和换行符)。
--->readLine()方法原理:无论是读一行,或读取多个字符,其实最终都是在硬盘上一个一个读取。所以最终使用的还是read方法一次读一个
public class BufferedReaderDemo {
public static void main(String[] args) {
method();
}
public static void method(){
//创建一个读取流对象
FileReader fr = null;
BufferedReader bfr = null;
try {
fr = new FileReader("buf.txt");
//为了提高效率。加入缓冲技术。将字符读取流对象作为参数传递给缓冲对象的构造函数。
bfr = new BufferedReader(fr);
// char[] buf = new char[1024];
// bfr.read(buf);
// int len = 0;
// while((len = bfr.read(buf)) != -1){
// System.out.println(new String(buf,0,len));
// }
String line = null;
while((line = bfr.readLine()) != null){
System.out.println(line);
}
} catch (IOException e) {
e.printStackTrace();
}finally{
try {
bfr.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
三、通过缓冲技术提高效率复制文件:
public class CopyTextByBufTest {
//
不处理异常,抛出去
public static void main(String[] args) throws IOException {
FileReader fr = new FileReader("buf.txt");
BufferedReader bufr = new BufferedReader(fr);
FileWriter fw = new FileWriter("buf_copy.txt");
BufferedWriter bufw = new BufferedWriter(fw);
String line = null;
while ((line = bufr.readLine()) != null) {
bufw.write(line);
bufw.newLine();
bufw.flush();
}
/*
* int ch = 0;
*
* while((ch=bufr.read())!=-1){
*
* bufw.write(ch); }
*/
bufw.close();
bufr.close();
}
}
处理异常try...catch
public class CopyByBuffer {
public static void main(String[] args) {
copyByBuf();
}
public static void copyByBuf() {
FileWriter fw = null;
FileReader fr = null;
BufferedWriter bfw = null;
BufferedReader bfr = null;
try {
fw = new FileWriter("C:\\buffertest.txt");//创建写文件对象
fr = new FileReader("buffertest.txt");//创建读文件对象
bfw = new BufferedWriter(fw);//使用缓冲区关联读写对象
bfr = new BufferedReader(fr);
String line = null;
while ((line = bfr.readLine()) != null) {//通过读一行的方式提高效率
bfw.write(line);
bfw.newLine();//换行,夸平台
}
} catch (IOException e) {
e.printStackTrace();
} finally {
if (bfw != null) {
try {
bfw.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if (bfr != null) {
try {
bfr.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
四、自定义缓冲区
其实就是模拟一个BufferedReader.
* 分析:
* 缓冲区中无非就是封装了一个数组,并对外提供了更多的方法对数组进行访问。 其实这些方法最终操作的都是数组的角标。
*
* 缓冲的原理:
* 其实就是从源中获取一批数据装进缓冲区中。在从缓冲区中不断的取出一个一个数据。
* 在此次取完后,在从源中继续取一批数据进缓冲区。 当源中的数据取光时,用-1作为结束标记。
public class MyBufferedReader extends Reader {
private Reader r;
//定义一个数组作为缓冲区。
private char[] buf = new char[1024];
//定义一个指针用于操作这个数组中的元素。当操作到最后一个元素后,指针应该归零。
private int pos = 0;
//定义一个计数器用于记录缓冲区中的数据个数。 当该数据减到0,就从源中继续获取数据到缓冲区中。
private int count = 0;
MyBufferedReader(Reader r){
this.r = r;
}
/**
* 该方法从缓冲区中一次取一个字符。
* @return
* @throws IOException
*/
/*
public int myRead() throws IOException{
//从源中获取一批数据到缓冲区中。需要先做判断,只有计数器为0时,才需要从源中获取数据。
if(count==0){
count = r.read(buf); //这是在底层
if(count<0)//没有了
return -1;
//每次获取数据到缓冲区后,角标归零(第一个read,底层)创建了一个数组。
pos = 0;
char ch = buf[pos];
// 然后调用一次(第二个)read,返回一个a,再调用返回b,角标取一个,count减一个
pos++;
count--;
return ch;
}
else if(count>0){//第二次调用的时候count就不为0了, 不需要count = r.read(buf)这一步了
char ch = buf[pos];
pos++;
count--;
return ch;
}*/
优化后
public int myRead() throws IOException{
if(count==0){
count = r.read(buf);
pos = 0;
}
if(count<0)
return -1;
char ch = buf[pos++];
count--;
return ch;
}
五、模拟ReadLine
public String myReadLine() throws IOException{
// 定义一个临时容器,原BufferedReader封装的是字符数组,这里定义StringBuilder演示原理
StringBuilder sb = new StringBuilder();
int ch = 0;
while((ch = myRead())!=-1){
if(ch=='\r') //如是'\r'继续往下读
continue;
if(ch=='\n') //读到'\n'就停止读,然后将这些数据返回
return sb.toString();
//将从缓冲区中读到的字符,存储到缓存行数据的缓冲区中。
sb.append((char)ch);
}
if(sb.length()!=0) //最后没有空格,说明有东西,返回,
//这里是防止最后一行没有回车符的情况
return sb.toString();
return null;
}
public void myClose() throws IOException {
r.close();
}
@Override
public int read(char[] cbuf, int off, int len) throws IOException {
return 0;
}
@Override
public void close() throws IOException {
}
}
运行 ReadLine
public class MyBufferedReaderDemo {
public static void main(String[] args) throws IOException {
FileReader fr = new FileReader("buf.txt");
MyBufferedReader bufr = new MyBufferedReader(fr);
String line = null;
while((line=bufr.myReadLine())!=null){
System.out.println(line);
}
bufr.myClose();
Collections.reverseOrder();
HashMap map = null;
map.values();
}
}
六、LineNumberReader(功能增强的读)
在BufferedReader中有个直接的子类LineNumberReader,其中有特有的方法获取和设置行号:
setLineNumber()和getLineNumber()
public class LineNumberReaderDemo {
public static void main(String[] args) throws IOException {
FileReader fr = new FileReader("IO流_2.txt");
LineNumberReader lnr = new LineNumberReader(fr);
String line = null;
lnr.setLineNumber(100);// 默认是0
while ((line = lnr.readLine()) != null) {
System.out.println(lnr.getLineNumber() + ":" + line);
}
lnr.close();
}
}
第四 装饰设计模式
装饰设计模式: (buffer就是装饰设计模式)
对一组对象的功能进行增强时,就可以使用该模式进行问题的解决。
装饰和继承都能实现一样的特点:进行功能的扩展增强。
有什么区别呢?
首先有一个继承体系。
Writer (抽取了一个写的)
|--TextWriter (随便写的):用于操作文本
|--MediaWriter:用于操作媒体。
想要对操作的动作进行效率的提高。
按照面向对象,可以通过继承对具体的进行功能的扩展。
效率提高需要加入缓冲技术。
Writer
|--TextWriter:用于操作文本
|--BufferTextWriter:加入了缓冲技术的操作文本的对象。
|--MediaWriter:用于操作媒体。
|--BufferMediaWriter:
到这里就哦了。但是这样做好像并不理想。
如果这个体系进行功能扩展,有多了流对象。
那么这个流要提高效率,是不是也要产生子类呢?是。这时就会发现只为提高功能,进行的继承,
导致继承体系越来越臃肿。不够灵活。
重新思考这个问题?
既然加入的都是同一种技术--缓冲。
前一种是让缓冲和具体的对象相结合。
可不可以将缓冲进行单独的封装,哪个对象需要缓冲就将哪个对象和缓冲关联。
class Buffer {
Buffer(TextWriter w) // 要缓冲谁就加入进来
{
}
Buffer(MediaWirter w) // 要缓冲谁就加入进来
{
}
} // 但是这样做,来新对象还得加入,比较麻烦
// 都能写入,为了名字有意义,写成BufferWriter
class BufferWriter extends Writer {
BufferWriter(Writer w) {
}
}
Writer
|--TextWriter:用于操作文本
|--MediaWriter:用于操作媒体。
|--BufferWriter:用于提高效率。
装饰比继承灵活。
特点:装饰类和被装饰类都必须所属同一个接口或者父类。
有个类想要增强,可以用装饰设计模式,把被装饰的类往里传进来就可以
第五 字节流
一、概述:
1、字节流和字符流的原理是相似的,而字符流是基于字节流的,字节流可以操作如媒体等其他数据,如媒体(MP3,图片,视频)等
2、由于媒体数据中都是以字节存储的,所以,字节流对象可直接对媒体进行操作,而不用再进行刷流动作。
3、InputStream ---> 输入流(读) OutputStream ---> 输出流(写)
4、为何不用进行刷流动作:因为字节流操作的是字节,即数据的最小单位,不需要像字符流一样要进行转换为字节。可直接将字节写入到指定文件中,但是需要在写代码的时候,如果有字符串,要将字符串转为字节数组再进行操作。
5、FileInputStream特有方法:int available() ---> 返回数据字节的长度,包含终止符
在定义字节数组长度的时候,可以用到这个方法:byte[] = new byte[fos.available()] (fos为字节流对象)但是,对于这个方法要慎用,如果字节过大,超过jvm所承受的大小(一般内存为64M),就会内存溢出。
实例:
/
* 字节流操作
* InputStream OutputStream
*
*/
public class FileStreamDemo {
public static void main(String[] args) {
// writeFile();
// readFile_1();
// readFile_2();
readFile_3();
}
/*
* 对字节流读操作的第一种方式 通过read()方法读取一个字节
*/
public static void readFile_1() {
FileInputStream fis = null;// 定义字节输入流
try {
fis = new FileInputStream("fos.txt");// 指定输入流和文件关联
int ch = 0;
while ((ch = fis.read()) != -1) {// 读取操作
System.out.println((char) ch);
}
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
if (fis != null)// 判空,提高程序的健壮性
fis.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
/*
* 对字节流读操作的第二种方式 通过read(byte[])方法读取字节数组
*/
public static void readFile_2() {
FileInputStream fis = null;
try {
fis = new FileInputStream("fos.txt");
byte[] byf = new byte[1024];
int len = 0;
while ((len = fis.read(byf)) != -1) {
System.out.println(new String(byf, 0, len));
}
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
if (fis != null)
fis.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
/*
* 对字节流读操作的第三种方式 通过字节流的available()方法获取到文件大小,定义一个大小刚刚好的数组,无需循环 但是,这种方式操作较大数据时容易内存溢出,所以要慎用 首选的还是定义1024的整数倍数组
*/
public static void readFile_3() {
FileInputStream fis = null;
try {
fis = new FileInputStream("fos.txt");
// 通过这样的方式定义一个刚刚好的数组
// 这种方法慎用,如果文件不大,可以用这个方法
byte[] byf = new byte[fis.available()];// fis.available()获取文件大小
fis.read(byf);
System.out.println(new String(byf));
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
fis.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
/*
* 对字节流进行写操作
*/
public static void writeFile() {
FileOutputStream fos = null;
try {
fos = new FileOutputStream("fos.txt");//定义字节写出流和文件关联
fos.write("abcds".getBytes());// str.getBytes()将String转化成字节数组
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} finally {
if (fos != null)
try {
fos.close();// 因为字节流没有用到缓冲区,所以不需要刷新,但必须关闭资源
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
二、通过字节流复制媒体文件
思路:
1、用字节流读取流对象和媒体文件相关联
2、用字节写入流对象,创建一个媒体文件,用于存储获取到的媒体文件数据
3、通过循环读写,完成数据的存储
4、关闭资源
* 通过字节流拷贝图片
*/
public class CopyPicture {
public static void main(String[] args) {
copyPic();
}
public static void copyPic() {
FileInputStream fis = null;
FileOutputStream fos = null;
try {
fis = new FileInputStream("1.jpg");
fos = new FileOutputStream("C:\\1.jpg");
byte[] byf = new byte[1024];
int len = 0;
while ((len = fis.read(byf)) != -1) {// 将数据读取到数组中
fos.write(byf, 0, len);// 写入数组中的有效数据
}
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} finally {
// 有多个流不能一起关闭,要分别关闭
if (fis != null) {
try {
fis.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if (fos != null) {
try {
fos.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
// 千万不要用,效率没有!
public static void copy_4() throws IOException {
FileInputStream fis = new FileInputStream("c:\\0.mp3");
FileOutputStream fos = new FileOutputStream("c:\\4.mp3");
int ch = 0;
while((ch =fis.read())!=-1){
fos.write(ch);
}
fos.close();
fis.close();
}
//不建议。
public static void copy_3() throws IOException {
FileInputStream fis = new FileInputStream("c:\\0.mp3");
FileOutputStream fos = new FileOutputStream("c:\\3.mp3");
byte[] buf = new byte[fis.available()];
fis.read(buf);
fos.write(buf);
fos.close();
fis.close();
}
//提高效率
public static void copy_2() throws IOException {
FileInputStream fis = new FileInputStream("c:\\0.mp3");
BufferedInputStream bufis = new BufferedInputStream(fis);
FileOutputStream fos = new FileOutputStream("c:\\2.mp3");
BufferedOutputStream bufos = new BufferedOutputStream(fos);
int ch = 0;
while((ch=bufis.read())!=-1){
bufos.write(ch);
}
bufos.close();
bufis.close();
}
三、字节流缓冲区
1、缓冲区的出现无非就是提高了流的读写效率,当然也就是因为这样,所以缓冲区的使用频率也是相当高的,所以要做必要性的掌握。
2、字节流缓冲区读写的特点:
这里没有什么特殊的读写方法,就是read()读一个字节,read(byte[])读数组的方法。写write(int b)写一个自己,write(byte[])写数组的方法。
注:需要注意个地方,write方法只写出二进制的最后八位。
原理:将数据拷贝一部分,读取一部分,循环,直到数据全部读取完毕。
1)先从数据中抓取固定数组长度的字节,存入定义的数组中,再通过然后再通过read()方法读取数组中的元素,存入缓冲区
2)循环这个动作,知道最后取出一组数据存入数组,可能数组并未填满,同样也取出包含的元素
3)每次取出的时候,都有一个指针在移动,取到数组结尾就自动回到数组头部,这样指针在自增
4)取出的时候,数组中的元素再减少,取出一个,就减少一个,直到减到0即数组取完
5)到了文件的结尾处,存入最后一组数据,当取完数组中的元素,就会减少到0,这是全部数据就取完了
3、自定义字节缓冲区:
* 自定义字节流缓冲区
*/
public class MyBufferedInputStream {
private InputStream in;
private byte[] buf = new byte[1024];
private int pos;// 定义数组的指针
private int count;// 定义计数器
MyBufferedInputStream(InputStream in) {
this.in = in;
}
// 一次读一个字节,从缓冲区(数组)中取得
public int myRead() throws IOException {
// 通过in对象读取数据并存放在buf数组中
if (count == 0) {
count = in.read(buf);
if (count < 0) {
return -1;
}
pos = 0;
byte b = buf[pos];
count--;
pos++;
return b & 255;// &255是因为存放的是二进制,也就是可能前八位是11111111,提升为int还是-1,就直接return了,
// &255就是让前面补0
/*
* 11111111 11111111 11111111 11111111
* &00000000 00000000 00000000 11111111
* ------------------------------------
* 00000000 00000000 00000000 11111111
*/
}else if(count>0){
byte b = buf[pos];
count--;
pos++;
return b&255;//&0xff
}
return -1;
}
public void myClose() throws IOException{
in.close();
}
}
注:取出的是byte型,返回的是int型,这里存在提升的动作,
当byte中的八位全为1的时候是byte的-1,提升为int类型,就变为int型的-1,为-1时程序就停止循环了,read循环条件就结束了,变为-1的原因是由于在提升时,将byte的八位前都补的是1,即32位的数都是1,即为int型的-1了。如何保证提升后的最后八位仍为1呢?就需要将前24位补0,就可以保留原字节数据不变,又可以避免转为int型出现-1的情况;
那么要如何做呢?
这就需要将提升为int的数据和前24位为0,后八位仍为原字节数据的这个值做与运算。即和255做与运算即可。说到了这里应该也明白了为什么Read方法返回值为int类型了。
第六 转换流
转换流:转换流可以实现字节数据和字符数据的相互转换方便与操作,而且在转换的时候可以指定编码,这也是该流最具特色的地方。:
InputStreamReader 是字节流通向字符流的桥梁
OutputStreamWriter 是字符流通向字节流的桥梁
转换流的子类和转换流的区别?
InputStreamReader 字节-->字符
|--FileReader : 字节流+本地默认码表。
OutputStreamWriter 字符-->字节
|--FileWriter
什么时候使用转换流呢?
1,源或者目的对应的设备是字节流,但是操作的却是文本数据,可以使用转换作为桥梁。
提高对文本操作的便捷。
2,一旦操作文本涉及到具体的指定编码表时,必须使用转换流 。
BufferedWriter bufw = new BufferedWriter(new OutputStreamWriter(new FileOutputStream("a.txt"),charsetName));
- //获取键盘录入对象
InputStream in = System.in;
//将字节流对象转换成字符流对象
InputStreamReader isr = new InputStreamReader(in);
//将字符流对象用缓冲技术高效处理
BufferedReader bufr = new BufferedReader(isr);
// 键盘录入最常见的写法
BufferedReader bufr = new BufferedReader(new InputStreamReader(System.in));
字符流转字节流:
-
// 操作输出
// 获取输出流
OutputStream out = System.out;
//将字符流转换成字节流,OutputStreamWriter字符流通向字节流的桥梁
OutputStreamWriter osw = new OutputStreamWriter(out);
BufferedWriter bfw = new BufferedWriter(osw);//高效缓冲区
获取键盘输入:键盘输入都是字节,所以用到字节流与标准输入流相关联就可以把输入的数据获取到流中,以达到数据的操作效果。
* 获取键盘录入
public class ReadIn {
public static void main(String[] args) {
// method_1();
// method_2();
InputStreamReaderDemo();
}
/*
* 获取键盘录入
*/
public static void method_1() {
InputStream in = System.in;// 定义输入流与键盘输入流相关联
int ch = 0;
try {
while ((ch = in.read()) != -1) {
System.out.println(ch);
}
} catch (IOException e) {
e.printStackTrace();
}
}
/*
* 需求:通过键盘录入数据。 当录入一行数据后,就将该行数据进行打印。 如果录入的数据是over,那么停止录入。
* 1,因为键盘录入只读取一个字节,要判断是否是over,需要将读取到的字节拼成字符串。
* 2,那就需要一个容器。StringBuilder.
* 3,在用户回车之前将录入的数据变成字符串判断即可。
*/
public static void method_2() {
InputStream in = System.in;
StringBuilder sb = new StringBuilder();// 定义一个临时容器
while (true) {
int ch = 0;
try {
ch = in.read();
if (ch == '\r')// windows中换行符为\r\n
continue;
if (ch == '\n') {
String s = sb.toString();// 当读到一行的结束标记时,把改行数据变成字符串
if ("over".equals(s))
break;
System.out.println(s.toUpperCase()); //变成大写
sb.delete(0, sb.length());// 清空容器 ,不清空会将上次输入的和这次输入的都输出
} else {
sb.append((char) ch); //将读取到的字节存储到StringBuilder中。
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
// **************************************************************************
/*
* 通过上面录入一行数据,发现其方法类似于readLine方法 但是readLine方法是字符流缓冲区的方法,所以需要把字节流转换成字符流进行操作
* 需要用到InputStreamReader方法进行转换,InputStreamReader字节通向字符的桥梁
*
* 转换流
*/
public static void InputStreamReaderDemo() {
BufferedReader bufr = null;
try {
// 源为文件
bufr = new BufferedReader(new InputStreamReader(
new FileInputStream("buf.txt")));
} catch (FileNotFoundException e2) {
e2.printStackTrace();
}
// 操作输出
// 目的地是控制台
BufferedWriter bfw = new BufferedWriter(new OutputStreamWriter(
System.out));
// 目的地是文件
// BufferedWriter bfw = null;
// try {
// bfw = new BufferedWriter(new OutputStreamWriter(new
// FileOutputStream("out.txt")));
// } catch (FileNotFoundException e1) {
// e1.printStackTrace();
// }
String line = null;
try {
while ((line = bufr.readLine()) != null) {
if ("over".equals(line)) {
break;
}
bfw.write(line.toUpperCase());
bfw.newLine();// 换行
bfw.flush();// 数据存放在缓冲区,所以需要刷新
}
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
bufr.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
不处理异常
public class TransStreamDemo2 {
public static void main(String[] args) throws IOException {
/*
* 1,需求:将键盘录入的数据写入到一个文件中。
*
* 2,需求:将一个文本文件内容显示在控制台上。
*
* 3,需求:将一个文件文件中的内容复制到的另一个文件中。
*/
BufferedReader bufr = new BufferedReader(new InputStreamReader(System.in));
BufferedWriter bufw = new BufferedWriter(new OutputStreamWriter(new FileOutputStream("b.txt")));
String line = null;
while((line=bufr.readLine())!=null){
if("over".equals(line))
break;
bufw.write(line.toUpperCase()); //变成大写
bufw.newLine();
bufw.flush();
}
}
}
第七 流的操作规律
之所以要弄清楚这个规律,是因为流对象太多,开发时不知道用哪个对象合适。
想要知道开发时用到哪些对象。只要通过四个明确即可。
1,明确源和目的(汇)
源:InputStream Reader
目的:OutputStream Writer
2,明确数据是否是纯文本数据。
源:是纯文本:Reader
否:InputStream
目的:是纯文本 Writer
否:OutputStream
到这里,就可以明确需求中具体要使用哪个体系。
3,明确具体的设备。
源设备:
硬盘:File
键盘:System.in
内存:数组
网络:Socket流
目的设备:
硬盘:File
控制台:System.out
内存:数组
网络:Socket流
4,是否需要其他额外功能。
1,是否需要高效(缓冲区);
是,就加上buffer.
2,转换。
需求1:复制一个文本文件。
1,明确源和目的。
源:InputStream Reader
目的:OutputStream Writer
2,是否是纯文本?
是!
源:Reader
目的:Writer
3,明确具体设备。
源:
硬盘:File
目的:
硬盘:File
FileReader fr = new FileReader("a.txt");
FileWriter fw = new FileWriter("b.txt");
4,需要额外功能吗?
需要,需要高效。
BufferedReader bufr = new BufferedReader(new FileReader("a.txt"));
BufferedWriter bufw = new BufferedWriter(new FileWriter("b.txt"));
================================================
需求2:读取键盘录入信息,并写入到一个文件中。
1,明确源和目的。
源:InputStream Reader
目的:OutputStream Writer
2,是否是纯文本呢?
是,
源:Reader
目的:Writer
3,明确设备
源:
键盘。System.in
目的:
硬盘。File
InputStream in = System.in;
FileWriter fw = new FileWriter("b.txt");
这样做可以完成,但是麻烦。将读取的字节数据转成字符串。再由字符流操作。
4,需要额外功能吗?
需要。转换。 将字节流转成字符流。因为名确的源是Reader,这样操作文本数据做便捷。
所以要将已有的字节流转成字符流。使用字节-->字符 。InputStreamReader
InputStreamReader isr = new InputStreamReader(System.in);
FileWriter fw = new FileWriter("b.txt");
还需要功能吗?
需要:想高效。
BufferedReader bufr = new BufferedReader(new InputStreamReader(System.in));
BufferedWriter bufw = new BufferedWriter(new FileWriter("b.txt"));
===================================================
需求3:将一个文本文件数据显示在控制台上。
1,明确源和目的。
源:InputStream Reader
目的:OutputStream Writer
2,是否是纯文本呢?
是,
源:Reader
目的:Writer
3,明确具体设备
源:
硬盘:File
目的:
控制台:System.out
FileReader fr = new FileReader("a.txt");
OutputStream out = System.out;//PrintStream
4,需要额外功能吗?
需要,转换。
FileReader fr= new FileReader("a.txt");
OutputStreamWriter osw = new OutputStreamWriter(System.out);
需要,高效。
BufferedReader bufr = new BufferedReader(new FileReader("a.txt"));
BufferedWriter bufw = new BufferedWriter(new OutputStreamWriter(System.out));
================================================================
需求4:读取键盘录入数据,显示在控制台上。
1,明确源和目的。
源:InputStream Reader
目的:OutputStream Writer
2,是否是纯文本呢?
是,
源:Reader
目的:Writer
3,明确设备。
源:
键盘:System.in
目的:
控制台:System.out
InputStream in = System.in;
OutputStream out = System.out;
4,明确额外功能?
需要转换,因为都是字节流,但是操作的却是文本数据。
所以使用字符流操作起来更为便捷。
InputStreamReader isr = new InputStreamReader(System.in);
OutputStreamWriter osw = new OutputStreamWriter(System.out);
为了将其高效。
BufferedReader bufr = new BufferedReader(new InputStreamReader(System.in));
BufferedWriter bufw = new BufferedWriter(new OutputStreamWriter(System.out));
============================================================
5,将一个中文字符串数据按照指定的编码表写入到一个文本文件中.
1,目的。OutputStream,Writer
2,是纯文本,Writer。
3,设备:硬盘File
FileWriter fw = new FileWriter("a.txt");
fw.write("你好");
注意:既然需求中已经明确了指定编码表的动作。
那就不可以使用FileWriter,因为FileWriter内部是使用默认的本地码表。
只能使用其父类。OutputStreamWriter.
OutputStreamWriter接收一个字节输出流对象,既然是操作文件,那么该对象应该是FileOutputStream
OutputStreamWriter osw = new OutputStreamWriter(new FileOutputStream("a.txt"),charsetName(编码));
需要高效吗?
BufferedWriter bufw = new BufferedWriter(new OutputStreamWriter(new FileOutputStream("a.txt"),charsetName));
演示:将一个中文字符串数据按照指定的编码表写入到一个文本文件中.
写:
public static void writeText_2() throws IOException {
OutputStreamWriter osw = new OutputStreamWriter(new FileOutputStream(
"gbk_3.txt"), "GBK");
// OutputStreamWriter osw = new OutputStreamWriter(new
// FileOutputStream("gbk_3.txt"),"GBK");
// FileWriter fw = new FileWriter("gbk_1.txt");
/*
* 这两句代码的功能是等同的。 FileWriter:其实就是转换流指定了本机默认码表的体现。而且这个转换流的子类对象,可以方便操作文本文件。
* 简单说:操作文件的字节流+本机默认的编码表。 这是按照默认码表来操作文件的便捷类。
*
* 如果操作文本文件需要明确具体的编码。FileWriter就不行了。必须用转换流
*/
osw.write("你好");
osw.close();
}
public static void writeText_3() throws IOException {
OutputStreamWriter osw = new OutputStreamWriter(new FileOutputStream(
"u8_1.txt"), "UTF-8");
osw.write("你好");
osw.close();
}
读:
public static void readText_1() throws IOException {
FileReader fr = new FileReader("gbk_1.txt");
char[] buf = new char[10];
int len = fr.read(buf);
String str = new String(buf,0,len);
System.out.println(str);
fr.close();
}
//用utf-8读默认的编码文件
public static void readText_2() throws IOException, FileNotFoundException {
InputStreamReader isr = new InputStreamReader(new FileInputStream("gbk_1.txt"),"utf-8");
char[] buf = new char[10];
int len = isr.read(buf);
String str = new String(buf,0,len);
System.out.println(str);
isr.close();
}
}
}
一、简述:
1、字符流中的对象融合了编码表。使用的是默认的编码,即当前系统的编码。
2、字符流只用于处理文字数据,而字节流可以任何数据。
3、既然IO流是用于操作数据的,那么数据的最常见体现形式是文件。专门用于操作文件的子类对象:FileWriter、FileReader
二、写入字符流
- 数据的续写是通过构造函数 FileWriter(String s,boolean append),根据给定文件名及指示是否附加写入数据的boolean值来构造FileWriter对象。为true时就是续写,为false就是不续写。
- 调用write()方法,将字符串写入到流中。这里他本身没有特定的写方法都是继承自父类的方法有写单个字符:
write(int c),写入字符数组:
write(char[] cbuf)这里的数组一般定义成1024的整数倍,不宜过大,过大容易造成内存溢出。写入字符数组的某一部分:write(char[] cbuf, int off, int len),写入字符串:
write(String str),写入字符串的某一部分:write(String str, int off, int len)
- *
- * 需求:在硬盘上,创建一个文件并写入一些文字数据。
- * 找到一个专门用于操作文件的Writer子类对象。FileWriter。 后缀名是父类名。 前缀名是该流对象的功能。
- */
- public class FileWriterDemo {
- public static void main(String[] args) {
- method();//写内容
- method2();//续写内容
- }
- /*
- * 写数据
- */
- public static void method(){
- FileWriter fw = null;
- try {
- //创建一个FileWriter对象。该对象一被初始化就必须要明确被操作的文件。
- //该文件会被存放在指定的目录下,如果该文件已经存在,会被覆盖
- //其实该步就是在明确数据要存放的目的地。
- fw = new FileWriter("testwrite.txt");// C:\\testwrite.txt
- //调用write方法,将字符串写入到流中
- fw.write("asdsadafsd");
- //刷新流对象中的缓冲中的数据。
- //将数据刷到目的地中。
- // fw.flush();
- //关闭流资源,但是关闭之前会刷新一次内部的缓冲中的数据。
- //将数据刷到目的地中。
- //和flush区别:flush刷新后,流可以继续使用,close刷新后,会将流关闭。
- // fw.close();
- } catch (IOException e) {
- e.printStackTrace();
- }finally{
- try {
- if(fw != null)
- fw.close();
- } catch (IOException e) {
- e.printStackTrace();
- }
- }
- }
- /*
- * 在文件原有内容的基础上续写内容
- */
- public static void method2(){
- FileWriter fw = null;
- try {
- fw = new FileWriter("testwrite.txt", true);
- fw.write("\r\nnihao\r\nxiexie");//\r\n是换行符\n是linux下的换行
- } catch (IOException e) {
- e.printStackTrace();
- }finally{
- if(fw != null){
- try {
- fw.close();
- } catch (IOException e) {
- e.printStackTrace();
- }
- }
- }
- }
- }
三、读取字符流
- 创建一个文件读取流对象,和指定名称的文件相关联。要保证该文件已经存在,若不存在,将会发生异常FileNotFoundException。
- 调用读取流对象的read()方法。read():一次读一个字符,且会继续往下读。(方法1)
read()读取单个字符。
其实都是按照每次只读取一个字符的方式读取的,只是读到数组中会把读取到的数据存放在数组中,起到一个临时缓存的作用,提高了读取效率。read(char[] cbuf)将字符读入数组。(方法2)
演示1:
public static void main(String[] args) throws IOException {
FileReader fr = new FileReader("demo.txt");
int ch = 0;
while((ch=fr.read())!=-1){// abcde读5次
System.out.println((char)ch);
fr.close();
}
演示2:
public class FileReaderDemo2 {
public static void main(String[] args) throws IOException {
FileReader fr = new FileReader("demo.txt");
/*
* 使用read(char[])读取文本文件数据。
*
* 先创建字符数组。
*/
char[] buf = new char[1024];
int len = 0;
while((len=fr.read(buf))!=-1){ // abcde读1次,提高效率,是一个数组一个数组的读
System.out.println(new String(buf,0,len));
}
/*
int num = fr.read(buf);//将读取到的字符存储到数组中。读到几个几个变成数组
System.out.println(num+":"+new String(buf,0,num));
int num1 = fr.read(buf);//将读取到的字符存储到数组中。
System.out.println(num1+":"+new String(buf,0,num1));
int num2 = fr.read(buf);//将读取到的字符存储到数组中。
System.out.println(num2+":"+new String(buf));
*/
- }
四、文件的拷贝:
原理:其实就是将磁盘下的文件数据读取出来,然后写入磁盘的一个文件中
步骤:
1、在硬盘上创建一个文件,用于存储读取出来的文件数据
2、定义读取流和文件关联
3、通过不断读写完成数据存储
方式一:读取一个字符,存入一个字符
方式二:先将读取的数据存入到内存中,再将存入的字符取出写入硬盘
4、关闭流资源:输入流资源和输出流资源。
方法1:
/*
* 思路:
* 1,需要读取源,
* 2,将读到的源数据写入到目的地。
* 3,既然是操作文本数据,使用字符流。
*
*/
public class CopyTextTest {
public static void main(String[] args) throws IOException {
//1,读取一个已有的文本文件,使用字符读取流和文件相关联。
FileReader fr = new FileReader("IO流_2.txt");
//2,创建一个目的,用于存储读到数据。
FileWriter fw = new FileWriter("copytext_1.txt");
//3,频繁的读写操作。
int ch = 0;
while((ch=fr.read())!=-1){
fw.write(ch);
}
//4,关闭流资源。先关写再关读
fw.close();
fr.close();
}
}
方法2:
public class CopyTextTest_2 {
private static final int BUFFER_SIZE = 1024;
public static void main(String[] args) {
FileReader fr = null;
FileWriter fw = null;
try {
fr = new FileReader("IO流_2.txt");
fw = new FileWriter("copytest_2.txt");
//创建一个临时容器,用于缓存读取到的字符。
char[] buf = new char[BUFFER_SIZE];//这就是缓冲区。
//定义一个变量记录读取到的字符数,(其实就是往数组里装的字符个数 )
int len = 0;
while((len=fr.read(buf))!=-1){
fw.write(buf, 0, len);
}
} catch (Exception e) {
// System.out.println("读写失败");
throw new RuntimeException("读写失败");
}finally{
if(fw!=null)
try {
fw.close();
} catch (IOException e) {
e.printStackTrace();
}
if(fr!=null)
try {
fr.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
第三 字符缓冲区
一、写入缓冲区BufferedWriter
/
* 缓冲区的出现就是为了提高操作流的效率
* 所以在创建缓冲区之前必须要先有流对象
*
* 缓冲区提供了一个跨平台的换行符 new Line();
*/
public class BufferedWriterDemo {
public static void main(String[] args) {
method();
}
public static void method() {
// 创建一个字符写入流对象
FileWriter fw = null;
BufferedWriter bfw = null;
try {
fw = new FileWriter("buf.txt");
// 为了提高字符写入流效率。加入了缓冲技术。其实缓冲区就是封装了数组,不用自己定义数组,用起来更方便
// 只要将需要被提高效率的流对象作为参数传递给缓冲区的构造函数即可。
bfw = new BufferedWriter(fw);
for (int x = 0; x < 10; x++) {
bfw.write("abcds" + x);
bfw.newLine();// 换行在windows中相当于\r\n,在linux中相当于\n
}
// 记住,只要用到缓冲区,就要记得刷新。
// bufw.flush();
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
bfw.close();// 关闭缓冲区就是在关闭缓冲区中的流对象,关闭前刷新
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
private static final String LINE_SEPARATOR = System.getProperty("line.separator");
//使用缓冲区的写入方法将数据先写入到缓冲区中。
// bufw.write("abcdefq"+LINE_SEPARATOR+"hahahha");
// bufw.write("xixiixii");
// bufw.newLine(); 行分隔,就不需要LINE_SEPARATOR,不过只在这里有效
// bufw.write("heheheheh");
该缓冲区提供了一个一次读一行的方法readLine(),方便与对文本数据的获取,当返回null时,表示读到文件末尾。
readLine()方法返回的时只返回回车符之前的数据内容,并不返回回车符,即读取的内容中不包含任何行终止符(回车符和换行符)。
--->readLine()方法原理:无论是读一行,或读取多个字符,其实最终都是在硬盘上一个一个读取。所以最终使用的还是read方法一次读一个
public class BufferedReaderDemo {
public static void main(String[] args) {
method();
}
public static void method(){
//创建一个读取流对象
FileReader fr = null;
BufferedReader bfr = null;
try {
fr = new FileReader("buf.txt");
//为了提高效率。加入缓冲技术。将字符读取流对象作为参数传递给缓冲对象的构造函数。
bfr = new BufferedReader(fr);
// char[] buf = new char[1024];
// bfr.read(buf);
// int len = 0;
// while((len = bfr.read(buf)) != -1){
// System.out.println(new String(buf,0,len));
// }
String line = null;
while((line = bfr.readLine()) != null){
System.out.println(line);
}
} catch (IOException e) {
e.printStackTrace();
}finally{
try {
bfr.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
public class CopyTextByBufTest {
//
不处理异常,抛出去public static void main(String[] args) throws IOException {
FileReader fr = new FileReader("buf.txt");
BufferedReader bufr = new BufferedReader(fr);
FileWriter fw = new FileWriter("buf_copy.txt");
BufferedWriter bufw = new BufferedWriter(fw);
String line = null;
while ((line = bufr.readLine()) != null) {
bufw.write(line);
bufw.newLine();
bufw.flush();
}
/*
* int ch = 0;
*
* while((ch=bufr.read())!=-1){
*
* bufw.write(ch); }
*/
bufw.close();
bufr.close();
}
}
public class CopyByBuffer {
public static void main(String[] args) {
copyByBuf();
}
public static void copyByBuf() {
FileWriter fw = null;
FileReader fr = null;
BufferedWriter bfw = null;
BufferedReader bfr = null;
try {
fw = new FileWriter("C:\\buffertest.txt");//创建写文件对象
fr = new FileReader("buffertest.txt");//创建读文件对象
bfw = new BufferedWriter(fw);//使用缓冲区关联读写对象
bfr = new BufferedReader(fr);
String line = null;
while ((line = bfr.readLine()) != null) {//通过读一行的方式提高效率
bfw.write(line);
bfw.newLine();//换行,夸平台
}
} catch (IOException e) {
e.printStackTrace();
} finally {
if (bfw != null) {
try {
bfw.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if (bfr != null) {
try {
bfr.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
public class MyBufferedReader extends Reader {
private Reader r;
//定义一个数组作为缓冲区。
private char[] buf = new char[1024];
//定义一个指针用于操作这个数组中的元素。当操作到最后一个元素后,指针应该归零。
private int pos = 0;
//定义一个计数器用于记录缓冲区中的数据个数。 当该数据减到0,就从源中继续获取数据到缓冲区中。
private int count = 0;
MyBufferedReader(Reader r){
this.r = r;
}
/**
* 该方法从缓冲区中一次取一个字符。
* @return
* @throws IOException
*/
/*
public int myRead() throws IOException{
//从源中获取一批数据到缓冲区中。需要先做判断,只有计数器为0时,才需要从源中获取数据。
if(count==0){
count = r.read(buf); //这是在底层
if(count<0)//没有了
return -1;
//每次获取数据到缓冲区后,角标归零(第一个read,底层)创建了一个数组。
pos = 0;
char ch = buf[pos];
// 然后调用一次(第二个)read,返回一个a,再调用返回b,角标取一个,count减一个
pos++;
count--;
return ch;
}
else if(count>0){//第二次调用的时候count就不为0了, 不需要count = r.read(buf)这一步了
char ch = buf[pos];
pos++;
count--;
return ch;
}*/
优化后
public int myRead() throws IOException{
if(count==0){
count = r.read(buf);
pos = 0;
}
if(count<0)
return -1;
char ch = buf[pos++];
count--;
return ch;
}
public String myReadLine() throws IOException{
// 定义一个临时容器,原BufferedReader封装的是字符数组,这里定义StringBuilder演示原理
StringBuilder sb = new StringBuilder();
int ch = 0;
while((ch = myRead())!=-1){
if(ch=='\r') //如是'\r'继续往下读
continue;
if(ch=='\n') //读到'\n'就停止读,然后将这些数据返回
return sb.toString();
//将从缓冲区中读到的字符,存储到缓存行数据的缓冲区中。
sb.append((char)ch);
}
if(sb.length()!=0) //最后没有空格,说明有东西,返回,
//这里是防止最后一行没有回车符的情况
return sb.toString();
return null;
}
public void myClose() throws IOException {
r.close();
}
@Override
public int read(char[] cbuf, int off, int len) throws IOException {
return 0;
}
@Override
public void close() throws IOException {
}
}
运行 ReadLine
public class MyBufferedReaderDemo {
public static void main(String[] args) throws IOException {
FileReader fr = new FileReader("buf.txt");
MyBufferedReader bufr = new MyBufferedReader(fr);
String line = null;
while((line=bufr.myReadLine())!=null){
System.out.println(line);
}
bufr.myClose();
Collections.reverseOrder();
HashMap map = null;
map.values();
}
}
在BufferedReader中有个直接的子类LineNumberReader,其中有特有的方法获取和设置行号:
setLineNumber()和getLineNumber()
public class LineNumberReaderDemo {
public static void main(String[] args) throws IOException {
FileReader fr = new FileReader("IO流_2.txt");
LineNumberReader lnr = new LineNumberReader(fr);
String line = null;
lnr.setLineNumber(100);// 默认是0
while ((line = lnr.readLine()) != null) {
System.out.println(lnr.getLineNumber() + ":" + line);
}
lnr.close();
}
}
第四 装饰设计模式
class Buffer {
Buffer(TextWriter w) // 要缓冲谁就加入进来
{
}
Buffer(MediaWirter w) // 要缓冲谁就加入进来
{
}
} // 但是这样做,来新对象还得加入,比较麻烦
// 都能写入,为了名字有意义,写成BufferWriter
class BufferWriter extends Writer {
BufferWriter(Writer w) {
}
}
第五 字节流
一、概述:
1、字节流和字符流的原理是相似的,而字符流是基于字节流的,字节流可以操作如媒体等其他数据,如媒体(MP3,图片,视频)等
2、由于媒体数据中都是以字节存储的,所以,字节流对象可直接对媒体进行操作,而不用再进行刷流动作。
3、InputStream ---> 输入流(读) OutputStream ---> 输出流(写)
4、为何不用进行刷流动作:因为字节流操作的是字节,即数据的最小单位,不需要像字符流一样要进行转换为字节。可直接将字节写入到指定文件中,但是需要在写代码的时候,如果有字符串,要将字符串转为字节数组再进行操作。
5、FileInputStream特有方法:int available() ---> 返回数据字节的长度,包含终止符
在定义字节数组长度的时候,可以用到这个方法:byte[] = new byte[fos.available()] (fos为字节流对象)但是,对于这个方法要慎用,如果字节过大,超过jvm所承受的大小(一般内存为64M),就会内存溢出。
/
* 字节流操作
* InputStream OutputStream
*
*/
public class FileStreamDemo {
public static void main(String[] args) {
// writeFile();
// readFile_1();
// readFile_2();
readFile_3();
}
/*
* 对字节流读操作的第一种方式 通过read()方法读取一个字节
*/
public static void readFile_1() {
FileInputStream fis = null;// 定义字节输入流
try {
fis = new FileInputStream("fos.txt");// 指定输入流和文件关联
int ch = 0;
while ((ch = fis.read()) != -1) {// 读取操作
System.out.println((char) ch);
}
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
if (fis != null)// 判空,提高程序的健壮性
fis.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
/*
* 对字节流读操作的第二种方式 通过read(byte[])方法读取字节数组
*/
public static void readFile_2() {
FileInputStream fis = null;
try {
fis = new FileInputStream("fos.txt");
byte[] byf = new byte[1024];
int len = 0;
while ((len = fis.read(byf)) != -1) {
System.out.println(new String(byf, 0, len));
}
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
if (fis != null)
fis.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
/*
* 对字节流读操作的第三种方式 通过字节流的available()方法获取到文件大小,定义一个大小刚刚好的数组,无需循环 但是,这种方式操作较大数据时容易内存溢出,所以要慎用 首选的还是定义1024的整数倍数组
*/
public static void readFile_3() {
FileInputStream fis = null;
try {
fis = new FileInputStream("fos.txt");
// 通过这样的方式定义一个刚刚好的数组
// 这种方法慎用,如果文件不大,可以用这个方法
byte[] byf = new byte[fis.available()];// fis.available()获取文件大小
fis.read(byf);
System.out.println(new String(byf));
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
fis.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
/*
* 对字节流进行写操作
*/
public static void writeFile() {
FileOutputStream fos = null;
try {
fos = new FileOutputStream("fos.txt");//定义字节写出流和文件关联
fos.write("abcds".getBytes());// str.getBytes()将String转化成字节数组
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} finally {
if (fos != null)
try {
fos.close();// 因为字节流没有用到缓冲区,所以不需要刷新,但必须关闭资源
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
思路:
1、用字节流读取流对象和媒体文件相关联
2、用字节写入流对象,创建一个媒体文件,用于存储获取到的媒体文件数据
3、通过循环读写,完成数据的存储
4、关闭资源
* 通过字节流拷贝图片
*/
public class CopyPicture {
public static void main(String[] args) {
copyPic();
}
public static void copyPic() {
FileInputStream fis = null;
FileOutputStream fos = null;
try {
fis = new FileInputStream("1.jpg");
fos = new FileOutputStream("C:\\1.jpg");
byte[] byf = new byte[1024];
int len = 0;
while ((len = fis.read(byf)) != -1) {// 将数据读取到数组中
fos.write(byf, 0, len);// 写入数组中的有效数据
}
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} finally {
// 有多个流不能一起关闭,要分别关闭
if (fis != null) {
try {
fis.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if (fos != null) {
try {
fos.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
// 千万不要用,效率没有!
public static void copy_4() throws IOException {
FileInputStream fis = new FileInputStream("c:\\0.mp3");
FileOutputStream fos = new FileOutputStream("c:\\4.mp3");
int ch = 0;
while((ch =fis.read())!=-1){
fos.write(ch);
}
fos.close();
fis.close();
}
//不建议。
public static void copy_3() throws IOException {
FileInputStream fis = new FileInputStream("c:\\0.mp3");
FileOutputStream fos = new FileOutputStream("c:\\3.mp3");
byte[] buf = new byte[fis.available()];
fis.read(buf);
fos.write(buf);
fos.close();
fis.close();
}
//提高效率
public static void copy_2() throws IOException {
FileInputStream fis = new FileInputStream("c:\\0.mp3");
BufferedInputStream bufis = new BufferedInputStream(fis);
FileOutputStream fos = new FileOutputStream("c:\\2.mp3");
BufferedOutputStream bufos = new BufferedOutputStream(fos);
int ch = 0;
while((ch=bufis.read())!=-1){
bufos.write(ch);
}
bufos.close();
bufis.close();
}
1、缓冲区的出现无非就是提高了流的读写效率,当然也就是因为这样,所以缓冲区的使用频率也是相当高的,所以要做必要性的掌握。
2、字节流缓冲区读写的特点:
这里没有什么特殊的读写方法,就是read()读一个字节,read(byte[])读数组的方法。写
write(int b)写一个自己,write(byte[])写数组的方法。
注:需要注意个地方,write方法只写出二进制的最后八位。
原理:将数据拷贝一部分,读取一部分,循环,直到数据全部读取完毕。
1)先从数据中抓取固定数组长度的字节,存入定义的数组中,再通过然后再通过read()方法读取数组中的元素,存入缓冲区
2)循环这个动作,知道最后取出一组数据存入数组,可能数组并未填满,同样也取出包含的元素
3)每次取出的时候,都有一个指针在移动,取到数组结尾就自动回到数组头部,这样指针在自增
4)取出的时候,数组中的元素再减少,取出一个,就减少一个,直到减到0即数组取完
5)到了文件的结尾处,存入最后一组数据,当取完数组中的元素,就会减少到0,这是全部数据就取完了
* 自定义字节流缓冲区
*/
public class MyBufferedInputStream {
private InputStream in;
private byte[] buf = new byte[1024];
private int pos;// 定义数组的指针
private int count;// 定义计数器
MyBufferedInputStream(InputStream in) {
this.in = in;
}
// 一次读一个字节,从缓冲区(数组)中取得
public int myRead() throws IOException {
// 通过in对象读取数据并存放在buf数组中
if (count == 0) {
count = in.read(buf);
if (count < 0) {
return -1;
}
pos = 0;
byte b = buf[pos];
count--;
pos++;
return b & 255;// &255是因为存放的是二进制,也就是可能前八位是11111111,提升为int还是-1,就直接return了,
// &255就是让前面补0
/*
* 11111111 11111111 11111111 11111111
* &00000000 00000000 00000000 11111111
* ------------------------------------
* 00000000 00000000 00000000 11111111
*/
}else if(count>0){
byte b = buf[pos];
count--;
pos++;
return b&255;//&0xff
}
return -1;
}
public void myClose() throws IOException{
in.close();
}
}
当byte中的八位全为1的时候是byte的-1,提升为int类型,就变为int型的-1,为-1时程序就停止循环了,read循环条件就结束了,变为-1的原因是由于在提升时,将byte的八位前都补的是1,即32位的数都是1,即为int型的-1了。如何保证提升后的最后八位仍为1呢?就需要将前24位补0,就可以保留原字节数据不变,又可以避免转为int型出现-1的情况;
那么要如何做呢?
这就需要将提升为int的数据和前24位为0,后八位仍为原字节数据的这个值做与运算。即和255做与运算即可。说到了这里应该也明白了为什么Read方法返回值为int类型了。
第六 转换流
InputStreamReader 是字节流通向字符流的桥梁
OutputStreamWriter 是字符流通向字节流的桥梁
转换流的子类和转换流的区别?
- //获取键盘录入对象
InputStream in = System.in;
//将字节流对象转换成字符流对象
InputStreamReader isr = new InputStreamReader(in);
//将字符流对象用缓冲技术高效处理
BufferedReader bufr = new BufferedReader(isr);
// 键盘录入最常见的写法
BufferedReader bufr = new BufferedReader(new InputStreamReader(System.in));
字符流转字节流:
// 操作输出
// 获取输出流
OutputStream out = System.out;
//将字符流转换成字节流,OutputStreamWriter字符流通向字节流的桥梁
OutputStreamWriter osw = new OutputStreamWriter(out);
BufferedWriter bfw = new BufferedWriter(osw);//高效缓冲区
* 获取键盘录入
public class ReadIn {
public static void main(String[] args) {
// method_1();
// method_2();
InputStreamReaderDemo();
}
/*
* 获取键盘录入
*/
public static void method_1() {
InputStream in = System.in;// 定义输入流与键盘输入流相关联
int ch = 0;
try {
while ((ch = in.read()) != -1) {
System.out.println(ch);
}
} catch (IOException e) {
e.printStackTrace();
}
}
/*
* 需求:通过键盘录入数据。 当录入一行数据后,就将该行数据进行打印。 如果录入的数据是over,那么停止录入。
* 1,因为键盘录入只读取一个字节,要判断是否是over,需要将读取到的字节拼成字符串。
* 2,那就需要一个容器。StringBuilder.
* 3,在用户回车之前将录入的数据变成字符串判断即可。
*/
public static void method_2() {
InputStream in = System.in;
StringBuilder sb = new StringBuilder();// 定义一个临时容器
while (true) {
int ch = 0;
try {
ch = in.read();
if (ch == '\r')// windows中换行符为\r\n
continue;
if (ch == '\n') {
String s = sb.toString();// 当读到一行的结束标记时,把改行数据变成字符串
if ("over".equals(s))
break;
System.out.println(s.toUpperCase()); //变成大写
sb.delete(0, sb.length());// 清空容器 ,不清空会将上次输入的和这次输入的都输出
} else {
sb.append((char) ch); //将读取到的字节存储到StringBuilder中。
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
// **************************************************************************
/*
* 通过上面录入一行数据,发现其方法类似于readLine方法 但是readLine方法是字符流缓冲区的方法,所以需要把字节流转换成字符流进行操作
* 需要用到InputStreamReader方法进行转换,InputStreamReader字节通向字符的桥梁
*
* 转换流
*/
public static void InputStreamReaderDemo() {
BufferedReader bufr = null;
try {
// 源为文件
bufr = new BufferedReader(new InputStreamReader(
new FileInputStream("buf.txt")));
} catch (FileNotFoundException e2) {
e2.printStackTrace();
}
// 操作输出
// 目的地是控制台
BufferedWriter bfw = new BufferedWriter(new OutputStreamWriter(
System.out));
// 目的地是文件
// BufferedWriter bfw = null;
// try {
// bfw = new BufferedWriter(new OutputStreamWriter(new
// FileOutputStream("out.txt")));
// } catch (FileNotFoundException e1) {
// e1.printStackTrace();
// }
String line = null;
try {
while ((line = bufr.readLine()) != null) {
if ("over".equals(line)) {
break;
}
bfw.write(line.toUpperCase());
bfw.newLine();// 换行
bfw.flush();// 数据存放在缓冲区,所以需要刷新
}
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
bufr.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
不处理异常
public class TransStreamDemo2 {
public static void main(String[] args) throws IOException {
/*
* 1,需求:将键盘录入的数据写入到一个文件中。
*
* 2,需求:将一个文本文件内容显示在控制台上。
*
* 3,需求:将一个文件文件中的内容复制到的另一个文件中。
*/
BufferedReader bufr = new BufferedReader(new InputStreamReader(System.in));
BufferedWriter bufw = new BufferedWriter(new OutputStreamWriter(new FileOutputStream("b.txt")));
String line = null;
while((line=bufr.readLine())!=null){
if("over".equals(line))
break;
bufw.write(line.toUpperCase()); //变成大写
bufw.newLine();
bufw.flush();
}
}
}
第七 流的操作规律
之所以要弄清楚这个规律,是因为流对象太多,开发时不知道用哪个对象合适。
想要知道开发时用到哪些对象。只要通过四个明确即可。
1,明确源和目的(汇)
源:InputStream Reader
目的:OutputStream Writer
2,明确数据是否是纯文本数据。
源:是纯文本:Reader
否:InputStream
目的:是纯文本 Writer
否:OutputStream
到这里,就可以明确需求中具体要使用哪个体系。
3,明确具体的设备。
源设备:
硬盘:File
键盘:System.in
内存:数组
网络:Socket流
目的设备:
硬盘:File
控制台:System.out
内存:数组
网络:Socket流
4,是否需要其他额外功能。
1,是否需要高效(缓冲区);
是,就加上buffer.
2,转换。
需求1:复制一个文本文件。
1,明确源和目的。
源:InputStream Reader
目的:OutputStream Writer
2,是否是纯文本?
是!
源:Reader
目的:Writer
3,明确具体设备。
源:
硬盘:File
目的:
硬盘:File
FileReader fr = new FileReader("a.txt");
FileWriter fw = new FileWriter("b.txt");
4,需要额外功能吗?
需要,需要高效。
BufferedReader bufr = new BufferedReader(new FileReader("a.txt"));
BufferedWriter bufw = new BufferedWriter(new FileWriter("b.txt"));
================================================
需求2:读取键盘录入信息,并写入到一个文件中。
1,明确源和目的。
源:InputStream Reader
目的:OutputStream Writer
2,是否是纯文本呢?
是,
源:Reader
目的:Writer
3,明确设备
源:
键盘。System.in
目的:
硬盘。File
InputStream in = System.in;
FileWriter fw = new FileWriter("b.txt");
这样做可以完成,但是麻烦。将读取的字节数据转成字符串。再由字符流操作。
4,需要额外功能吗?
需要。转换。 将字节流转成字符流。因为名确的源是Reader,这样操作文本数据做便捷。
所以要将已有的字节流转成字符流。使用字节-->字符 。InputStreamReader
InputStreamReader isr = new InputStreamReader(System.in);
FileWriter fw = new FileWriter("b.txt");
还需要功能吗?
需要:想高效。
BufferedReader bufr = new BufferedReader(new InputStreamReader(System.in));
BufferedWriter bufw = new BufferedWriter(new FileWriter("b.txt"));
===================================================
需求3:将一个文本文件数据显示在控制台上。
1,明确源和目的。
源:InputStream Reader
目的:OutputStream Writer
2,是否是纯文本呢?
是,
源:Reader
目的:Writer
3,明确具体设备
源:
硬盘:File
目的:
控制台:System.out
FileReader fr = new FileReader("a.txt");
OutputStream out = System.out;//PrintStream
4,需要额外功能吗?
需要,转换。
FileReader fr= new FileReader("a.txt");
OutputStreamWriter osw = new OutputStreamWriter(System.out);
需要,高效。
BufferedReader bufr = new BufferedReader(new FileReader("a.txt"));
BufferedWriter bufw = new BufferedWriter(new OutputStreamWriter(System.out));
================================================================
需求4:读取键盘录入数据,显示在控制台上。
1,明确源和目的。
源:InputStream Reader
目的:OutputStream Writer
2,是否是纯文本呢?
是,
源:Reader
目的:Writer
3,明确设备。
源:
键盘:System.in
目的:
控制台:System.out
InputStream in = System.in;
OutputStream out = System.out;
4,明确额外功能?
需要转换,因为都是字节流,但是操作的却是文本数据。
所以使用字符流操作起来更为便捷。
InputStreamReader isr = new InputStreamReader(System.in);
OutputStreamWriter osw = new OutputStreamWriter(System.out);
为了将其高效。
BufferedReader bufr = new BufferedReader(new InputStreamReader(System.in));
BufferedWriter bufw = new BufferedWriter(new OutputStreamWriter(System.out));
============================================================
5,将一个中文字符串数据按照指定的编码表写入到一个文本文件中.
1,目的。OutputStream,Writer
2,是纯文本,Writer。
3,设备:硬盘File
FileWriter fw = new FileWriter("a.txt");
fw.write("你好");
注意:既然需求中已经明确了指定编码表的动作。
那就不可以使用FileWriter,因为FileWriter内部是使用默认的本地码表。
只能使用其父类。OutputStreamWriter.
OutputStreamWriter接收一个字节输出流对象,既然是操作文件,那么该对象应该是FileOutputStream
OutputStreamWriter osw = new OutputStreamWriter(new FileOutputStream("a.txt"),charsetName(编码));
需要高效吗?
BufferedWriter bufw = new BufferedWriter(new OutputStreamWriter(new FileOutputStream("a.txt"),charsetName));
演示:将一个中文字符串数据按照指定的编码表写入到一个文本文件中.
写:
public static void writeText_2() throws IOException {
OutputStreamWriter osw = new OutputStreamWriter(new FileOutputStream(
"gbk_3.txt"), "GBK");
// OutputStreamWriter osw = new OutputStreamWriter(new
// FileOutputStream("gbk_3.txt"),"GBK");
// FileWriter fw = new FileWriter("gbk_1.txt");
/*
* 这两句代码的功能是等同的。 FileWriter:其实就是转换流指定了本机默认码表的体现。而且这个转换流的子类对象,可以方便操作文本文件。
* 简单说:操作文件的字节流+本机默认的编码表。 这是按照默认码表来操作文件的便捷类。
*
* 如果操作文本文件需要明确具体的编码。FileWriter就不行了。必须用转换流
*/
osw.write("你好");
osw.close();
}
public static void writeText_3() throws IOException {
OutputStreamWriter osw = new OutputStreamWriter(new FileOutputStream(
"u8_1.txt"), "UTF-8");
osw.write("你好");
osw.close();
}
读:
public static void readText_1() throws IOException {
FileReader fr = new FileReader("gbk_1.txt");
char[] buf = new char[10];
int len = fr.read(buf);
String str = new String(buf,0,len);
System.out.println(str);
fr.close();
}
//用utf-8读默认的编码文件
public static void readText_2() throws IOException, FileNotFoundException {
InputStreamReader isr = new InputStreamReader(new FileInputStream("gbk_1.txt"),"utf-8");
char[] buf = new char[10];
int len = isr.read(buf);
String str = new String(buf,0,len);
System.out.println(str);
isr.close();
}
}
}
public static void writeText_2() throws IOException {
OutputStreamWriter osw = new OutputStreamWriter(new FileOutputStream(
"gbk_3.txt"), "GBK");
// OutputStreamWriter osw = new OutputStreamWriter(new
// FileOutputStream("gbk_3.txt"),"GBK");
// FileWriter fw = new FileWriter("gbk_1.txt");
/*
* 这两句代码的功能是等同的。 FileWriter:其实就是转换流指定了本机默认码表的体现。而且这个转换流的子类对象,可以方便操作文本文件。
* 简单说:操作文件的字节流+本机默认的编码表。 这是按照默认码表来操作文件的便捷类。
*
* 如果操作文本文件需要明确具体的编码。FileWriter就不行了。必须用转换流
*/
osw.write("你好");
osw.close();
}
public static void writeText_3() throws IOException {
OutputStreamWriter osw = new OutputStreamWriter(new FileOutputStream(
"u8_1.txt"), "UTF-8");
osw.write("你好");
osw.close();
}
读:
public static void readText_1() throws IOException {
FileReader fr = new FileReader("gbk_1.txt");
char[] buf = new char[10];
int len = fr.read(buf);
String str = new String(buf,0,len);
System.out.println(str);
fr.close();
}
//用utf-8读默认的编码文件
public static void readText_2() throws IOException, FileNotFoundException {
InputStreamReader isr = new InputStreamReader(new FileInputStream("gbk_1.txt"),"utf-8");
char[] buf = new char[10];
int len = isr.read(buf);
String str = new String(buf,0,len);
System.out.println(str);
isr.close();
}
}
}
JavaSE学习笔记(14)---File类和IO流(字节流和字符流) File类 概述 java.io.File 类是文件和目录路径名的抽象表示,主要用于文件和目录的创建.查找和删除等操作. 构造方 ...
1. 流的概念 流(stream)的概念源于UNIX中管道(pipe)的概念.在UNIX中,管道是一条不间断的字节流,用来实现程序或进程间的通信,或读写外围设备.外部文件等. 一个流,必有源端和目的端 ...
一.流 1.流的概念 流是一组有顺序的,有起点和终点的字节集合,是对数据传输的总称或抽象.即数据在两设备间的传输称为流,流的本质是数据传输,根据数据传输特性将流抽象为各种类,方便更直观的进行数据操作. ...
Java IO流字符流简介及常用字符流的基本使用 字符流分为输入字符流(Writer)和输出字符流(Reader),这两种字符流及其子类字符流都有自己专门的功能.在编码中我们常用的输出字符流有File ...
java io流(字节流) 复制文件 //复制文件 //使用字节流 //复制文本文件用字符流,复制其它格式文件用字节流 import java.io.*; public class Index{ pu ...
java中io流系统庞大,知识点众多,作为小白通过五天的视频书籍学习后,总结了io系列的随笔,以便将来复习查看. 本篇为此系列随笔的第一篇:io系列之字节流. 一.字节流的File读写操作. Inpu ...
作者: Jakob Jenkov 译者: 李璟(jlee381344197@gmail.com) 本小节会简要概括Java IO中的PushbackReader,LineNumberReader,St ...
package ioDemo; import java.io.*; /** * IO流(字节流复制) * Created by lcj on 2017/11/2. */ public class bu ...
概述 IO流用来处理设备之间的数据传输 Java对数据的操作时通过流的方式 Java用于操作流的对象都在IO包中 流按操作的数据分为:字节流和字符流 流按流向不同分为:输入流和输出流 IO流常用基类 ...
#!/bin/sh#根据进程名杀死进程#FileName: killjavaphprm.sh #查看php进程IDecho "php进程ID:"pgrep php #杀死所有php ...
Sqlite: 1.一款用来实现本地数据存储的轻量级数据管理工具,是众多用来实现数据库管理的工具之一. 2.Android已经将SQLite的代码功能吸收在它的系统中,我们可以直接在Android程序 ...
目录 1.概述 2.实现同步的基础 3.实现方式 4.Java对象头(存储锁类型) 5.优化后synchronized锁的分类 6.锁的升级(进化) 6-1.偏向锁 6-2.轻量级锁 6-3.锁的比较 ...
http://blog.chinaunix.net/uid/10167808/abstract/1.html?year=2008 http://linux.linuxidc.com/ind ...
一.java.util.HashSet 1.1 HashSet集成结构 1.2 java.util.HashSet属性 private transient HashMap<E,Object> ...
最近学习Django的过程中,在cmd打算使用python manage.py shell来测试数据的时候,当我一导入自己写的model类,就发现报了这个错误django.core.exception ...
idea导入eclipse项目需要的配置 1.配置jdk,这两处选择一样就可以,也可以根据自己需求选择,上边的比下边的版本高就行 2.这里会默认和配置jdk一样 3.添加lib依赖,选择到项目的lib ...
一,什么是元祖 Python的元组与列表类似,不同之处在于元组的元素不能修改. 元组使用小括号,列表使用方括号. 元组创建很简单,只需要在括号中添加元素,并使用逗号隔开即可. 1.创建一个空元组 tu ...
错误: fatal: unable to access 'https://github.com/******': Failed to connect to 127.0.0.1 port 1080: C ...
1.行内样式获取打印出来 2.内嵌和外链的获取不了 <div style="width:200px;height:200px; background: red;">&l ...