java 用二種方式, 追加寫入文件, 同時指定文件的編碼格式, 讀/寫線程併發操作同一文件

目前在網上很多人用 FilterWriter來寫文件, 但是 FilterWriter 不能指定編碼格式, 導致編碼問題,

有些人換成 BufferedWriter 來寫文件, 可以指定構建時的編碼,  但是又不知道怎麼追加到文件尾.

因此, 今天把本人工作中用到的代碼整理後歸納二式方式, 供大家參考.

同時以多線程同時讀/寫同一個文件, 邊寫,邊讀, 以下是完整代碼:

package com.test;

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.ByteArrayInputStream;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

/**
 * 指定編碼格式,並且以追加的方式寫入文件,並且線程同步讀取文件
 * @author guishuanglin 2019-09-20
 *
 */
public class ReadWriteFile {
	private Log logger = LogFactory.getLog(this.getClass());
	
	//線程是否在運行
	private static boolean isRuning = false;
	
	
	public static void main(String[] args) {
		ReadWriteFile ff = new ReadWriteFile();
		ff.runThread();
	}
	
	/**
	 * 運行處理線程,如果已經運行,則不再重複運行.
	 */
	private void runThread() {
		if(isRuning) return;
		isRuning = true;
		
		try {
			Thread.sleep(2000);
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
		new Thread(new writeThread()).start();	// 寫數據
		new Thread(new readThread()).start();	// 讀線程
	}
	
	/**
	 * 讀取文件線程
	 */
	private class readThread implements Runnable{
		@Override
		public void run() {
			String msg = null;
			FileInputStream in = null;
			BufferedReader bufReader = null;
			try {
				String pathFileName = "D:/test/test.txt";
				//File inf = new File(pathFileName);
				in = new FileInputStream(pathFileName);
				
				//以指定的編碼讀取文件
				bufReader = new BufferedReader(new InputStreamReader(in, "GBK"));
				
				for(int i=0; i< 30; i++){
					//此方法達到文件尾時, 返回 null
					msg = bufReader.readLine();
					if(msg != null) {
						logger.info("讀取文件 ->: "+ msg );
					}else {
						logger.info("讀取空行: ============" );
					}
					
					//測試時用,正常代碼去掉
					Thread.sleep(1000);
				}
				
			}catch (Exception e) {
				logger.error("讀取文件線程異常", e);
			} finally {
				try {
					if (in != null) {
						in.close();
					}
					in = null;
					if (bufReader != null) {
						bufReader.close();
					}
					bufReader = null;
				} catch (Exception e) {
					e.printStackTrace();
				}
				
			}
		}
	}
	
	/**
	 * 方式1: 追加文件, 指定編碼格式
	 */
	private class writeThread implements Runnable{
		@Override
		public void run() {
			String msg = null;
			FileOutputStream out = null;
			BufferedWriter bufWriter = null;
			try {
				String pathFileName = "D:/test/test.txt";
				//指定以追加方式寫文件, 不要用 FilterWriter追加文件,因爲FilterWriter不能指定編碼格式
				out = new FileOutputStream(pathFileName, true);
				//指定寫入內容編碼格式, 只有用OutputStreamWriter才能指定編碼格式, 這個是FilterWriter沒有的.
				bufWriter = new BufferedWriter(new OutputStreamWriter(out, "GBK"));
				
				msg =" 測試寫文件,方式1..........................";
				StringBuffer bf = new StringBuffer();
				String msg2 = null;
				
				for(int i=0; i< 20; i++){
					msg2 = i +", " + msg;
					bf.append(msg2 + "\r\n");
					
					bufWriter.write(bf.toString());
					bufWriter.flush();
					
					logger.info("寫入文件: " + msg2);
					bf = new StringBuffer(128);
					
					//測試時用,正常代碼去掉
					Thread.sleep(1000);
				}
				
			}catch (Exception e) {
				logger.error("寫入文件線程異常", e);
			} finally {
				try {
					if (bufWriter != null) {
						bufWriter.close();
					}
					bufWriter = null;
					
					if (out != null) {
						out.close();
					}
					out = null;
				} catch (Exception e) {
					e.printStackTrace();
				}
			}
		}
	}
	
	/**
	 * 
	 * 方式2: 追加文件, 指定編碼格式
	 * 此方法比較原始, 都是用byte寫入, 並且緩衝也可以自己控制,
	 * 此方式本人用了上10年, 性能可靠, 可對編碼格式隨意控制, 並且針對jdk各種新方式測試, 比原生jdk1.6緩存寫入方式性能要高.
	 */
	private class writeThread2 implements Runnable{
		@Override
		public void run() {
			String msg = null;
			FileOutputStream out = null;
			try {
				String pathFileName = "D:/test/test.txt";
				//以追加的方式寫入文件, 如果覆蓋文件則這樣寫: out = new FileOutputStream(pathFileName), 這樣就每次都會重寫文件
				out = new FileOutputStream(pathFileName, true);
				
				//寫入內容,這個僅爲測試方便,所以固定了,實際上應當從外部傳入List<String> 或者傳入StringBuffer
				msg =" 測試寫文件,方式2..........................";
				StringBuffer bf = new StringBuffer();
				String msg2 = null;
				
				byte[] bytes = new byte[20480];//20k,根據硬盤緩存大小調節,這個對性能很重要,可以根據服務器硬盤緩衝大小定製,以達到最佳寫入速度.一般爲硬盤緩存的1/4
				byte[] inbytes = null;
				for(int i=0; i< 20; i++){
					msg2 = i +", " + msg;
					bf.append(msg2+"\r\n");
					//對於達到最佳寫速度, 這個判斷很重要, 緩存適當, 儘量減少對硬盤的寫入(此爲測試,因此不考慮性能)
					if(bf.length() > 0) {
						//不管原內容是什麼格式, 拿到之後傳成指定格式的byte
						inbytes = bf.toString().getBytes("GBK");
						//臨時用字節輸入流
						ByteArrayInputStream in = new ByteArrayInputStream(inbytes);
						int c;
						while ((c = in.read(bytes)) != -1) {
							out.write(bytes, 0, c);
						}
						in.close();
						in = null;
						logger.info("寫入文件: " + msg2);
						bf = new StringBuffer(128);
					}
					//測試時用,正常代碼去掉
					Thread.sleep(1000);
				}
				//結束時清空變量,養成習慣.
				bytes =null;
				inbytes =null;
				bf = null;
				msg = null;
			}catch (Exception e) {
				logger.error("寫入文件線程異常", e);
			} finally {
				try {
					if (out != null) {
						out.close();
					}
					out = null;
				} catch (Exception e) {
					e.printStackTrace();
				}
			}
		}
	}
}

發佈之前, 以上代碼經過簡單的測試.

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章