您好,登錄后才能下訂單哦!
利用JAVA讀取HDFS文件數(shù)據(jù)時(shí)出現(xiàn)亂碼如何解決?針對(duì)這個(gè)問(wèn)題,這篇文章詳細(xì)介紹了相對(duì)應(yīng)的分析和解答,希望可以幫助更多想解決這個(gè)問(wèn)題的小伙伴找到更簡(jiǎn)單易行的方法。
使用JAVA api讀取HDFS文件亂碼踩坑
想寫(xiě)一個(gè)讀取HFDS上的部分文件數(shù)據(jù)做預(yù)覽的接口,根據(jù)網(wǎng)上的博客實(shí)現(xiàn)后,發(fā)現(xiàn)有時(shí)讀取信息會(huì)出現(xiàn)亂碼,例如讀取一個(gè)csv時(shí),字符串之間被逗號(hào)分割
查閱了眾多博客,解決方案大概都是:使用xxx字符集解碼。抱著不信的想法,我依次嘗試,果然沒(méi)用。
解決思路
因?yàn)镠DFS支持6種字符集編碼,每個(gè)本地文件編碼方式又是極可能不一樣的,我們上傳本地文件的時(shí)候其實(shí)就是把文件編碼成字節(jié)流上傳到文件系統(tǒng)存儲(chǔ)。那么在GET文件數(shù)據(jù)時(shí),面對(duì)不同文件、不同字符集編碼的字節(jié)流,肯定不是一種固定字符集解碼就能正確解碼的吧。
那么解決方案其實(shí)有兩種
我選用動(dòng)態(tài)解碼的思路后,其難點(diǎn)在于如何判斷使用哪種字符集解碼。參考下面的內(nèi)容,獲得了解決方案
java檢測(cè)文本(字節(jié)流)的編碼方式
需求:
某文件或者某字節(jié)流要檢測(cè)他的編碼格式。
實(shí)現(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); } } }
測(cè)試:
String file = "C:/3737001.xml"; String[] probableSet = DetectorUtils.detectChineseCharset(new FileInputStream(file)); for (String charset : probableSet) { System.out.println(charset); }
Google提供了檢測(cè)字節(jié)流編碼方式的包。那么方案就很明了了,先讀一些文件字節(jié)流,用工具檢測(cè)編碼方式,再對(duì)應(yīng)進(jìn)行解碼即可。
具體解決代碼
pom
<dependency> <groupId>net.sourceforge.jchardet</groupId> <artifactId>jchardet</artifactId> <version>1.0</version> </dependency>
從HDFS讀取部分文件做預(yù)覽的邏輯
// 獲取文件的部分?jǐn)?shù)據(jù)做預(yù)覽 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; }
關(guān)于利用JAVA讀取HDFS文件數(shù)據(jù)時(shí)出現(xiàn)亂碼如何解決問(wèn)題的解答就分享到這里了,希望以上內(nèi)容可以對(duì)大家有一定的幫助,如果你還有很多疑惑沒(méi)有解開(kāi),可以關(guān)注億速云行業(yè)資訊頻道了解更多相關(guān)知識(shí)。
免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀(guān)點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。