Java课堂篇12_字符集编码、IO读、写、文件复制、Buffered缓冲处理流相关

写在前面

编码知识是每个开发人员都需要掌握的知识 ,乱码问题是开发中常遇到的问题,这一点我深有体会

编码是解决不同语言转换的桥梁

目录


  • 字符集编码
  • IO相关
    • 文件读、写
      • FileWriter、FileReader字符节点流组合
      • OutputStreamWriter字符处理流、FileOuputStream字节节点流组合输出数据
        InputStreamReader字符处理流、FileInputStream字节节点流组合读取数据
    • 文件复制
      • FileReader、FileWriter组合文件复制
      • FileInputStream、FileOutputStream组合文件复制
      • BufferReader、BufferWriter组合文件复制
    • Buffered缓冲处理流

开始

一、字符集编码

参考:菜鸟教程-字符集和字符编码(Charset & Encoding)
参考:百度百科

1.1小结一下课堂上涉及到的部分编码知识

常见的字符编码

  1. ACSII码:美国信息标准交换标准代码,主要对接老外,它是现今最通用的单字节编码系统,使用7位表示一个字符共128个字符,等同于国际标准ISO 646

    • ASCII扩展码:为了表示欧洲更多常用的字符,使用8位表示一个字符,共256个字符,扩充后的符号增加了表格符号、计算符号、希腊字母和特殊的拉丁符号。
  2. ISO-8859码:ISO8859 不是一个标准,而是一系列的标准,这套字符集与编码系统的共同特色是,以同样的码位对应不同字符集。

    • 历史上存在两个独立的尝试创立单一字符集的组织,即国际标准化组织(ISO)和多语言软件制造商组成的统一码联盟。前者开发的 ISO/IEC 10646 项目,后者开发的统一码项目。因此最初制定了不同的标准。
    • 后来两个组织开始合作合并双方的工作成果
    • 从Unicode 2.0开始,Unicode采用了与ISO 10646-1相同的字库和字码;ISO也承诺,ISO 10646将不会替超出U+10FFFF的UCS-4编码赋值,以使得两者保持一致。两个项目仍都存在,并独立地公布各自的标准。
  3. ANSI编码:不同的国家和地区制定了不同的标准,由此产生了 GB2312、GBK、Big5、Shift_JIS 等各自的编码标准。这些使用 1 至 4 个字节来代表一个字符的各种汉字延伸编码方式,称为 ANSI 编码

    • 每个国家都有一套,需要对应各国的代码页,中国的是936
    • 以0开头的8位是一个字符,1开头的两个 8 位 是一个汉字
  4. BIG5码

  5. GBX系列编码:国标码,主要是对汉字圈国家,

    • GB2312码:GB2312是中国国家标准的简体中文字符集。它所收录的汉字已经覆盖99.75%的使用频率,基本满足了汉字的计算机处理需要。在中国大陆和新加坡获广泛使用。对于人名、古汉语等方面出现的罕用字,GB2312不能处理,这导致了后来GBK及GB 18030汉字字符集的出现
    • GBK码:GBK是对GB2312-80的扩展,也就是CP936字码表
    • GBK 18030:与UTF-8相同,每个字可以由1、2、4个字节组成。汉字收录范围包含繁体汉字以及日韩汉字。
  6. Unicode字符集&UTF编码:万国码
    像天朝一样,当计算机传到世界各个国家时,为了适合当地语言和字符,设计和实现类似GB232/GBK/GB18030/BIG5的编码方案。这样各搞一套,在本地使用没有问题,一旦出现在网络中,由于不兼容,互相访问就出现了乱码现象。
    (可以这样理解:Unicode是字符集,UTF-32/ UTF-16/ UTF-8是三种字符编码方案。)

    • UTF-32:对每个字符都使用4字节。就空间而言,是非常没有效率的。就空间而言,是非常没有效率的。
      这种方法有其优点,最重要的一点就是可以在常数时间内定位字符串里的第N个字符,因为第N个字符从第4×Nth个字节开始。虽然每一个码位使用固定长定的字节看似方便,它并不如其它Unicode编码使用得广泛。

    • UTF-16:尽管有Unicode字符非常多,但是实际上大多数人不会用到超过前65535个以外的字符。因此,就有了另外一种Unicode编码方式,叫做UTF-16(因为16位 = 2字节)。

    • UTF-8:是一种针对Unicode的可变长度字符编码(定长码),也是一种前缀码。它可以用来表示Unicode标准中的任何字符,且其编码中的第一个字节仍与ASCII兼容,这使得原来处理ASCII字符的软件无须或只须做少部份修改,即可继续使用。因此,它逐渐成为电子邮件、网页及其他存储或传送文字的应用中,优先采用的编码。互联网工程工作小组(IETF)要求所有互联网协议都必须支持UTF-8编码。
      UTF-8使用一至四个字节为每个字符编码:

    • 大头、小头模式

      对于UTF-32和UTF-16编码方式还有一些其他不明显的缺点。不同的计算机系统会以不同的顺序保存字节。这意味着字符U+4E2D在 UTF- 16编码方式下可能被保存为4E 2D或者2D 4E,这取决于该系统使用的是大尾端(big-endian)还是小尾端(little-endian)。

      为了解决这个问题,多字节的Unicode编码方式定义了一个"字节顺序标记(Byte Order Mark)",它是一个特殊的非打印字符,你可以把它包含在文档的开头来指示你所使用的字节顺序。对于UTF-16,字节顺序标记是U+FEFF。如果收到一个以字节FF FE开头的UTF-16编码的文档,你就能确定它的字节顺序是单向的(one way)的了;如果它以FE FF开头,则可以确定字节顺序反向了。

