目前在網上很多人用 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();
}
}
}
}
}
發佈之前, 以上代碼經過簡單的測試.