JAVA讀取HDFS的文件數(shù)據(jù)出現(xiàn)亂碼的解決方案
使用JAVA api讀取HDFS文件亂碼踩坑
想寫一個讀取HFDS上的部分文件數(shù)據(jù)做預覽的接口,根據(jù)網(wǎng)上的博客實現(xiàn)后,發(fā)現(xiàn)有時讀取信息會出現(xiàn)亂碼,例如讀取一個csv時,字符串之間被逗號分割
- 英文字符串a(chǎn)aa,能正常顯示
- 中文字符串“你好”,能正常顯示
- 中英混合字符串如“aaa你好”,出現(xiàn)亂碼
查閱了眾多博客,解決方案大概都是:使用xxx字符集解碼。抱著不信的想法,我依次嘗試,果然沒用。
解決思路
因為HDFS支持6種字符集編碼,每個本地文件編碼方式又是極可能不一樣的,我們上傳本地文件的時候其實就是把文件編碼成字節(jié)流上傳到文件系統(tǒng)存儲。那么在GET文件數(shù)據(jù)時,面對不同文件、不同字符集編碼的字節(jié)流,肯定不是一種固定字符集解碼就能正確解碼的吧。
那么解決方案其實有兩種
- 固定HDFS的編解碼字符集。比如我選用UTF-8,那么在上傳文件時統(tǒng)一編碼,即把不同文件的字節(jié)流都轉(zhuǎn)化為UTF-8編碼再進行存儲。這樣的話在獲取文件數(shù)據(jù)的時候,采用UTF-8字符集解碼就沒什么問題了。但這樣做的話仍然會在轉(zhuǎn)碼部分存在諸多問題,且不好實現(xiàn)。
- 動態(tài)解碼。根據(jù)文件的編碼字符集選用對應的字符集對解碼,這樣的話并不會對文件的原生字符流進行改動,基本不會亂碼。
我選用動態(tài)解碼的思路后,其難點在于如何判斷使用哪種字符集解碼。參考下面的內(nèi)容,獲得了解決方案
java檢測文本(字節(jié)流)的編碼方式
需求:
某文件或者某字節(jié)流要檢測他的編碼格式。
實現(xiàn):
基于jchardet
<dependency> <groupId>net.sourceforge.jchardet</groupId> <artifactId>jchardet</artifactId> <version>1.0</version> </dependency>
代碼如下:
public class DetectorUtils {
private DetectorUtils() {
}
static class ChineseCharsetDetectionObserver implements
nsICharsetDetectionObserver {
private boolean found = false;
private String result;
public void Notify(String charset) {
found = true;
result = charset;
}
public ChineseCharsetDetectionObserver(boolean found, String result) {
super();
this.found = found;
this.result = result;
}
public boolean isFound() {
return found;
}
public String getResult() {
return result;
}
}
public static String[] detectChineseCharset(InputStream in)
throws Exception {
String[] prob=null;
BufferedInputStream imp = null;
try {
boolean found = false;
String result = Charsets.UTF_8.toString();
int lang = nsPSMDetector.CHINESE;
nsDetector det = new nsDetector(lang);
ChineseCharsetDetectionObserver detectionObserver = new ChineseCharsetDetectionObserver(
found, result);
det.Init(detectionObserver);
imp = new BufferedInputStream(in);
byte[] buf = new byte[1024];
int len;
boolean isAscii = true;
while ((len = imp.read(buf, 0, buf.length)) != -1) {
if (isAscii)
isAscii = det.isAscii(buf, len);
if (!isAscii) {
if (det.DoIt(buf, len, false))
break;
}
}
det.DataEnd();
boolean isFound = detectionObserver.isFound();
if (isAscii) {
isFound = true;
prob = new String[] { "ASCII" };
} else if (isFound) {
prob = new String[] { detectionObserver.getResult() };
} else {
prob = det.getProbableCharsets();
}
return prob;
} finally {
IOUtils.closeQuietly(imp);
IOUtils.closeQuietly(in);
}
}
}
測試:
String file = "C:/3737001.xml";
String[] probableSet = DetectorUtils.detectChineseCharset(new FileInputStream(file));
for (String charset : probableSet) {
System.out.println(charset);
}
Google提供了檢測字節(jié)流編碼方式的包。那么方案就很明了了,先讀一些文件字節(jié)流,用工具檢測編碼方式,再對應進行解碼即可。
具體解決代碼
pom
<dependency> <groupId>net.sourceforge.jchardet</groupId> <artifactId>jchardet</artifactId> <version>1.0</version> </dependency>
從HDFS讀取部分文件做預覽的邏輯
// 獲取文件的部分數(shù)據(jù)做預覽
public List<String> getFileDataWithLimitLines(String filePath, Integer limit) {
FSDataInputStream fileStream = openFile(filePath);
return readFileWithLimit(fileStream, limit);
}
// 獲取文件的數(shù)據(jù)流
private FSDataInputStream openFile(String filePath) {
FSDataInputStream fileStream = null;
try {
fileStream = fs.open(new Path(getHdfsPath(filePath)));
} catch (IOException e) {
logger.error("fail to open file:{}", filePath, e);
}
return fileStream;
}
// 讀取最多l(xiāng)imit行文件數(shù)據(jù)
private List<String> readFileWithLimit(FSDataInputStream fileStream, Integer limit) {
byte[] bytes = readByteStream(fileStream);
String data = decodeByteStream(bytes);
if (data == null) {
return null;
}
List<String> rows = Arrays.asList(data.split("\\r\\n"));
return rows.stream().filter(StringUtils::isNotEmpty)
.limit(limit)
.collect(Collectors.toList());
}
// 從文件數(shù)據(jù)流中讀取字節(jié)流
private byte[] readByteStream(FSDataInputStream fileStream) {
byte[] bytes = new byte[1024*30];
int len;
ByteArrayOutputStream stream = new ByteArrayOutputStream();
try {
while ((len = fileStream.read(bytes)) != -1) {
stream.write(bytes, 0, len);
}
} catch (IOException e) {
logger.error("read file bytes stream failed.", e);
return null;
}
return stream.toByteArray();
}
// 解碼字節(jié)流
private String decodeByteStream(byte[] bytes) {
if (bytes == null) {
return null;
}
String encoding = guessEncoding(bytes);
String data = null;
try {
data = new String(bytes, encoding);
} catch (Exception e) {
logger.error("decode byte stream failed.", e);
}
return data;
}
// 根據(jù)Google的工具判別編碼
private String guessEncoding(byte[] bytes) {
UniversalDetector detector = new UniversalDetector(null);
detector.handleData(bytes, 0, bytes.length);
detector.dataEnd();
String encoding = detector.getDetectedCharset();
detector.reset();
if (StringUtils.isEmpty(encoding)) {
encoding = "UTF-8";
}
return encoding;
}
以上就是JAVA讀取HDFS的文件數(shù)據(jù)出現(xiàn)亂碼的解決方案的詳細內(nèi)容,更多關(guān)于JAVA讀取HDFS的文件亂碼的資料請關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
java利用htmlparser獲取html中想要的代碼具體實現(xiàn)
這篇文章主要介紹了java利用htmlparser獲取html中想要的代碼具體實現(xiàn),需要的朋友可以參考下2014-02-02
解決spring boot網(wǎng)關(guān)gateway導致的坑,無法下載文件問題
這篇文章主要介紹了解決spring boot網(wǎng)關(guān)gateway導致的坑,無法下載文件的問題,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2021-07-07
Javaweb請求轉(zhuǎn)發(fā)及重定向?qū)崿F(xiàn)詳解
這篇文章主要介紹了Javaweb請求轉(zhuǎn)發(fā)及重定向?qū)崿F(xiàn)詳解,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友可以參考下2020-07-07