1.2在记事本中占字节大小

  1. ANSI编码:一个字母占8位,一个汉字占16位
  2. UTF-8:一个字母应该占1个字节,但是一个字母实际占4个字节,两个字母占5个字节
  3. Unicode:一个字母占2个字节,一个汉字占2个字节

二、IO相关

目前主要涉及的是简单的同步IO,以后还会涉及异步IO,阻塞IO、NIO等
在这里插入图片描述

大致划分

  • 字节流
    • InputStream
    • OutStream
  • 字符流
    • Reader
    • Writer

字符流也是字节流,万物皆字节

也可划分

  • 节点流
  • 处理流

2.1写、读文件

组合1

  • FileWriter字符节点流输出数据
  • FileReader字符节点流写入数据
    • 根据操作系统的编码格式确定文件的编码格式,输出为Unicode编码格式
    • 单个整数的write()写入为ASCII码,字符串的写入为原字符

组合2

  • OutputStreamWriter字符处理流、FileOuputStream字节节点流组合输出数据
  • InputStreamReader字符处理流、FileInputStream字节节点流组合读取数据
    • 可以指定读取文件默认编码格式,
    • 为了区分InputStream的read方法的结束返回,返回一个4个字节的-1,读了一个字节,返回4个字节,只有低字节有效区分字符,无符号数据255
    • 对于write的write方法,传入一个4字节,但是只是低16位有效
2.1.1FileWriter字符输出流写入

FileWrite为字符输出流,节点流

举栗

在这里插入图片描述
在这里插入图片描述

在这里插入图片描述
单个写入 默认写入的为ASCIi编码的字符

如果想得到原样,写入的时候需要以字符串的形式写入

package four.test;

import java.io.File;
import java.io.FileWriter;
import java.io.IOException;

public class TestFileReaderAndWriter {
    
    
	public static void main(String[] args) {
    
    
		FileWriter fWriter = null;
		try {
    
    
			//创建文件
			File file = new File("src\\a.txt");
			
			fWriter = new FileWriter(file);
			//写文件
			for (int i = 0; i < 100; i++) {
    
    
				fWriter.write(i + " ");
			}
			fWriter.close();
		} catch (Exception e) {
    
    
			// TODO: handle exception
			e.printStackTrace();
			System.out.println("文件写入错误!");
			System.exit(-1);
		}
		finally {
    
    
			
		}
		
		
		
	}

}

在这里插入图片描述

2.1.2FileReader字符输入流读取文件

字符输入流,节点流

public static void testFileReader() {
    
    
		File file = new File("src//a.txt");
		FileReader fileReader = null;
		
		try {
    
    
			fileReader = new FileReader(file);
			int ln = 0;
			while((ln = fileReader.read()) != -1) {
    
    
				System.out.println(ln);
			}
		} catch (Exception e) {
    
    
			// TODO: handle exception
			e.printStackTrace();
		}
	}

在这里插入图片描述
在这里插入图片描述

2.1.3OutputStreamWriter字符处理流、FileOutputStream字节节点输出流写入文件

需要注意关闭流的顺序,先开的后关,否则报错

public static void testOutputStreamWriter() {
    
    
		OutputStreamWriter osw = null;
		FileOutputStream fos = null;
		try {
    
    
			//创建文件
			File file = new File("src\\b.txt");
			
			fos = new FileOutputStream(file);
			osw = new OutputStreamWriter(fos);
			//写文件
			for (int i = 0; i < 100; i++) {
    
    
				osw.write(i+" ");
			}
			
			osw.close();
			fos.close();
		} catch (Exception e) {
    
    
			// TODO: handle exception
			e.printStackTrace();
			System.out.println("文件写入错误!");
			System.exit(-1);
		}
		finally {
    
    
			
		}
		
	}

在这里插入图片描述

2.1.4InputStreamReader字符处理流、FileInputStream字节节点流读取文件

大致步骤类似

public static void testInputStreamReader() {
    
    
		InputStreamReader isr = null;
		FileInputStream fis = null;
		
		try {
    
    
			//创建文件
			File file = new File("src\\b.txt");
			
			fis  = new FileInputStream(file);
			isr = new InputStreamReader(fis);
			
			//读文件
			int ln = 0;
			while((ln = isr.read()) != -1) {
    
    
				System.out.print((char)ln );
			}
			
		} catch (Exception e) {
    
    
			// TODO: handle exception
			e.printStackTrace();
			System.out.println("文件写入错误!");
			System.exit(-1);
		}
		finally {
    
    
			
		}
		
	}

