一、InputStreamReader类
API文档说明:InputStreamReader类是从字节流到字符流的桥接器:它使用指定的字符集读取字节并将它们解码为字符。 它使用的字符集可以通过名称指定,也可以明确指定,或者可以接受平台的默认字符集。每次调用一个InputStreamReader的read()方法都可能导致从底层字节输入流中读取一个或多个字节。 为了实现字节到字符的有效转换,可以从基础流中提取比满足当前读取操作所需的更多字节。为了获得最高效率,请考虑在BufferedReader中包装InputStreamReader
其继承Reader类
public class InputStreamReader extends Reader {}
1)字节流到字符流的桥梁怎么理解?
1、计算机存储的单位是字节,如尽管txt文本中有中文汉字这样的字符,但是对计算机而言,其是字节形式存在的
2、字节流读取是单字节读取,但是一个字符可能由1个或多个字节组成,所以字节读取会有问题,需要字符流读取
3、 尽管我们使用字符流读取字符,但其底层本质还是字节流。因此就需要一个流,将底层的字节流封装成字符流
4、InputStreamReader流就是起这个作用,把底层字节流封装成字符流进行字符读取工作,所以称为桥梁
2)使用指定的字符集读取字节并将它们解码为字符怎么理解?
字节本质是8个二进制位,且不同的字符集对同一字节解码后的字符结果是不同的,因此在读取字符时务必要指定合适的字符集,否则读取的内容会产生乱码
3)它使用的字符集可以通过名称指定,也可以明确指定,或者可以接受平台的默认字符集怎么理解?
意味着InputStreamReader类有多个方法或者多个构造方法来设置字符集
4)每次调用一个InputStreamReader的read()方法都可能导致从底层字节输入流中读取一个或多个字节怎么理解?
因为字符流的read()方法是要返回一个字符的,但是不同的字符在字符集中可能由不同的字节个数组成,所以会导致这一结果
5)为了实现字节到字符的有效转换,可以从基础流中提取比满足当前读取操作所需的更多字节,请考虑在BufferedReader中包装InputStreamReader
该话暂未理解,需要了解BufferedReader类后对比读取效率才可得出答案
二、InputStreamReader构造方法
1)使用默认的字符集构造InputStreamReader流:本质是初始化其实例域的一个变量,并未看到任何关于字符集的设置
public InputStreamReader(InputStream in) {
super(in);
try {
sd = StreamDecoder.forInputStreamReader(in, this, (String)null);
} catch (UnsupportedEncodingException e) {
throw new Error(e);
}
}
2)使用指定的字符集名称构造InputStreamReader流:本质是初始化其实例域的一个变量,可以发现字符集是初始化方法的第三个参数
public InputStreamReader(InputStream in, String charsetName)
throws UnsupportedEncodingException
{
super(in);
if (charsetName == null)
throw new NullPointerException("charsetName");
sd = StreamDecoder.forInputStreamReader(in, this, charsetName);
}
3)sd变量:本质是StreamDecoder类的对象,InputStreamReader的构造方法就是在给此对象做初始化操作
private final StreamDecoder sd;
三、InputStreamReaderAPI
1、我们可以发现InputStreamReaderAPI类的所有API都是利用了sd变量的,因此可以看出InputStreamReader类的方法本质是调用StreamDecoder类方法
2、但是由于StreamDecoder类中的读取方法等涉及到了字节的高低位的等问题,所以暂时不去了解,待后续补充
/**
* 获取设置的字符集
*/
public String getEncoding() {
return sd.getEncoding();
}
/**
* 读取流并返回一个字符,遇到文件末尾返回-1
*/
public int read() throws IOException {
return sd.read();
}
/**
* 读取字符到字符数组的部分中,遇到文件末尾返回-1
*/
public int read(char cbuf[], int offset, int length) throws IOException {
return sd.read(cbuf, offset, length);
}
/**
* 检测流是否准备好呗读取
*/
public boolean ready() throws IOException {
return sd.ready();
}
/**
* 关闭流并释放资源
*/
public void close() throws IOException {
sd.close();
}
四、InputStreamReader类的读取方法
1)读取一个或多个字节并返回一个字符,若遇到文件末尾,则返回-1:暂时不涉及具体细节,了解作用即可
/**
* 读取流并返回一个字符,遇到文件末尾返回-1
*/
public int read() throws IOException {
return sd.read();
}
read()方法实例:务必注意构造InputStreamReader对象时要指定字符集,否则会出现不可预估的错误
utf-8的字符集解码,最终结果是:咕噜是个大胖子
gbk的字符集解码,最终结果是:鍜曞櫆鏄釜澶ц儢瀛�--这样的就代表字符集错误,乱码了
public static void main(String[] args)
{
// 建立文件对象,文中内容是"咕噜是个大胖子";
File file = new File("C:\\Users\\Administrator\\Desktop\\4.txt");
try
{
//构建InputStreamReader流对象并指定字符集
InputStreamReader reader = new InputStreamReader(
new FileInputStream(file), "utf-8");
int n = 0;
StringBuffer buffer=new StringBuffer();
while(n!=-1)
{
n=reader.read();
char result=(char) n; //转为字符
buffer.append(result);
}
System.out.println(buffer.toString());
}
catch (FileNotFoundException e)
{
System.out.println("文件不存在或者文件不可读或者文件是目录");
}
catch (IOException e)
{
System.out.println("读取过程存在异常");
}
}
2)读取最多length个字符到字符数组中,从数组的偏移量offset开始存储,读到文件末尾时返回-1
public int read(char[] cbuf, int offset,int length) throws IOException
使用该方法时务必要注意保证length要比读取的数据长度要大,否则读取的数据会出现乱序问题--原因要在具体实现中才能理解,暂时先记住即可
当legnth为6时,结果为子噜是个大胖;当length为4时,结果为大胖子个;当length长度为8时,结果为咕噜是个大胖子
public static void main(String[] args)
{
// 建立文件对象,文中内容是"咕噜是个大胖子";字符个数是7个
File file = new File("C:\\Users\\Administrator\\Desktop\\4.txt");
try
{
// 构建InputStreamReader流对象
InputStreamReader reader = new InputStreamReader(
new FileInputStream(file), "utf-8");
int n = 0;
char[] datas=new char[10];
while (n != -1)
{
n = reader.read(datas,0,6); //length变量至关重要
}
reader.close();
String resultString=new String(datas);
System.out.println(resultString );
}
catch (FileNotFoundException e)
{
System.out.println("文件不存在或者文件不可读或者文件是目录");
}
catch (IOException e)
{
System.out.println("读取过程存在异常");
}
}
如何解决该问题?即如何保证要读取的最大字符数是高于流的,因为字符是由1个或多个字节组成,因此我们可以利用字符的文件个数来做参,但是该步骤会导致字符数组内存浪费且结果的末尾会多很多空白
char[] datas=new char[(int) file.length()];
五、InputStreamReader类与FileReader类关系
1、FileReader类仅仅是InputStreamReader的简单衍生并未扩展任何功能
2、FileReader类读取数据实质是InputStreamReader类在读取,而InputStreamReader读取数据实际是StreamDecoder类读取
3、因此在使用字符输入流的时候实际是StreamDecoder类在发挥作用