参考链接:
http://ifeve.com/java-io/
http://www.regexlab.com/zh/encoding.htm
Java IO主要用于原始数据源以及目标媒介之间传输数据,常见的数据源和目标媒介有:
输入和输出是以程序为中心的,输入指的是将数据输入到程序内存中,输出指的是将数据从内存输出到目标媒介中。
在Java IO中,流是一个核心的概念。流从概念上来说是一个连续的数据流。你既可以从流中读取数据,也可以往流中写数据。流与数据源或者数据流向的媒介相关联。在Java IO中流既可以是字节流(以字节为单位进行读写),也可以是字符流(以字符为单位进行读写)。
输入流: 数据源----->内存 输出流: 内存--->目标媒介
输入流: InputStream Reader 输出流: OutputStream Writer
Java IO主要用于如下:
根据输入输出以及字节字符,功能特性,可以将IO大致分类如下:
字节: 计算机中存储数据的单元,一个8位的二进制数,是一个很具体的存储空间。
字符: 人们使用的记号,抽象意义上的一个符号。
编码:规定每个“字符”分别用一个字节还是多个字节存储,用哪些字节来存储。
常见的编码方式有:
分类 | 编码标准 | 说明 |
单字节字符编码 | ISO-8859-1 | 最简单的编码规则,每一个字节直接作为一个 UNICODE 字符。比如,[0xD6, 0xD0] 这两个字节,通过 iso-8859-1 转化为字符串时,将直接得到 [0x00D6, 0x00D0] 两个 UNICODE 字符,即 "ÖÐ"。 反之,将 UNICODE 字符串通过 iso-8859-1 转化为字节串时,只能正常转化 0~255 范围的字符。 |
ANSI 编码 | GB2312, BIG5, Shift_JIS, ISO-8859-2 …… |
把 UNICODE 字符串通过 ANSI 编码转化为“字节串”时,根据各自编码的规定,一个 UNICODE 字符可能转化成一个字节或多个字节。 反之,将字节串转化成字符串时,也可能多个字节转化成一个字符。比如,[0xD6, 0xD0] 这两个字节,通过 GB2312 转化为字符串时,将得到 [0x4E2D] 一个字符,即 '中' 字。 “ANSI 编码”的特点: 1. 这些“ANSI 编码标准”都只能处理各自语言范围之内的 UNICODE 字符。 2. “UNICODE 字符”与“转换出来的字节”之间的关系是人为规定的。 |
UNICODE 编码 | UTF-8, UTF-16, UnicodeBig …… |
与“ANSI 编码”类似的,把字符串通过 UNICODE 编码转化成“字节串”时,一个 UNICODE 字符可能转化成一个字节或多个字节。 与“ANSI 编码”不同的是: 1. 这些“UNICODE 编码”能够处理所有的 UNICODE 字符。 2. “UNICODE 字符”与“转换出来的字节”之间是可以通过计算得到的。 |
字符串是一种抽象意义上的表示方法,例如“字符串123”可以认为有6个字符组成,其中汉字和数字都认为是一个字符。
而字节串是从存储角度来说,每一个字符是多少个字节存储的,例如在GBK编码方式下,“字符串123”有9个字节存储,其中每个汉字两个字节,每个数字一个字节。
public static void main(String[] args) throws Exception {
System.out.println("本机的默认编码 ====== "+System.getProperty("file.encoding"));
String str = "字符串123";
System.out.println("字符串的长度 " + str.length());
System.out.println("存储字节的长度 " + str.getBytes().length);
System.out.println("GBK存储字节的长度 " + str.getBytes("GBK").length + " " + Arrays.toString(str.getBytes("GBK")));
System.out.println("UTF8存储字节的长度 " + str.getBytes("UTF8").length + " " + Arrays.toString(str.getBytes("UTF8")));
}
测试如上所示: 可知字符串的长度为6,在不同编码方式下,存储的字节长度不同,对于数字是采用ASC||编码,对于汉字则GBK与UTF8编码方法则不一样。
本机的默认编码 ====== GBK
字符串的长度 6
存储字节的长度 9
GBK存储字节的长度 9 [-41, -42, -73, -5, -76, -82, 49, 50, 51]
UTF8存储字节的长度 12 [-27, -83, -105, -25, -84, -90, -28, -72, -78, 49, 50, 51]
对于字符串“字符串123”以不同编码方式存入文件中,然后通过字节流读取出来,每次读取一个字节,易得GBK应该循环9次,UTF8应该循环12次。
@Test
public void testByte() throws Exception{
String filepath = "D://debug_drv2-stream.log";
String encode = "UTF8";
OutputStream os = new FileOutputStream(new File(filepath));
os.write(new String("字符串123").getBytes(encode));
os.close();
InputStream is = new FileInputStream(new File(filepath));
int n = -1;
int count = 0;
while((n = is.read())!=-1){
count++;
System.out.println(n);
}
System.out.println("count = " + count);
is.close();
byte[] bytes = new byte[1024];
InputStream is2 = new FileInputStream(new File(filepath));
is2.read(bytes);
System.out.println(new String(bytes, encode));
is2.close();
}
当利用输出流将字符串往文件中写入时,一定要指定编码,如果没有指定这里由于Windows操作系统原因默认使用GBK编码。当利用输入流从文件中读取字节时,将字节转换为字符串,一定要指定编码方式,默认为GBK。
在Java中,具体字符与字节的编码转换可以通过String来实现,
字符转换为字节:
public byte[] getBytes(String charsetName)
throws UnsupportedEncodingException {
if (charsetName == null) throw new NullPointerException();
return StringCoding.encode(charsetName, value, 0, value.length);
}
字节转换为字符:
public String(byte bytes[], String charsetName)
throws UnsupportedEncodingException {
this(bytes, 0, bytes.length, charsetName);
}
默认在同一个操作系统下,使用相同编码来转换不会出现乱码,如果编码方式不一致则会出现乱码现象。
String str = "字符串123";
byte[] bytes = str.getBytes("UTF8");
System.out.println(new String(bytes));
System.out.println(new String(bytes,"UTF8"));
瀛楃涓?123
字符串123
由于str.getBytes("")使用UTF8来编码,而转换为String时,第一种方式没有 指定默认为GBK,因此乱码;第二种指定编码方式,则不会出现乱码。
Java IO基本使用方式:
字节输出流写:
@Test
public void testOutputStream() throws Exception{
OutputStream os = new FileOutputStream(new File("D://debug_drv2-stream.log"));
os.write(new String("字符串123").getBytes());
os.close();
}
字节输入流读:
@Test
public void testInputStream() throws Exception{
byte[] bytes = new byte[1024];
InputStream is = new FileInputStream(new File("D://debug_drv2-stream.log"));
// StringBuilder sb = new StringBuilder();
// int n = -1;
// int count = 0;
// while((n = is.read())!=-1){
// count++;
// System.out.println(n);
// sb.append((char)n);
// }
// System.out.println(sb.toString());
// System.out.println("count = " + count);
is.read(bytes);
System.out.println(Arrays.toString(bytes));
System.out.println(new String(bytes));
is.close();
}
使用字节流时一定要注意编码方式的统一。
字符输出流写:
@Test
public void testWriter() throws Exception{
Writer w = new FileWriter(new File("D://debug_drv2-writer.log"));
w.write(new String("字符串123"));
w.close();
}
字符输入流读:
@Test
public void testReader() throws Exception{
//Reader r = new InputStreamReader(new FileInputStream(new File("D://debug_drv.log")));
Reader r = new FileReader(new File("D://debug_drv2-writer.log"));
int c = -1;
int count = 0;
while((c = r.read())!=-1){
count++;
System.out.println(c + "----" + (char)c);
}
System.out.println("count = " + count);
r.close();
}
字符流与字节流可以相互转换:
InputStream is = new FileInputStream(new File("D://debug_drv2-stream.log"));
InputStreamReader isr = new InputStreamReader(is, "GBK");
ReaderInputStream ris = new ReaderInputStream(isr, "GBK");