在这里插入图片描述

2.2文件复制

2.2.1FileReader、FileWriter组合

使用FileReader、FileWriter来实现文件复制

  • 注意仅限于一般的文本文件,文本文件编码为GBK,对于其他识别不了编码的文件,reader()读取的过程中遇到未识别的编码就会停止读写。
  • 速度很慢,因为是从一个文件中一个个的取出字符,送到另一个文件
import java.io.*;
public class FileCopy {
    
    
  public static void main(String[] args) {
    
    
	  int b = 0;
	  FileReader in = null;
	  FileWriter out = null;
	  try {
    
    
	    in = new FileReader("d:/share/java/HelloWorld.java");
	    out = new FileWriter("d:/share/java/io/HW.java");
	    while((b=in.read())!=-1){
    
    
	      out.write(b);
	    }
	    out.close();
	    in.close(); 
	    
	  } catch (FileNotFoundException e2) {
    
    
	    System.out.println("找不到指定文件"); System.exit(-1);
	  } catch (IOException e1) {
    
    
	    System.out.println("文件复制错误"); System.exit(-1);
	  }
	  System.out.println("文件已复制");
  }
}

2.2.2 FileInputStream、FileOutputStream组合文件复制
  • 对于非文本文件也适合,不会存在解码失败复制失败的问题,因为这是一个字节一个字节的读取、写入
  • 速度也是很慢
public static void testFileCopy1() {
    
    
		
		FileInputStream fis = null;
		
		
		FileOutputStream fos = null;
		
		try {
    
    
			//读取文件
			File file1 = new File("src\\b.txt");
			fis  = new FileInputStream(file1);
			
			
			//写出文件
			File file2 = new File("src\\a.txt");
			fos = new FileOutputStream(file2);
			
			
			int ln = 0;
			while((ln = fis.read()) != -1) {
    
    
				fos.write(ln);
			}
			
			fis.close();
			
			
			fos.close();
			
		} catch (Exception e) {
    
    
			// TODO: handle exception
			e.printStackTrace();
			System.out.println("文件复制错误!");
			System.exit(-1);
		}
		finally {
    
    
			
		}
		
		
	}
public static void testFileCopy2() {
    
    
		InputStreamReader isr = null;
		FileInputStream fis = null;
		
		OutputStreamWriter osw = null;
		FileOutputStream fos = null;
		
		try {
    
    
			//读取文件
			File file1 = new File("src\\b.txt");
			fis  = new FileInputStream(file1);
			isr = new InputStreamReader(fis);
			
			//写出文件
			File file2 = new File("src\\a.txt");
			fos = new FileOutputStream(file2);
			osw = new OutputStreamWriter(fos);
			
			int ln = 0;
			while((ln = isr.read()) != -1) {
    
    
				osw.write(ln);
			}
			isr.close();
			fis.close();
			
			osw.close();
			fos.close();
			
		} catch (Exception e) {
    
    
			// TODO: handle exception
			e.printStackTrace();
			System.out.println("文件复制错误!");
			System.exit(-1);
		}
		finally {
    
    
			
		}
		
		
	}

2.3Buffer缓冲处理流

  • FileInputStream、BufferedInputStream组合读取文件
import java.io.*;
public class TestBufferStream1 {
    
    
  public static void main(String[] args) {
    
    
    try {
    
    
      FileInputStream fis = new FileInputStream("d:\\share\\java\\HelloWorld.java");
      BufferedInputStream bis = new BufferedInputStream(fis);
      int c = 0;
      System.out.println(bis.read());
      System.out.println(bis.read());
      bis.mark(100);
      for(int i=0;i<=10 && (c=bis.read())!=-1;i++){
    
    
			System.out.print((char)c+" ");
      }
      System.out.println(); 
      bis.reset();
      for(int i=0;i<=10 && (c=bis.read())!=-1;i++){
    
    
			System.out.print((char)c+" ");
      }
      bis.close();
    } catch (IOException e) {
    
    e.printStackTrace();}
  }
}
  • BufferedReader、BufferedWriter实现文件内容随机互换
import java.io.*;
public class TestBufferStream2 {
    
    
  public static void main(String[] args) {
    
    
    try {
    
    
      BufferedWriter bw = new BufferedWriter(new FileWriter("d:\\share\\java\\dat2.txt"));
      BufferedReader br = new BufferedReader(new FileReader("d:\\share\\java\\dat2.txt"));
      String s = null;
      for(int i=1;i<=100;i++){
    
    
			s = String.valueOf(Math.random());
			bw.write(s);
			bw.newLine();
      }
      bw.flush();
      while((s=br.readLine())!=null){
    
    
			System.out.println(s);
      }
      bw.close(); 
      br.close();
    } catch (IOException e) {
    
     e.printStackTrace();}
  }
}

猜你喜欢

转载自blog.csdn.net/qq_24654501/article/details/111622110