一、字节流vs字符流
在实际开发中,字节流一定是优先考虑的,只有在处理中文时才会考虑字符流。因为所有的字符都需要通过内存缓冲来进行处理。所有字符流的操作,无论是输入还是输出,数据都先保存在缓存中。
1.字节流输出和字符流输出的区别
如果字符流不关闭,数据就有可能保存在缓存中并没有输出到目标源。这种情况下,必须强制刷新才能得到完整数据。
package com.xunpu.myio;
import java.io.*;
/**
* 字符流的强制刷新操作
*/
public class TestFlush {
public static void main(String[] args) {
File file=new File(File.separator+"E:"+File.separator+"JavaFile"+File.separator+
"Directory"+File.separator+"bbb"+File.separator+"hello.txt");
if(!file.getParentFile().exists()){
file.mkdirs();//如果父文件对象不存在,就递归创建父目录。
}
try(Writer writer=new FileWriter(file);
Reader reader=new FileReader(file)) {
//写数据
writer.write("hello world");
//读数据
char[] chars=new char[1024];
int len=reader.read(chars);
String res=new String(chars);
System.out.println("文件内容为:"+res);//此时没有内容输出,这是因为writer没有刷新到缓冲区
} catch (IOException e) {
e.printStackTrace();
}
}
}
此时结果为:
这是因为数据保存在缓冲区。解决办法:强制刷新缓冲区中的数据。
package com.xunpu.myio;
import java.io.*;
/**
* 字符流的强制刷新操作
*/
public class TestFlush {
public static void main(String[] args) {
File file=new File(File.separator+"E:"+File.separator+"JavaFile"+File.separator+
"Directory"+File.separator+"bbb"+File.separator+"hello.txt");
if(!file.getParentFile().exists()){
file.mkdirs();//如果父文件对象不存在,就递归创建父目录。
}
try(Writer writer=new FileWriter(file);
Reader reader=new FileReader(file)) {
//写数据
writer.write("hello world");
writer.flush();//强制刷新
//读数据
char[] chars=new char[1024];
int len=reader.read(chars);
String res=new String(chars);
System.out.println("文件内容为:"+res);//此时没有内容输出,这是因为writer没有刷新到缓冲区
} catch (IOException e) {
e.printStackTrace();
}
}
}
二、转换流
转换流就是将字节流转换为字符流的流。字符流类中提供了把字节流转换为相应字符流的类:
public class OutputStreamWriter extends Writer;
实现方法:OutputStreamWriter类的构造方法:
public OutputStreamWriter(OutputStream out);
public class InputStreamReader extends Reader;
实现方法:InputStreamReader类的构造方法:
public InputStreamReader(InputStream in)
使用方法:
package com.xunpu.myio;
import java.io.*;
/**
* 测试将字节流转换为字符流
*/
public class TestChange {
public static void main(String[] args) throws IOException {
File file=new File("E:"+File.separator+"JavaFile"+File.separator+"Directory"+File.separator+"bbb"
+File.separator+"aaa.txt");
OutputStream out=new FileOutputStream(file);
OutputStreamWriter writer=new OutputStreamWriter(out);//将输出字节流转换为字符流
writer.write("The weather is fine");
writer.close();
InputStream in=new FileInputStream(file);
InputStreamReader reader=new InputStreamReader(in);//将输入字节流转为输入字符流
char[] chars=new char[1024];
int len = reader.read(chars);
String res=new String(chars);
System.out.println("文件内容为:"+res);
}
}
三、文件拷贝案例
思路:
/** * 实现文件拷贝(通过传参数的方式) * 1.判断拷贝的源文件(args[0])是否存在。 * 1.1 如果不存在,则抛出异常(FileNotFoundException) * 1.2 如果存在,进行下一步操作 * 2.判断目的文件(args[1])的父路径是否存在。 * 2.1 如果不存在,则递归创建父路径,继续下一步。 * 2.2 如果存在,什么也不做。 * 3.进行文件的拷贝工作。 * 从源文件中读取数据,同时向目的文件中写入数据。直到读源文件到末尾。 */
package com.xunpu.myio;
import java.io.*;
/**
* 实现文件拷贝
* 1.判断拷贝的源文件(args[0])是否存在。
* 1.1 如果不存在,则抛出异常(FileNotFoundException)
* 1.2 如果存在,进行下一步操作
* 2.判断目的文件(args[1])的父路径是否存在。
* 2.1 如果不存在,则递归创建父路径,继续下一步。
* 2.2 如果存在,什么也不做。
* 3.进行文件的拷贝工作。
* 从源文件中读取数据,同时向目的文件中写入数据。直到读源文件到末尾。
*/
public class CopyFile {
public static void main(String[] args) throws IOException {
if(args.length!=2){
System.out.println("不符合拷贝文件要求,请给出源文件路径和目的文件路径,重新尝试");
return;
}
//1.判断源文件是否存在
boolean isSourceexist=sourceFileIsExisted(args[0]);
if(!isSourceexist){
System.out.println("源文件不存在,拷贝失败!");
throw new FileNotFoundException();
}
//2.判断目的文件的父目录对象是否存在,不存在则创建。
isParentDirectory(args[1]);
//3.进行文件拷贝
File res=copyFile(args[0],args[1]);
//打印出文件的内容
Reader reader=new FileReader(res);
char[] data=new char[1024];
int len=reader.read(data);
String str=new String(data,0,len);
System.out.println("文件内容为:"+str);
}
//文件拷贝具体实现
private static File copyFile(String srcPath, String desPath) throws IOException {
File src=new File(srcPath);
File des=new File(desPath);
//定义输出流
InputStream in=new FileInputStream(src);
InputStreamReader reader=new InputStreamReader(in);
//定义输入流
OutputStream out=new FileOutputStream(des);
OutputStreamWriter writer=new OutputStreamWriter(out);
//进行文件的拷贝
int len=0;
char[] data=new char[1024];
while((len=reader.read(data))!=-1){
writer.write(data,0,len);
}
writer.flush();//强制刷新缓冲区的字符
return des;
}
//判断目的文件的父目录对象是否存在
private static void isParentDirectory(String desPath) {
File file=new File(desPath);
if(!file.getParentFile().exists()) {//取得父File对象,并判断是否存在。
file.mkdirs();//如果不存在,则递归创建。
}
}
//判断源文件是否存在
private static boolean sourceFileIsExisted(String sourcePath) {
File file=new File(sourcePath);
return file.exists();
}
}
将配置环境变量设置为:
注意:在使用字符流写文件时,一定要使用writer.flush()将缓冲区的数据刷新!!!
四、字符编码
在计算机的世界里,所有的文件都是通过编码来描述的。
1.常见的编码
1)描述中文的编码:GBK、GB2312:国标编码,GBK支持简体中文和繁体中文,而GB2312只包含简体中文。
2)支持所有的文字信息:UNICODE编码,Java提供的16进制编码,可以描述世界上任意的文字信息。缺点是:编码较庞大,会造成网络传输上的负担。
3)国际通用编码:ISO8859-1,单字节编码,向下兼容ASCII码,其编码范围是0X00~0XFF.缺点:所有的编码都需要进行转换。
4)UTF编码:结合了UNICODE编码和ISO8859-1编码。如果使用16进制文字就使用UNICODE;如果是字母就使用ISO8859-1。常用的是UTF-8编码。
2.查看操作系统中Java默认支持的编码
package com.xunpu.myio;
/**
* 测试字符编码
*/
public class TestCode {
public static void main(String[] args) {
System.getProperties().list(System.out);//获取Java运行属性
}
}
结果截取一部分:
sun.jnu.encoding=GBK是操作系统中的默认编码,而file.encoding=UTF-8是我们自己设置的。
3.观察乱码
如果本地系统所用的编码和程序所用编码不同,那么强制转换就会出现乱码。
package com.xunpu.myio;
import java.io.*;
/**
* 测试字符编码
*/
public class TestCode {
public static void main(String[] args) throws IOException {
File file=new File("E:"+File.separator+"JavaFile"+File.separator+"Directory"+File.separator+"bbb"
+File.separator+"aaa.txt");
OutputStream out=new FileOutputStream(file);
out.write("你好,我是xxx".getBytes("ISO8859-1"));//设置编码格式为ISO8859-1
out.close();
}
}
结果:
改正方法为:
package com.xunpu.myio;
import java.io.*;
/**
* 测试字符编码
*/
public class TestCode {
public static void main(String[] args) throws IOException {
File file=new File("E:"+File.separator+"JavaFile"+File.separator+"Directory"+File.separator+"bbb"
+File.separator+"aaa.txt");
OutputStream out=new FileOutputStream(file);
out.write("你好,我是xxx".getBytes("GBK"));//或者设置为UTF-8
out.close();
}
}
结果:
乱码产生的本质:编码和解码不统一。
建议:以后使用UTF-8编码!!!