疯狂Java学习笔记(55)----------字节流与字符流

字节流与字符流

在java.io包中操作文件内容的主要有两大类:字节流、字符流,两类都分为输入和输出操作。在字节流中输出数据主要是使用OutputStream完成,输入使的是InputStream,在字符流中输出主要是使用Writer类完成,输入流主要使用Reader类完成。(这四个都是抽象类)

处理流的用法:

按照流是否直接与特定的地方(如磁盘、内存、设备等)相连,分为节点流和处理流两类。

 节点流:可以从或向一个特定的地方(节点)读写数据。如FileReader

处理流:是对一个已存在的流的连接和封装,通过所封装的流的功能调用实现数据读写。

如BufferedReader。处理流的构造方法总是要带一个其他的流对象做参数。一个流对象经过其他流的多次包装,称为流的链接。

常用的节点流 

父  类  InputStream OutputStream Reader Writer

文  件  *FileInputStream FileOutputStrean FileReader FileWriter 文件进行处理的节点流

数  组  *ByteArrayInputStream  ByteArrayOutputStream  CharArrayReader  CharArrayWriter

对数组进行处理的节点流(对应的不再是文件,而是内存中的一个数组)

字符串  *无    无  StringReader StringWriter 对字符串进行处理的节点流

管  道  *PipedInputStream  PipedOutputStream  PipedReader  PipedWriter 对管道进行处理的节点流

常用处理流(关闭处理流使用关闭里面的节点流)

父  类  InputStream OutputStream Reader Writer

缓冲流  *BufferedImputStrean BufferedOutputStream BufferedReader BufferedWriter ----需要父类作为参数构造,增加缓冲功能,避免频繁读写硬盘,可以初始化缓冲数据的大小,由于带了缓冲功能,所以就写数据的时候需要使用flush方法。

转换流  *InputStreamReader  OutputStreamWriter-  要inputStream 或OutputStream作为参数,实现从字节流到字符流的转换 数据流  *DataInputStream DataOutputStream -提供将基础数据类型写入到文件中,或者读取出来,为什么要有这个流呢?

看这样的分析,如果没有这种流的话,有一个long,本身只占8个字节,如果我要写入到文件,需要转成字符串,然后在转成字符数组,那空间会占用很多,但是有了这种流之后就很方便了,直接将这8个字节写到文件就完了。。是不是既节约了内存空间有让程序写起来更加方便简单了呐。写倒是很简单,但是读取的时候就注意了,根据读取的数据类型,指针会往下移,所以你写的顺序必须要和读的顺序一致才能完成你正确的需求。

因此,我们使用处理流时的典型思路是:使用处理流来包装字节流,程序通过处理流来执行输入/输出功能,让字节流与底层的I/O设备、文件进行交互。

输入输出流体系:

Java的输入输出流提供了近40个类:看似毫无规律,但我们可以按功能划分:如下图:

通常来说,字节流的功能比字符流的功能强大,因为计算机里的所有数据都是二进制的,你懂得!

但我还是喜欢字符流,直接明了。

转换流

两个转换流:

1.将字节转换成字符流

InputStreamReader:将字节输入流转换成字符输入流,OutPutStreamWriter:将字节输出流转换成字符输出流

2.从字符流到字节流:可以从字符流中获取char[]数组,转换为String,然后调用String的API函数getBytes() 获取到byte[],然后就可以通过ByteArrayInputStream、ByteArrayOutputStream来实现到字节流的转换。

package com.haixu.io;

import java.io.BufferedReader;
import java.io.InputStreamReader;

public class KeyinTest {

	public static void main(String[] args) {

		try {
			//将system.in对象转换成Reader对象
			System.out.println("请输入:");
			InputStreamReader reader = new InputStreamReader(System.in);
			//将Reader对象转换成BufferedReader对象
			BufferedReader br = new BufferedReader(reader);

			String buffer = null;
			//进行逐行的读取
			while((buffer = br.readLine()) != null){

				//如果读取的内容为exit,则退出程序
				if(buffer.equals("exit")){
					System.exit(1);
				}
				System.out.println("输入内容为:" + buffer);
			}
		} catch (Exception e) {
			// TODO: handle exception
			e.printStackTrace();
		}
finally{reader.close();br.close()}//不要忘记关闭流 我忘记了,后加入的!
	}
}

弄点高大尚的:

推回输入流

在输入输出流中,有两个特殊的流:  PushbackInputStream 和  PushbackReader

Pushback用于输入流允许字节被读取然后返回(即“推回”)到流。PushbackInputStream类实现了这个想法。它提供了一种机制来“窥视”在没有受到破坏的情况下输入流生成了什么。

PushbackInputStream有两个构造函数:

