47-IO操作深入

IO操作深入

字符编码

  在计算机世界中只认0、1的数据,如果要想描述一些文字的编码就需要对这些二进制的数据进行组合,所以才有了现在可以看见的中文,但是进行编码的时候要想正确显示出内容一定要有解码,所以编码和解码肯定要采用同一套的统一的标准,如果不统一的时候就会出现乱码。
  在实践中开发中常用的编码有如下几种:

  • GBK/GB2312:国标编码,可以描述中文信息,其中GB2312只描述简体中文,而GBK包含简体与繁体;
  • ISO8859-1:国际通用编码,可以用其描述所有的字母信息,如果是象形文字则需要进行编码处理;
  • UNICODE编码:采用十六进制的方式存储,可以描述所有的文字;
  • UTF编码:象形文字部分使用十六进制编码,而普通字母采用的是ISO8859-1编码,优势在于快速传输节约带宽,也就成为了开发中首先得编码(“UTF-8”)。

  如果要想知道当前系统中支持的编码规则,则可以采用如下代码列出全部本机属性:
列出本机属性

public class Char_Encode {
    
    

	public static void main(String[] args) {
    
    
		System.getProperties().list(System.out);
	}
}

  不设置编码时,使用默认编码进行,可通过强制指定编码。

import java.io.File;
import java.io.FileOutputStream;
import java.io.OutputStream;

public class Char_Encode {
    
    

	public static void main(String[] args) throws Exception {
    
    
		OutputStream output = new FileOutputStream("C:"+File.separator+"mld.txt");
		output.write("中国人民万岁".getBytes("ISO8859-1"));		//指定编码
	}
}

  项目中出现的乱码问题就是编码和解码标准的不统一,而最好的解决乱码的方式,所有的编码方式都使用“UTF-8”。

内存操作流

  之前使用的全部都是文件操作流,文件操作的特点,程序利用InputStream读取文件内容,之后程序利用OutputStream向文件输出内容,所有操作都是以程序为终端的。
ynCVoT.png

  假设现在需要实现IO操作,但是又不希望产生文件(临时文件)则就可以以内存为终端进行出,这个时候的流程如下:
