字节流读取文本文件的问题
当文本文件中存在汉字和字母的时候,由于汉字和字母占用的字节数不一样,再读取的时候就可能产生乱码的问题。
假设有一个a.txt文件
a你好bc
使用下面的FileInputStream读取文件中的字节,把字节转成字符串就有乱码问题。
FileInputStream fis=new FileInputStream("myModule12\\a.txt");
//一次读取多个字节
byte[] bs=new byte[2];
int len; //记录每次读取的字节个数
while ((len=fis.read(bs))!=-1){
//把字节数组转换为一个字符串
System.out.println(new String(bs,0,len));
}
字符流
Java的API中,为了解决前面的乱码问题,专门提供了类流用来对文本文件进行操作。Reader和Writer
Reader: 字符输入流(读取字符)
-- FileReader
-- BufferedReader
Writer: 字符输出流(写入字符)
-- FileWriter
-- BufferedWriter
IO流的使用步骤
1.创建流对象(搭桥)
Reader
Writer
2.读/写数据(过桥)
read
write
3.释放资源(过河拆桥)
close
字符流读取数据
- 一次读取一个字符
//字符输入流
FileReader fr=new FileReader("myModule12\\a.txt");
//循环读取,一次读一个字符
int b; //记录每次读取的字符
while ((b=fr.read())!=-1){
System.out.print((char) b);
}
//释放资源
fr.close();
- 一次读取多个字符
//创建输入流对象
FileReader fr=new FileReader("myModule12\\a.txt");
//读取数据: 一次读取多个字符
char[] chs=new char[1024];
int len; //记录每次读取的字符个数
while ((len=fr.read(chs))!=-1){
//把字符数组的一部分,转换为字符串
String str=new String(chs,0,len);
System.out.println(str);
}
//释放资源
fr.close();
字符流写入数据
//创建输出流对象FileWriter
FileWriter fw=new FileWriter("myModule12\\b.txt");
//写一个字符
fw.write(97); //写97对应的是 字符‘a’
//写多个字符
char[] chs={'h','e','l','l','o'};
fw.write(chs);
//写一个字符数组的一部分
fw.write(chs,0,3);
//写一个字符串
fw.write("你好世界");
//释放资源
fw.close();
字符流复制文件
//源文件:a.txt ,使用FileReader进行读取
FileReader fr=new FileReader("myModule12\\a.txt");
//目标文件:b.txt,使用FileWriter进行写入
FileWriter fw=new FileWriter("myModule12\\b.txt");
//一边读,一遍写
char[] chs=new char[1024];
int len; //记录每次读取的字符个数
while ((len=fr.read(chs))!=-1){
//把读取到的有效字符,写入到目标文件
fw.write(chs,0,len);
}
//释放资源
fw.close();
fr.close();
字符缓冲流提高读写效率
BufferedReader和BufferedWriter是一个包装流,它的读和写依赖于Reader和Writer,在内部有一个缓冲区数组,可以提高读写的效率。
//源文件:a.txt ,使用FileReader进行读取
//FileReader fr=new FileReader("myModule12\\a.txt");
BufferedReader br=new BufferedReader(new FileReader("myModule12\\a.txt"));
//目标文件:b.txt,使用FileWriter进行写入
//FileWriter fw=new FileWriter("myModule12\\b.txt");
BufferedWriter bw=new BufferedWriter(new FileWriter("myModule12\\b.txt"));
//一边读,一遍写
char[] chs=new char[1024];
int len; //记录每次读取的字符个数
while ((len=br.read(chs))!=-1){
//把读取到的有效字符,写入到目标文件
bw.write(chs,0,len);
}
//释放资源
bw.close();
br.close();
- 使用BufferedReader和BufferedWriter缓冲流读取写数据,一次读写一行
BufferedReader 有一个读取一个行的方法 readLine()
BufferedWriter 有一个写换行符的方法 newLine(); //具有跨平台性
Win: \r\n
Linux: \r
Mac: \n
//源文件:a.txt ,使用FileReader进行读取
BufferedReader br=new BufferedReader(new FileReader("myModule12\\a.txt"));
//目标文件:b.txt,使用FileWriter进行写入
BufferedWriter bw=new BufferedWriter(new FileWriter("myModule12\\b.txt"));
//读一行,写一行
String line; //每次读取的行
while ((line=br.readLine())!=null){
//把读取的每一行,写入到目标文件
bw.write(line);
//bw.write("\r\n"); //Win: \r\n Linux: \r Mac \n
bw.newLine(); //写一个换行符,具有跨平台性,
}
//释放资源
bw.close();
br.close();
close和flush的区别
flush: 把流中缓冲的数据刷新到文件中
close: 先自动刷新,再释放资源。
文件续写
//字符流,参数true表示可以续写
FileWriter fw=new FileWriter("myModule12\\a.txt",true);
fw.write("world");
fw.close();
//字节流,参数true表示可以续写
FileOutputStream fos=new FileOutputStream("myModule12\\a.txt",true);
fos.write("world".getBytes());
fos.close();
字符转换流
常见的字符编码
ASCII: 美国信息交换码表,包含一些字母、数字、标点符号
一个字符占1个字节
GBK: 中国人的码表,兼容ASCII编码,还包含汉字、日韩文字
一字母占1个字节
一个汉字占2个字节
UTf-8:万国码,包含各个国家的文字
一字母占1个字节
一个汉字占3个字节
注意:当读取数据写如数据的文件编码不一致,就会产生乱码问题。
字符转换流解决乱码问题
Java的API中提供的字符流FileReader和FileWriter默认是按照UTF-8编码进行读和写的。
为了读写其他编码的文件,Java的API又提供了InputStreamReader和OutputStreamWriter两个流,可以指定编码进行读和写。
- 使用InputStreamReader读取指定编码的文本文件
FileInputStream fis = new FileInputStream("C:\\Users\\it\\Desktop\\a.txt");
//把FileInputStream读取的字节数,按照GBK编码进行转换
InputStreamReader isr=new InputStreamReader(fis,"GBK");
//一次读取多个字符
char[] chs=new char[1024];
int len; //记录每次读取的字符个数
while ((len=isr.read(chs))!=-1){
//把数组转好为字符串
String str=new String(chs,0,len);
System.out.println(str);
}
isr.close();
- 使用OutputStreamWriter写入指定编码的文件
FileOutputStream fos = new FileOutputStream("C:\\Users\\it\\Desktop\\b.txt");
//把FileOutputStream + 编码表 转换为 OutputStreamWriter
OutputStreamWriter osw=new OutputStreamWriter(fos,"GBK");
//写数据
osw.write("hello你好");
char[] chs={'h','e','l','l','o'};
osw.write(chs);
//释放资源
osw.close();
- 把a.txt(GBK编码)文件中的字符,写入到b.txt(UTF-8)文件中。
//使用InputStreamReader流,指定编码GBK读取a.txt文件
FileInputStream fis = new FileInputStream("C:\\Users\\it\\Desktop\\a.txt");
InputStreamReader isr=new InputStreamReader(fis,"GBK");
//使用OutputStreamWriter流,指定编码UTF-8写入b.txt文件
FileOutputStream fos=new FileOutputStream("C:\\Users\\it\\Desktop\\b.txt");
OutputStreamWriter osw=new OutputStreamWriter(fos,"UTF-8");
//读写数据
char[] chs=new char[1024];
int len; //记录每次读取到的字符个数
while ((len=isr.read(chs))!=-1){
osw.write(chs,0,len);
}
//释放资源
osw.close();
isr.close();
使用缓冲流进行包装
//使用InputStreamReader流,指定编码GBK读取a.txt文件
FileInputStream fis = new FileInputStream("C:\\Users\\it\\Desktop\\a.txt");
InputStreamReader isr=new InputStreamReader(fis,"GBK");
BufferedReader br=new BufferedReader(isr);
//使用OutputStreamWriter流,指定编码UTF-8写入b.txt文件
FileOutputStream fos=new FileOutputStream("C:\\Users\\it\\Desktop\\b.txt");
OutputStreamWriter osw=new OutputStreamWriter(fos,"UTF-8");
BufferedWriter bw=new BufferedWriter(osw);
//读写一行数据
String line;
while((line=br.readLine())!=null){
bw.write(line);
bw.newLine(); //换行
}
//释放资源
br.close();
bw.close();
对象流(序列化流)
Java的API提供了ObjectInputStream和ObjectOutputStream用来读对象和写对象。
序列化:写对象
反序列化:读对象
注意:被序列化和反序列化的对象,必须实现一个接口Serializable(这个接口仅仅起到一个标记的作用)
//创建序列化流(对象输出流)
ObjectOutputStream oos=new ObjectOutputStream(new FileOutputStream("myModule12\\a.txt"));
GirlFriend gf=new GirlFriend("翠花",18,165,100);
//写对象
oos.writeObject(gf);
//反序列化流(对象输入流)
ObjectInputStream ois=new ObjectInputStream(new FileInputStream("myModule12\\a.txt"));
Object obj = ois.readObject();
System.out.println(obj);
序列号冲突问题
问题:
每次编译源代码都会自动生成一个序列号,如果修改了源代码序列化和反序列化的序列号就会不一致。这个时候会导 致序列号冲突。
解决办法:
在源代码中,写一个固定的序列号,固定写法
private static final long serialVersionUID = -68497944707523234L;
Properties类
Properties是Map的子类,是一个双列集合,键和值都是字符串类型。Map集合的方法它都能使用。
但是推荐是Properties自己特有的方法,对集合进行操作。
public Object setProperty(String key, String value)
添加键和值,如果键重复,旧值会被覆盖
public String getProperty(String key)
据键获取值
public Set<String> stringPropertyNames()
获取键的集合
Properties pro=new Properties();
//使用Properties集合的特有方法,存储键-值
pro.setProperty("张三","18");
pro.setProperty("李四","20");
pro.setProperty("王五","19");
pro.setProperty("王五","22");
//通过键,获取值
String obj = pro.getProperty("李四");
System.out.println(obj);
//遍历
Set<String> keys = pro.stringPropertyNames();
for (String key : keys) {
String vlaue = pro.getProperty(key);
System.out.println(key+"..."+vlaue);
}
Properties提供了两个和IO流相关的方法,用来把键值对存储到文件和读取键值对到集合。
public void store(OutputStream out, String comments)
把集合中的键和值存储到文件中
public void load(Reader reader)
把文件中的键和值读取到集合中
Properties pro=new Properties();
pro.setProperty("zhangsan","20");
pro.setProperty("lis","28");
pro.setProperty("wangwu","21");
//把集合中的键和值,写到文件中
pro.store(new FileWriter("myModule12\\a.txt"),null);
//读取文件中的键和值读取到集合
pro.load(new FileReader("myModule12\\a.txt"));
System.out.println(pro);
Properties集合一般和软件的配置文件一起使用,把软件的相关配置写成一个文件,在文件中键值对的形式来保存配置信息。
Properties pro=new Properties();
// 1.读取配合文件中键对应的值
pro.load(new FileReader("myModule12\\a.txt"));
// 2.把值转换为整数,判断整数是否>0,说明还有使用次数
String count = pro.getProperty("count");
int value = Integer.parseInt(count);
if(value>0){
System.out.println("欢迎使用");
//每次使用之后,需要修改配置文件的值,-1
value--;
pro.setProperty("count",value+"");
//把集合中的键和值写到文件中
pro.store(new FileWriter("myModule12\\a.txt"),null);
}else{
System.out.println("使用次数已到,请登录www.baidu.com");
}