PushbackInputStream(InputStream inputStream)

PushbackInputStream(InputStream inputStream, int numBytes)

第一种形式创建了一个允许一个字节推回到输入流的流对象。第二种形式创建了一个具有numBytes长度缓冲区的推回缓冲流。它允许多个字节推回到输入流。除了具有与InputStream相同的方法,PushbackInputStream提供了unread( )方法,表示如下:

void unread(int ch)

void unread(byte buffer[ ])

void unread(byte buffer, int offset, int numChars)

第一种形式推回ch的低位字节,它将是随后调用read( )方法所返回的下一个字节。第二种形式返回buffer缓冲器中的字节。第三种形式推回buffer中从offset处开始的numChars个字节。如果在推回缓冲器为满时试图返回一个字节,IOException异常将被引发。Java 2 对PushbackInputStream作了一些小的修改:它实现skip( )方法。

实例:

package com.haixu.io;

import java.io.FileReader;
import java.io.PushbackReader;

public class PushBackTest {
	/**
	 * 推回输入流练习
	 *
	 * */
	public static void main(String[] args) {
		try {
			/*
			 * 创建推回输入流的对象,并设定缓存的大小为:64字节
			 * */
			PushbackReader pr = new PushbackReader(
					new FileReader("E://Java编程//Java06//src//com//haixu//io//PushbackTest.java") , 64);
			char [] buf = new char[64];
			//用于保存上次读取的字符串内容
			String lastContent = "";
			int hasRead = 0;

			//循环读取文件内容
			while((hasRead = pr.read(buf)) > 0 ){

				String content = new String(buf , 0 , hasRead);
				int targetIndex = 0 ;

				//将上次读取的内容与本次读取的内容拼接起来
				//查看是否包含目标字符串,如果包含目标字符串
				if((targetIndex = (lastContent + content).indexOf("new PushbackReader"))>0){

					//将本次内容与上次内容一起推回缓冲区
					pr.unread((lastContent + content).toCharArray());
					int len = targetIndex >32 ? 32 : targetIndex;

					//指定读取指定长度的内容
					pr.read(buf , 0 , len);

					//打印输出
					System.out.println(new String(buf ,0 , len));
					System.exit(0);
				}else{
					System.out.println(lastContent);
					//将本次内容设定上次读取的内容
					lastContent = content;
				}
			}

		} catch (Exception e) {
			// TODO: handle exception
			e.printStackTrace();
		}
	}

}<span style="color:#ff0000;">
</span>

重定向输入输出

Java的标准输入/输出分别通过System.in和System.out来代表,在默认的情况下分别代表键盘和显示器,当程序通过System.in来获得输入时,实际上是通过键盘获得输入。当程序通过System.out执行输出时,程序总是输出到屏幕。

在System类中提供了三个重定向标准输入/输出的方法

static void setErr(PrintStream err) 重定向“标准”错误输出流

static void setIn(InputStream in)    重定向“标准”输入流

static void setOut(PrintStream out)重定向“标准”输出流

package com.haixu.io;

import java.io.FileOutputStream;
import java.io.PrintStream;

public class RedirectOut {

	public static void main(String[] args) {
		try {
			/*
			 * 一次性创建PrintStream输出流
			 */
			PrintStream ps = new PrintStream(new FileOutputStream("out.text"));

			//将标准输入重定向到ps输入流中
			System.setOut(ps);

			//向标准输入一个字符串
			System.out.println("普通字符串");
			//向标准输出输出一个对象
			System.out.println((new RedirectOut()));

		} catch (Exception e) {
			// TODO: handle exception
			e.printStackTrace();
		}
	}
}
package com.haixu.io;

import java.io.FileInputStream;
import java.util.Scanner;

public class RedirectIN {

	public static void main(String[] args) {
		try {
			//创建FileInputStreamd对象
			FileInputStream fis = new FileInputStream("E://Java编程//Java06//src//com//haixu//io//RedirectIN.java");

			//标准的输入重定向到fis输入流
			System.setIn(fis);

			//使用System.in创建Scanner对象,用于标准的输入
			Scanner sc = new Scanner(System.in);
			//回车
			sc.useDelimiter("/n");
			while(sc.hasNext()){
				System.out.println("键盘输入的内容" + sc.next());
			}
		} catch (Exception e) {
			// TODO: handle exception
			e.printStackTrace();
		}
	}

}

RandomAccessFile

RandomAccessFile是用来访问那些保存数据记录的文件的,你就可以用seek( )方法来访问记录,并进行读写了。这些记录的大小不必相同;但是其大小和位置必须是可知的。但是该类仅限于操作文件。