ynCnW4.png
  在Java中提供两类内存操作流:

  • 字节内存操作流:ByteArrayOutputStream(OutputStream子类)、ByteArrayInputStream(InputStream子类
  • 字符内存操作流:CharArrayWriter、CharArrayReader

  下面以ByteArrayOutputStream、ByteArrayInputStream类为主进行内存使用分析:

  • ByteArrayOutputStream构造方法:public ByteArrayOutputStream();
  • ByteArrayInputStream构造方法:public ByteArrayInputStream(byte[] buf);

在ByteArrayOutputStream类中有一个重要的方法,这个方法可以获取全部保存在内存流中的数据信息:

  • 获取数据:public byte[] toByteArray();
  • 使用字符串形式来获取:public String toString();

利用内存流时间小写字母转大写字母

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.InputStream;
import java.io.OutputStream;

public class Memory_IO {
    
    

	public static void main(String[] args) throws Exception {
    
    
		String str = "test!";
		InputStream input = new ByteArrayInputStream(str.getBytes());		//将数据保存在内存流
		OutputStream output = new ByteArrayOutputStream();
		int data = 0;
		while((data = input.read()) != -1) {
    
    		//每次读取一个字节
			output.write(Character.toUpperCase((char)data));
		}
		System.out.println(output);
		input.close();
		output.close();
	}
}

  如果现在不希望只是以字符串的形式返回,因为存放的可能是其他二进制数据,那么此时就可以利用ByteArrayOutputStream子类的扩展功能获取全部数据。
获取全部字节数据

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.InputStream;
import java.io.OutputStream;

public class Memory_IO {
    
    

	public static void main(String[] args) throws Exception {
    
    
		String str = "test!";
		InputStream input = new ByteArrayInputStream(str.getBytes());		//将数据保存在内存流
		// 必须使用子类来调用子类自己的扩展方法
		ByteArrayOutputStream output = new ByteArrayOutputStream();
		int data = 0;
		while((data = input.read()) != -1) {
    
    		//每次读取一个字节
			output.write(Character.toUpperCase((char)data));
		}
		byte result[] = output.toByteArray();		//获取全部数据
		System.out.println(new String(result));		//自己处理字节数据
		input.close();
		output.close();
	}
}

  在最初时可以利用ByteArrayOutputStream实现大规模文本文件的读取。

管道流

  管道流主要功能是实现两个线程之间的IO操作。
yn18Fe.png
  对于管道流也是分为两类:

  • 字符管道流:PipedWriter(Writer子类)、PipedReader(Reader子类)
    • 连接处理:public void connect(PipedInputStream snk) throws IOException
  • 字节管道流:PipedOutputStream(OutputStream子类)、PipedInputStream(InputStream子类)
    • 连接处理:public void connect(PipedReader snk) throws IOException

实现管道处理

//代码有问题
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.PipedInputStream;
import java.io.PipedOutputStream;

class SendThread implements Runnable{
    
    
	private PipedOutputStream output;		//管道输出流
	public SendThread() {
    
    
		this.output = new PipedOutputStream();		//实例化管道输出流
	}
	@Override
	public void run() {
    
    
		for(int x=0; x<10;x++) {
    
    
			try {
    
    		//利用管道实现数据发送处理
				this.output.write(("No:"+(x+1)+"-"+Thread.currentThread().getName()+"信息发送\n").getBytes());
			} catch (IOException e) {
    
    
				e.printStackTrace();
			}
			try {
    
    
				this.output.close();
			} catch (IOException e) {
    
    
				e.printStackTrace();
			}
		}
	}
	public PipedOutputStream getOutput() {
    
    
		return output;
	}
}

class ReceiveThread implements Runnable{
    
    
	private PipedInputStream input;
	public ReceiveThread() {
    
    
		this.input = new PipedInputStream();
	}
	@Override
	public void run() {
    
    
		byte data[] = new byte[1024];
		int len = 0;
		ByteArrayOutputStream bos = new ByteArrayOutputStream();		//将所有数据保存到内存输出流
		try {
    
    
			while((len = this.input.read(data)) != -1) {
    
    
				bos.write(data,0,len);		//所有数据保存到内存流bos
			}
		} catch (IOException e) {
    
    
			e.printStackTrace();
		}
		System.out.println("{"+ Thread.currentThread().getName() +"接收消息}" + new String(bos.toByteArray()));
		try {
    
    
			this.input.close();
		} catch (IOException e) {
    
    
			e.printStackTrace();
		}
	}
	public PipedInputStream getInput() {
    
    
		return input;
	}
}

public class Piped_IO {
    
    

	public static void main(String[] args) throws IOException {
    
    
		SendThread send = new SendThread();
		ReceiveThread receive = new ReceiveThread();
		send.getOutput().connect(receive.getInput());		//进行管道连接
		new Thread(send,"发送线程").start();
		new Thread(receive,"接收线程").start();
	}
}

  管道类似于医院打点滴的效果,一个负责发送另一个负责接收,中间靠管道连接。

RandomAccessFile(随机读取类)

  对于文件内容的处理操作主要是通过InputStream(Reader),OutputStream(Writer)来实现,但是利用这些类实现的内容读取只能够将数据部分读取进来,如果现在有这样一种要求:
  现在给了一个非常庞大的文件,如果此时按照传统的IO操作进行读取和分析根本就不可能完成,所以这种情况下在java.io包提供有一个RandomAccessFile类,这个类可以实现文件的跳跃式的读取,可以只读取文件部分内容(前提:需要有一个完善的保存形式,即数据的保存位数都要确定好。)
  RandomAccessFile类中定义有如下操作方法:

  • 构造方法:public RandomAccessFile(File file,String mode) throws FileNotFoundException;
    • 处理模式:r、rw

实现文件保存

import java.io.File;
import java.io.FileNotFoundException;
import java.io.RandomAccessFile;

public class Random_AccessFile {
    
    

	public static void main(String[] args) throws Exception {
    
    
		File file = new File("C:\\Project\\Java_study\\src\\文件\\test.txt");
		RandomAccessFile raf = new RandomAccessFile(file, "rw");
		String names[] = new String[] {
    
    "zhangsan","wangwu  ","lisi    "};
		int ages[] = new int[] {
    
    30,16,20};
		for(int x=0;x<names.length;x++) {
    
    
			raf.write(names[x].getBytes());
			raf.writeInt(ages[x]);
		}
		raf.close();
	}
}

  RandomAccessFile最大的特点是在于数据的读取处理上,因为所有的数据是按照固定长度进行保存的,所以读取时可以进行跳字节读取:

  • 向下跳:public int skipBytes(int n) throws IOException;
  • 向回跳:public void seek(long pos) throws IOException;

读取数据

import java.io.File;
import java.io.FileNotFoundException;
import java.io.RandomAccessFile;

public class Random_AccessFile {
    
    

	public static void main(String[] args) throws Exception {
    
    
		File file = new File("C:\\Project\\Java_study\\src\\文件\\test.txt");
		RandomAccessFile raf = new RandomAccessFile(file, "rw");
		//跳过24位
		{
    
    
		raf.skipBytes(24);
		byte data[] = new byte[8];
		int len = raf.read(data);
		System.out.println(new String(data,0,len) + " - " + raf.readInt());
		}
		//回
		{
    
    
		raf.seek(0);		//回到顶点
		byte data[] = new byte[8];
		int len = raf.read(data);
		System.out.println(new String(data,0,len) + " - " + raf.readInt());
		}
		raf.close();
	}
}

  整体使用中由用户自行定义要读取的位置,而后按照指定结构进行数据的读取。

猜你喜欢

转载自blog.csdn.net/MARVEL_3000/article/details/114435620
47
今日推荐