Java IO详解

参考链接:

http://ifeve.com/java-io/

http://www.regexlab.com/zh/encoding.htm

Java IO主要用于原始数据源以及目标媒介之间传输数据,常见的数据源和目标媒介有:

  • 文件
  • 管道
  • 网络连接
  • 内存缓存
  • System.in, System.out, System.error(注:Java标准输入、输出、错误输出)

输入和输出是以程序为中心的,输入指的是将数据输入到程序内存中,输出指的是将数据从内存输出到目标媒介中。

在Java IO中,流是一个核心的概念。流从概念上来说是一个连续的数据流。你既可以从流中读取数据,也可以往流中写数据。流与数据源或者数据流向的媒介相关联。在Java IO中流既可以是字节流(以字节为单位进行读写),也可以是字符流(以字符为单位进行读写)。

输入流: 数据源----->内存      输出流: 内存--->目标媒介

输入流: InputStream  Reader     输出流: OutputStream  Writer

Java IO主要用于如下:

  • 文件访问
  • 网络访问
  • 内存缓存访问
  • 线程内部通信(管道)
  • 缓冲
  • 过滤
  • 解析
  • 读写文本 (Readers / Writers)
  • 读写基本类型数据 (long, int etc.)
  • 读写对象

Java IO分类

根据输入输出以及字节字符,功能特性,可以将IO大致分类如下:

     Java IO详解_第1张图片

字节,字符,编码

字节: 计算机中存储数据的单元,一个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");

 

你可能感兴趣的:(Java基础)