RandomAccessFile不属于InputStream和OutputStream类系的。实际上,除了实现DataInput和DataOutput接口之外(DataInputStream和DataOutputStream也实现了这两个接口),它和这两个类系毫不相干,甚至不使用InputStream和OutputStream类中已经存在的任何功能;它是一个完全独立的类,所有方法(绝大多数都只属于它自己)都是从零开始写的。这可能是因为RandomAccessFile能在文件里面前后移动,所以它的行为与其它的I/O类有些根本性的不同。总而言之,它是一个直接继承Object的,独立的类。

基本上,RandomAccessFile的工作方式是,把DataInputStream和DataOutputStream结合起来,再加上它自己的一些方法,比如定位用的getFilePointer( ),在文件里移动用的seek( ),以及判断文件大小的length( )、skipBytes()跳过多少字节数。此外,它的构造函数还要一个表示以只读方式("r"),还是以读写方式("rw")打开文件的参数 (和C的fopen( )一模一样)。它不支持只写文件。

只有RandomAccessFile才有seek搜寻方法,而这个方法也只适用于文件。BufferedInputStream有一个mark( )方法,你可以用它来设定标记(把结果保存在一个内部变量里),然后再调用reset( )返回这个位置,但是它的功能太弱了,而且也不怎么实用。

内存映射文件

内存映射文件能让你创建和修改那些因为太大而无法放入内存的文件。有了内存映射文件,你就可以认为文件已经全部读进了内存,然后把它当成一个非常大的数组来访问。这种解决办法能大大简化修改文件的代码。

fileChannel.map(FileChannel.MapMode mode, long position, long size)将此通道的文件区域直接映射到内存中。注意,你必须指明,它是从文件的哪个位置开始映射的,映射的范围又有多大;也就是说,它还可以映射一个大文件的某个小片断。

MappedByteBuffer是ByteBuffer的子类,因此它具备了ByteBuffer的所有方法,但新添了force()将缓冲区的内容强制刷新到存储设备中去、load()将存储设备中的数据加载到内存中、isLoaded()位置内存中的数据是否与存储设置上同步。这里只简单地演示了一下put()和get()方法,除此之外,你还可以使用asCharBuffer( )之类的方法得到相应基本类型数据的缓冲视图后,可以方便的读写基本类型数据。

/*
 * 程序功能:演示了RandomAccessFile类的操作,同时实现了一个文件复制操作。
 */
package com.lwj.demo;

import java.io.*;

public class RandomAccessFileDemo {
 public static void main(String[] args) throws Exception {
  RandomAccessFile file = new RandomAccessFile("file", "rw");
  // 以下向file文件中写数据
  file.writeInt(20);// 占4个字节
  file.writeDouble(8.236598);// 占8个字节
  file.writeUTF("这是一个UTF字符串");// 这个长度写在当前文件指针的前两个字节处,可用readShort()读取
  file.writeBoolean(true);// 占1个字节
  file.writeShort(395);// 占2个字节
  file.writeLong(2325451l);// 占8个字节
  file.writeUTF("又是一个UTF字符串");
  file.writeFloat(35.5f);// 占4个字节
  file.writeChar('a');// 占2个字节

  file.seek(0);// 把文件指针位置设置到文件起始处

  // 以下从file文件中读数据,要注意文件指针的位置
  System.out.println("——————从file文件指定位置读数据——————");
  System.out.println(file.readInt());
  System.out.println(file.readDouble());
  System.out.println(file.readUTF());

  file.skipBytes(3);// 将文件指针跳过3个字节,本例中即跳过了一个boolean值和short值。
  System.out.println(file.readLong());

  file.skipBytes(file.readShort()); // 跳过文件中“又是一个UTF字符串”所占字节,注意readShort()方法会移动文件指针,所以不用加2。
  System.out.println(file.readFloat());

  //以下演示文件复制操作
  System.out.println("——————文件复制(从file到fileCopy)——————");
  file.seek(0);
  RandomAccessFile fileCopy=new RandomAccessFile("fileCopy","rw");
  int len=(int)file.length();//取得文件长度(字节数)
  byte[] b=new byte[len];
  file.readFully(b);
  fileCopy.write(b);
  System.out.println("复制完成!");
 }
}
/**
 *
 * @param skip 跳过多少过字节进行插入数据
 * @param str 要插入的字符串
 * @param fileName 文件路径
 */
public static void beiju(long skip, String str, String fileName){
	try {
		RandomAccessFile raf = new RandomAccessFile(fileName,"rw");
		if(skip <  0 || skip > raf.length()){
			System.out.println("跳过字节数无效");
			return;
		}
		byte[] b = str.getBytes();
		raf.setLength(raf.length() + b.length);
		for(long i = raf.length() - 1; i > b.length + skip - 1; i--){
			raf.seek(i - b.length);
			byte temp = raf.readByte();
			raf.seek(i);
			raf.writeByte(temp);
		}
		raf.seek(skip);
		raf.write(b);
		raf.close();
	} catch (Exception e) {
		e.printStackTrace();
	}
}
时间: 2024-10-12 12:10:39

