Java I/O(二)--转换流,编码与文件分割


IO 中的使用到了一个设计模式:装饰设计模式。装饰设计模式解决:对一组类进行功能的增强。包装:写一个类(包装类)对被包装对象进行包装;

1、包装类和被包装对象要实现同样的接口;

2、包装类要持有一个被包装对象;

3、包装类在实现接口时,大部分方法是靠调用被包装对象来实现的,对于需要修改的方法我们自己实现。

很多时候我们读取到的字节流需要转换成具体的字符流,然后才能进行处理因此产生了包装后的转换流,转换流的出现方便了键盘录入,并且提高了字节流的处理速度。

1、InputStreamReader 是字节流通向字符流的桥梁:它使用读取的字节解码为字符。它使用的字符集可以由名称指定或显式给定,或者可以接受平台默认的字符集。

每次调用 InputStreamReader 中的一个 read() 方法都会导致从底层输入流读取一个或多个字节。要启用从字节到字符的有效转换,可以提前从底层流读取更多的字节,使其超过满足当前读取操作所需的字节。

2、OutputStreamWriter 是字符流通向字节流的桥梁:可使用要写入流中的字符解码成字节。它使用的字符集可以由名称指定或显式给定,否则将接受平台默认的字符集。 

每次调用 OutputStreamWriter中的write() 方法都会导致在给定字符(或字符集)上调用编码转换器。在写入底层输出流之前,得到的这些字节将在缓冲区中累积。可以指定此缓冲区的大小,不过,默认的缓冲区对多数用途来说已足够大。

转换流的键盘读取示例代码:


<span style="font-family:SimSun;font-size:14px;">  import java.io.BufferedReader;
  import java.io.BufferedWriter;
  import java.io.InputStreamReader;
  import java.io.OutputStreamWriter;
  
  public class Input2Reader {
  	public static void main(String[] args) {
  		BufferedWriter bufferedWriter = null;
  		BufferedReader bufferedReader = null;
  		try {
  			bufferedReader = new BufferedReader(
  					new InputStreamReader(System.in));
  			bufferedWriter = new BufferedWriter(new OutputStreamWriter(
  					System.out));
  
  			String line;
  			while ((line = bufferedReader.readLine()) != null) {
  				if ("over".equals(line)) {
  					break;
  				} else {
  					bufferedWriter.write(line.toUpperCase());
  					bufferedWriter.newLine();
  					bufferedWriter.flush();
  				}
  			}
  
  		} catch (Exception e) {
  			throw new RuntimeException(e.toString());
  		} finally {
  			try {
  				if (bufferedReader != null) {
  					bufferedReader.close();
  				}
  				if (bufferedWriter != null) {
  					bufferedWriter.close();
  				}
  			} catch (Exception e) {
  				throw new RuntimeException(e.toString());
  			}
  		}
  	}
  }</span>

代码可见当键盘录入字符数据时采用转换流将输入的字节流转换成字符流可以有效地提高效率。

文本文件涉及编码解码问题,Java中char默认使用Unicode编码,文本文件使用本地编码。当采用转换流时可指定编码表进行文件编码的处理,如果文本文件与读取时指定的编码方式不同将会可能出现编码转换的错误。

编码就是将现有数据根据编码表翻译成加密文件,解码就是将加密文件根据编码表翻译成原本数据。常见编码方式如下:

1、ASCII:美国标准信息交换码,用一个字节的7位可以表示。

2、ISO8859-1:拉丁码表,用一个字节的8位表示。

3、GBK:中国的中文编码表升级,融合了更多的中文文字符号,采用了两个字节表示一个文字,并且这两个字节最高位为一。

4、Unicode:国际标准码,融合了多种文字,所有文字都用两个字节来表示,Java语言使用的就是Unicode编码,但是包含的字符很有限,只对符数据使用也就是char,但是字符串编码是由本地编码决定的。

5、UTF-8:最多用三个字节来表示一个字符,当一个字节可以表示是采用一个字节表示,当两个字节可以表示是采用两个字节表示,因此说UTF-8字符的表示方法并不固定。

如果文本采用的编码方式与读取方式不同时采用某些方法可以实现文件的正确解析。示例代码如下:

<span style="font-family:SimSun;font-size:14px;">  import java.io.IOException;
  
  public class Test {
  
  	public static void main(String[] args) throws IOException {
  
  		String str = "ab你好cd谢谢";
  
  		int len = str.getBytes("utf-8").length;
  		for (int x = 0; x < len; x++) {
  			System.out.println("截取" + (x + 1) + "个字节结果是:"
  					+ cutStringByU8Byte(str, x + 1));
  		}
  
  	}
  
  	/*
  	 * 在java中,字符串“abcd”与字符串“ab你好”的长度是一样,都是四个字符。 但对应的字节数不同,一个汉字占两个字节。
  	 * 定义一个方法,按照最大的字节数来取子串。 如:对于“ab你好”,如果取三个字节,那么子串就是ab与“你”字的半个,
  	 * 那么半个就要舍弃。如果去四个字节就是“ab你”,取五个字节还是“ab你”.
  	 */
  
  	public static String cutStringByU8Byte(String str, int len)
  			throws IOException {
  
  		byte[] buf = str.getBytes("utf-8");
  
  		int count = 0;
  		for (int x = len - 1; x >= 0; x--) {
  			if (buf[x] < 0)
  				count++;
  			else
  				break;
  		}
  
  		if (count % 3 == 0)
  			return new String(buf, 0, len, "utf-8");
  		else if (count % 3 == 1)
  			return new String(buf, 0, len - 1, "utf-8");
  		else
  			return new String(buf, 0, len - 2, "utf-8");
  
  	}
  
  	public static String cutStringByByte(String str, int len)
  			throws IOException {
  
  		byte[] buf = str.getBytes("gbk");
  
  		int count = 0;
  		for (int x = len - 1; x >= 0; x--) {
  			if (buf[x] < 0)
  				count++;
  			else
  				break;
  		}
  
  		if (count % 2 == 0)
  			return new String(buf, 0, len, "gbk");
  		else
  			return new String(buf, 0, len - 1, "gbk");
  	}
  }</span>

    文件分割可使用配置文件进行打包处理,提高文件分割后合并的安全性与保密性。
    配置文件可采用Properties类,该类是一个HashTable的子类,因此说这种配置文件实际上是一种Map,它将Value与Key成对存储到文件中或从文件中解析出来。

    文件分割代码如下:

