Java 初识IO流笔记
内容:
1.常见的输入流FileInputStream的三种read方法使用
2.过程中遇到的编码和类型转换问题分析
1.输入流
InputStream是抽象类(其中的read()方法为抽象方法无法直接实例化使用),所以这个类不能直接使用可以:
所以实例化时new 的是FileInputStrean这个类(这个类继承了InputStream并实现了read()方法)
所以一般的文件输入留有以下两种方式创建
InputStream fis=new FileInputStream("url");
FileInputStream fis=new FileInputStream("url")
url是需要读取文件的存储位置,可以是绝对路径也可以是相对路径。
先着重演示文件输入流三个不同read方法的用途
1.无参数输入的read()
需要读取文件hello.txt中存储的内容:
hello
用法
read()方法返回的是一个int类型的值,如果输入的这个文件流中还有值没有读出则返回一个字节对应的int类型值。
如果这个流已经读完了则返回-1。
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
public class test_1 {
public static void main(String[] args) throws IOException {
InputStream fis=new FileInputStream("D:\\工作区\\SE\\IO\\srcs\\hello.txt");
int b;
while ((b=fis.read())!=-1){//通过是否为-1判断流是否读完
System.out.print((char)b);
}
System.out.println();
}
}
控制台输出:
hello
若read()返回的不是-1则对应返回的int值转换为char就是对应的字符(这种情况就是一个字节翻译一个值出来根据Unicode码来翻译)
根据这个表可知如果返回的是65则对应字符A(对应的int类型前加一个强制转换就可以完成对应的翻译)
提示但是这种一个字节一个字节读的方式无法显示中文会出现乱码,因为中文的表示,GBK需要两个字节,UTF-8需要三个字节
如将源文件hello.txt中的文件内容改为 中文,这两个汉字
hello.txt文件改为:
中文
控制台输出:
ä¸æ–‡ //出现乱码
所以read有以下重载方法提供解决方案
2.read(byte [])
这个方法可以解决上面中文乱码的问题,上面的那种原始read()方法因为每次只能取一个字节,所以不能不能翻译中文,所以只要能解决这个问题,一次性从流中传入一个数组的数据(一个byte存储一个字节的数据,所以byte[]数组的长度是多少就能存储多少个字节的数据)
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
public class test_2 {
public static void main(String[] args) throws IOException {
InputStream is=new FileInputStream("D:\\工作区\\SE\\IO\\srcs\\hello.txt");
byte b[]=new byte[1024];//这个长度可以根据自己需要调整
while (is.read(b)!=-1){
// System.out.println(new String(b,"UTF-8"));//String类提供了把byte数组转化为String类的构造方法,第二个参数可以不写,如果不要第二个参数,IDEA会以平台默认的编码方式编码
}
}
}
控制台输出
中文
这种方法每次从流放入数组的数据的长度是由数据决定的(每次byte数组这个容器能满就装满的意思,当然如果数据很少,可能第一次装个几字节就没了),无法手动控制长度,所以提供了以下方法
3.read(byte[] b, int off, int len)
这个方法增强了使用的灵活性
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
public class test_4 {
public static void main(String[] args) throws IOException {
InputStream is=new FileInputStream("D:\\工作区\\SE\\IO\\srcs\\hello.txt");
byte b[]=new byte[1024];
while (is.read(b,0,3)!=-1){//每次的偏移量必须是三的倍数(这种编码模式下3个字节一个中文嘛)
System.out.println(new String(b,"UTF-8"));//这种方法可以获得正确的中文编码
}
}
}
控制台输出
中
文
因为用的是UTF-8的编码,一个中文需要三个字节表示,所以我们从最开始的位置开始取,每次取三个字节,中文这两个汉字取了两次,所以打印了两次
2.输入缓冲流
import java.io.BufferedInputStream;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
public class test_5 {
public static void main(String[] args) throws IOException {
BufferedInputStream bis=new BufferedInputStream(new FileInputStream("D:\\工作区\\SE\\IO\\srcs\\hello.txt"));//和一般文件流的创建略有不同
byte[] b=new byte[1024];
while (bis.read(b)!=-1){
System.out.println(new String(b));//这里就是使用的默认编码方式
}
}
}
控制台输出:
中文
缓冲流比一般的流快(这个缓冲流的读取效率更高),但是需要注意,具体组件中的一些功能他是没有的。比如read(byte[]),这个方法在 BufferedInputStream中就没有,所以在用构造方法声明的时候需要传入 FileInputStream()的实例,而不只是一个源文件的地址
3.遇到的编码问题
一) java进制问题
public class test_3 {
public static void main(String[] args) {
int c=021; //0开头表示八进制
int d=0x10; //0x打头表示16进制(x大小写都可)
System.out.println(c);
System.out.println(d);
/**
* Java所有整型的数据默认都是int,小数默认为double
* **/
byte e=(byte) 0x121; //强转为byte会用十六进制(因为之前的数据是用16进制表示的)舍去高位
System.out.println(e); //可以发现最高位的1被舍去
/**
* Java运算的时候会产生类型提升
* 范围小于int的值进行运算,运算过程中会转化为int
* **/
byte f=0x11;
byte g=0x01;
byte h=(byte)(f-g); //这里必须强转byte,不然就会发生int赋给byte的大赋小的问题
}
}
控制台输出
17
16
33
总结:
1.在强制转换过程中丢精度,需要根据数据的进制类型来丢,如果是16进制,就丢去16进制的高位如e=0x121,强转之后就只有0x21(转化为10进制为33)的值了
2.Java所有整型的数据默认都是int,小数默认为double
3范围比int小的值进行运算,运算过程中会转化为int(如两个byte类型的值相减)
二)乱码分析
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
/**
* GBK 两个字节一个字符
* UTF-8 三个字节一个字符
* UTF-8 中英文都可以支持所以使用更广
* **/
public class test_2 {
public static void main(String[] args) throws IOException {
InputStream is=new FileInputStream("D:\\工作区\\SE\\IO\\srcs\\hello.txt");
int len;
while ((len=is.read())!=-1){
System.out.println(len);
}
}
}
控制台输出
228
184
173
230
150
135 //此时源文件中还是存入的中文两个汉字
idea默认的编码是UTF-8,所以存入的文件也是按照一个汉字等于三个字节存储,所以最后读取的时候两个汉字读了6个字节。
而直接用char强转每一个字节的话是按照Unicode编码的,所以中文就会出现乱码,而如果存入的英文的话,一个字节就可以存储所有英文字母,Unicode是支持英文的,所以不会出现乱码。
按照这个逻辑这些int类型的值在输出流的位置不需要强转任何类型就可以写入
4.最常见的输入流和输出流结合用法(完成文件的复制)
public class test_6 {
public static void main(String[] args)throws Exception {
FileInputStream fis=new FileInputStream("D:\\工作区\\SE\\IO\\srcs\\hello.txt");
FileOutputStream fos=new FileOutputStream("D:\\工作区\\SE\\IO\\srcs\\test2.txt");
int len;
while ((len=fis.read())!=-1){
fos.write((len)); //直接写入
}
}
}
这里的输出流会先检查目标位置上有没有对应文件,如果没有就先则创建它,然后存在这个文件会先清空文件内容,然后写入。(当然也有不清空,直接追加内容的方法)。