疯狂Java学习笔记(55)----------字节流与字符流的相关文章

IO流学习笔记3——字节流和字符流的输入/输出

字节流和字符流的操作方式基本一样,区别只是操作的数据单元有区别:字节流操作的数据单元是字节,字符流操作的数据单元字符. InputStream和Reader是所有输入流的基类,它们两个都是抽象类,本身不能创建实例,但它们分别有一个用于读取文件的输入流:FileInputStream和FileReader,它们都是节点流. OutputStream和Writer是所有输出流的基类,和输入流相似,也有对应的输出流:FileOutputStream和FileWriter,它们也是节点流. 1.使用Fi

JAVA学习笔记(三十二)- 字符流 FileReader &amp; FileWriter

标题 import java.io.BufferedInputStream; import java.io.BufferedOutputStream; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.FileReader; import java.io.FileWriter; import java.io.IOException; import java.io.InputStream;

Java学习笔记之 IO包 字符流

字符流:Writer/Reader(字符流) 通过子类FileWriter和FileReader实现父类实例化 package iotest; import java.io.File; import java.io.Writer; import java.io.Reader; import java.io.FileWriter; import java.io.FileReader; public class OutputStreamDemo2 { public static void main(

java:I/O 字节流和字符流

字节流 InputStream和OutputStream的子类:FileInputStream 和 FileOutputStream 方法: int read(byte[] b,int off,int len); void write(byte[] b,int off,int len); 字符流 Reader和Writer的子类:FileReader 和 FileWriter 方法: int read(char[] b,int off,int len); void write(char[] b,

疯狂Java学习笔记(54)----------详解Java IO(全面)

流的大家族 流(stream)的概念: 源于UNIX中管道(pipe)的概念.在UNIX中,管道是一条不间断的字节流,用来实现程序或进程间的通信,或读写外围设备.外部文件等. 一个流,必有源端和目的端,它们可以是计算机内存的某些区域,也可以是磁盘文件,甚至可以是Internet上的某个URL. 流的方向是重要的,根据流的方向,流可分为两类:输入流和输出流. 用户可以从输入流中读取信息,但不能写它.相反,对输出流,只能往输入流写,而不能读它. 实际上,流的源端和目的端可简单地看成是字节的生产者和消

Java IO-file(读写查)字节流、字符流

使用字节流操作汉字或特殊的符号语言的时候,容易乱码,建议使用字符流. 先有字节流,后有字符流,字符流是对字节流的补充. 使用记事本打开某个文件,可以看到内容的就是文本文件,否则可以理解二进制. 一般的,操作二进制文件(图片,音频,视频等)必须使用字节流. 一般的,操作文本文件使用字符流. 如果不清楚是哪一类型文件,使用字节流. -----------------------------------------字节流-----------------------------------------

Java学习笔记六(I/O流)

1.介绍 在实际开发过程中经常会用到数据的输入/输出操作,本篇博客着重分析一下,java中经常用到的有关IO操作的类.而在java中可以将常用的流分为两个部分:字节流和字符流. 1.流的抽象基类 字节流 字符流 输入流 InputStream Reader 输出流 OutPutStream Writer 正如上图表格所示,字符和字节流都有自己的基类,其余的都是继承基类的扩展流的操作.下面会着重的讲解一下.(只要会了字符流,字节流与其操作一样,只不过操作的文件类型不一致而已) 2.字符流 专门用于

java IO(五):字节流、字符流的选择规律

*/ .hljs { display: block; overflow-x: auto; padding: 0.5em; color: #333; background: #f8f8f8; } .hljs-comment, .hljs-template_comment, .diff .hljs-header, .hljs-javadoc { color: #998; font-style: italic; } .hljs-keyword, .css .rule .hljs-keyword, .h

【疯狂Java学习笔记】【理解面向对象】

[学习笔记]1.Java语言是纯粹的面向对象语言,这体现在Java完全支持面向对象的三大基本特征:封装.继承.多态.抽象也是面向对象的重要组成部分,不过它不是面向对象的特征之一,因为所有的编程语言都需要抽象. 2.面向对象开发方法比较结构化开发方法的优势在于可以提供更好的可重用性.可扩展性.可维护性. 3.基于对象和面向对象的区别:基于对象也使用了对象,但是无法通过现有的对象作为模板来生成新的对象类型,继而产生新的对象,也就是说,基于对象没有继承的特点.而面向对象有继承,而多态则是建立在继承的基