<span style="font-family:SimSun;font-size:14px;">package com.amorvos.filesplit;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.util.Properties;

public class FileSplit {

	public static void main(String[] args) {

		File dir = new File("c:/source.mp3");
		File src = new File("c:/");

		if (isValid(dir)) {
			split(dir, src);
		}

	}

	/**
	 * 文件分割前的文件校正
	 * 
	 * @param dir
	 * @return
	 */
	public static boolean isValid(File dir) {
		boolean flag = true;
		if (dir.isDirectory()) {
			flag = false;
			System.out.println("ERROR--文件分割的是一个目录");
		}
		if (!dir.exists()) {
			flag = false;
			System.out.println("ERROR--分割的文件不存在");
		}
		return flag;
	}

	/**
	 * 文件分割
	 * 
	 * @param dir
	 *            原文件地址
	 * @param src
	 *            分割文件输出目录
	 */
	public static void split(File dir, File src) {
		FileInputStream fis = null;
		FileOutputStream fos = null;
		try {

			fis = new FileInputStream(dir);

			byte[] buffer = new byte[1024 * 1024];
			int len = -1;
			int count = 1;

			while ((len = fis.read(buffer)) != -1) {
				File split = new File(src, count + ".part");
				fos = new FileOutputStream(split);
				fos.write(buffer, 0, len);
				fos.flush();
				count++;
			}

			File py = new File(src, count + ".properties");
			fos = new FileOutputStream(py);

			Properties properties = new Properties();
			properties.setProperty("FileName", dir.getName());
			properties.setProperty("PartCount", count + "");

			properties.store(fos, "");

		} catch (Exception e) {
			e.printStackTrace();
		} finally {
			try {
				if (fis != null) {
					fis.close();
				}
				if (fos != null) {
					fos.close();
				}
			} catch (Exception e) {
				e.printStackTrace();
			}
		}
	}
}</span>

    文件合并代码如下:

package com.amorvos.filesplit;

import java.io.File;
import java.io.FileFilter;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.SequenceInputStream;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Enumeration;
import java.util.Properties;

public class FileMerge {

	public static void main(String[] args) throws Exception {
		File dir = new File("C:/");
		
		mergeFile(dir);
	}

	/**
	 * 
	 * @param dir
	 * @return
	 * @throws Exception
	 */
	public static void mergeFile(File dir) throws Exception {
		if (!dir.exists()) {
			throw new RuntimeException("路径不存在");
		}

		File[] property = dir.listFiles(new FileFilter() {
			@Override
			public boolean accept(File pathname) {
				return pathname.getName().endsWith(".properties");
			}
		});

		if (property.length != 1) {
			System.out.println("ERROR--配置文件不正确");
		}

		FileInputStream fis = new FileInputStream(property[0]);

		Properties properties = new Properties();
		properties.load(fis);

		int count = Integer.parseInt(properties.getProperty("PartCount"));
		String filename = properties.getProperty("FileName");
		
		File[] files = dir.listFiles(new FileFilter() {
			@Override
			public boolean accept(File pathname) {
				return pathname.getName().endsWith(".part");
			}
		});

		for (int i = 0; i < count - 1 ; i++) {
			File part = new File(dir, (i + 1) + ".part");
			if (!part.exists()) {
				throw new RuntimeException("ERRO--碎片不正确");
			}
		}

		if (files.length != count - 1) {
			System.out.println("ERROR--合并文件数目不正确");
		}

		merge(dir, count, filename);
	}

	/**
	 * 
	 * @param dir
	 * @param count
	 * @param filename
	 * @throws IOException
	 */
	public static void merge(File dir, int count, String filename)
			throws IOException {

		ArrayList<FileInputStream> al = new ArrayList<FileInputStream>();

		for (int i = 0; i < count - 1; i++) {
			al.add(new FileInputStream(new File(dir, (i + 1) + ".part")));
		}

		Enumeration<FileInputStream> en = Collections.enumeration(al);

		SequenceInputStream sq = new SequenceInputStream(en);

		FileOutputStream fos = new FileOutputStream(new File(dir, filename));

		int len = -1;
		byte[] buffer = new byte[1024];

		while ((len = sq.read(buffer)) != -1) {
			fos.write(buffer, 0, len);
		}

		fos.close();
		sq.close();

	}

}



  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值