Java讀取HDFS的文件數(shù)據(jù)出現(xiàn)亂碼的方案

使用JAVA api讀取HDFS文件亂碼踩坑

想寫一個(gè)讀取HFDS上的部分文件數(shù)據(jù)做預(yù)覽的接口撤缴,根據(jù)網(wǎng)上的博客實(shí)現(xiàn)后,發(fā)現(xiàn)有時(shí)讀取信息會(huì)出現(xiàn)亂碼幻工,例如讀取一個(gè)csv時(shí)巫延,字符串之間被逗號(hào)分割

英文字符串a(chǎn)aa效五,能正常顯示

中文字符串“你好”,能正常顯示

中英混合字符串如“aaa你好”炉峰,出現(xiàn)亂碼

查閱了眾多博客畏妖,解決方案大概都是:使用xxx字符集解碼。抱著不信的想法疼阔,我依次嘗試戒劫,果然沒用。

解決思路

因?yàn)镠DFS支持6種字符集編碼婆廊,每個(gè)本地文件編碼方式又是極可能不一樣的迅细,我們上傳本地文件的時(shí)候其實(shí)就是把文件編碼成字節(jié)流上傳到文件系統(tǒng)存儲(chǔ)。那么在GET文件數(shù)據(jù)時(shí)淘邻,面對(duì)不同文件茵典、不同字符集編碼的字節(jié)流,肯定不是一種固定字符集解碼就能正確解碼的吧宾舅。

那么解決方案其實(shí)有兩種

固定HDFS的編解碼字符集统阿。比如我選用UTF-8,那么在上傳文件時(shí)統(tǒng)一編碼筹我,即把不同文件的字節(jié)流都轉(zhuǎn)化為UTF-8編碼再進(jìn)行存儲(chǔ)扶平。這樣的話在獲取文件數(shù)據(jù)的時(shí)候,采用UTF-8字符集解碼就沒什么問題了蔬蕊。但這樣做的話仍然會(huì)在轉(zhuǎn)碼部分存在諸多問題结澄,且不好實(shí)現(xiàn)。

動(dòng)態(tài)解碼袁串。根據(jù)文件的編碼字符集選用對(duì)應(yīng)的字符集對(duì)解碼概而,這樣的話并不會(huì)對(duì)文件的原生字符流進(jìn)行改動(dòng),基本不會(huì)亂碼囱修。

我選用動(dòng)態(tài)解碼的思路后,其難點(diǎn)在于如何判斷使用哪種字符集解碼王悍。參考下面的內(nèi)容破镰,獲得了解決方案

java檢測(cè)文本(字節(jié)流)的編碼方式

需求:

某文件或者某字節(jié)流要檢測(cè)他的編碼格式。

實(shí)現(xiàn):

基于jchardetdependency>

????<groupId>net.sourceforge.jchardet</groupId>

????<artifactId>jchardet</artifactId>

????<version>1.0</version>

</dependency>

代碼如下:

publicclassDetectorUtils {

????privateDetectorUtils() {

????}


????staticclassChineseCharsetDetectionObserver implements

????????????nsICharsetDetectionObserver {

????????privatebooleanfound = false;

????????privateString result;


????????publicvoidNotify(String charset) {

????????????found = true;

????????????result = charset;

????????}


????????publicChineseCharsetDetectionObserver(booleanfound, String result) {

????????????super();

????????????this.found = found;

????????????this.result = result;

????????}


????????publicbooleanisFound() {

????????????returnfound;

????????}


????????publicString getResult() {

????????????returnresult;

????????}


????}


????publicstaticString[] detectChineseCharset(InputStream in)

????????????throwsException {

????????String[] prob=null;

????????BufferedInputStream imp = null;

????????try{

????????????booleanfound = false;

????????????String result = Charsets.UTF_8.toString();

????????????intlang = nsPSMDetector.CHINESE;

????????????nsDetector det = newnsDetector(lang);

????????????ChineseCharsetDetectionObserver detectionObserver = newChineseCharsetDetectionObserver(

????????????????????found, result);

????????????det.Init(detectionObserver);

????????????imp = newBufferedInputStream(in);

????????????byte[] buf = newbyte[1024];

????????????intlen;

????????????booleanisAscii = 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();

????????????booleanisFound = detectionObserver.isFound();

????????????if(isAscii) {

????????????????isFound = true;

????????????????prob = newString[] { "ASCII"};

????????????} elseif(isFound) {

????????????????prob = newString[] { detectionObserver.getResult() };

????????????} else{

????????????????prob = det.getProbableCharsets();

????????????}

????????????returnprob;

????????} finally{

????????????IOUtils.closeQuietly(imp);

????????????IOUtils.closeQuietly(in);

????????}

????}

測(cè)試:?

String file = "C:/3737001.xml";

String[] probableSet = DetectorUtils.detectChineseCharset(newFileInputStream(file));

for(String charset : probableSet) {

????System.out.println(charset);

}

從HDFS讀取部分文件做預(yù)覽的邏輯

// 獲取文件的部分?jǐn)?shù)據(jù)做預(yù)覽

publicList<String> getFileDataWithLimitLines(String filePath, Integer limit) {

?FSDataInputStream fileStream = openFile(filePath);

?returnreadFileWithLimit(fileStream, limit);

}


// 獲取文件的數(shù)據(jù)流

privateFSDataInputStream openFile(String filePath) {

?FSDataInputStream fileStream = null;

?try{

??fileStream = fs.open(newPath(getHdfsPath(filePath)));

?} catch(IOException e) {

??logger.error("fail to open file:{}", filePath, e);

?}

?returnfileStream;

}


// 讀取最多l(xiāng)imit行文件數(shù)據(jù)

privateList<String> readFileWithLimit(FSDataInputStream fileStream, Integer limit) {

?byte[] bytes = readByteStream(fileStream);

?String data = decodeByteStream(bytes);

?if(data == null) {

??returnnull;

?}


?List<String> rows = Arrays.asList(data.split("\\r\\n"));

?returnrows.stream().filter(StringUtils::isNotEmpty)

???.limit(limit)

???.collect(Collectors.toList());

}


// 從文件數(shù)據(jù)流中讀取字節(jié)流

privatebyte[] readByteStream(FSDataInputStream fileStream) {

?byte[] bytes = newbyte[1024*30];

?intlen;

?ByteArrayOutputStream stream = newByteArrayOutputStream();

?try{

??while((len = fileStream.read(bytes)) != -1) {

???stream.write(bytes, 0, len);

??}

?} catch(IOException e) {

??logger.error("read file bytes stream failed.", e);

??returnnull;

?}

?returnstream.toByteArray();

}


// 解碼字節(jié)流

privateString decodeByteStream(byte[] bytes) {

?if(bytes == null) {

??returnnull;

?}


?String encoding = guessEncoding(bytes);

?String data = null;

?try{

??data = newString(bytes, encoding);

?} catch(Exception e) {

??logger.error("decode byte stream failed.", e);

?}

?returndata;

}


// 根據(jù)Google的工具判別編碼

privateString guessEncoding(byte[] bytes) {

?UniversalDetector detector = newUniversalDetector(null);

?detector.handleData(bytes, 0, bytes.length);

?detector.dataEnd();

?String encoding = detector.getDetectedCharset();

?detector.reset();


?if(StringUtils.isEmpty(encoding)) {

??encoding = "UTF-8";

?}

?returnencoding;

}

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市鲜漩,隨后出現(xiàn)的幾起案子源譬,更是在濱河造成了極大的恐慌,老刑警劉巖孕似,帶你破解...
    沈念sama閱讀 222,946評(píng)論 6 518
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件踩娘,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡喉祭,警方通過查閱死者的電腦和手機(jī)养渴,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 95,336評(píng)論 3 399
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來泛烙,“玉大人理卑,你說我怎么就攤上這事”伟保” “怎么了藐唠?”我有些...
    開封第一講書人閱讀 169,716評(píng)論 0 364
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)鹉究。 經(jīng)常有香客問我宇立,道長(zhǎng),這世上最難降的妖魔是什么自赔? 我笑而不...
    開封第一講書人閱讀 60,222評(píng)論 1 300
  • 正文 為了忘掉前任妈嘹,我火速辦了婚禮,結(jié)果婚禮上匿级,老公的妹妹穿的比我還像新娘蟋滴。我一直安慰自己,他們只是感情好痘绎,可當(dāng)我...
    茶點(diǎn)故事閱讀 69,223評(píng)論 6 398
  • 文/花漫 我一把揭開白布津函。 她就那樣靜靜地躺著,像睡著了一般孤页。 火紅的嫁衣襯著肌膚如雪苫费。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 52,807評(píng)論 1 314
  • 那天左驾,我揣著相機(jī)與錄音匹层,去河邊找鬼。 笑死蛾号,一個(gè)胖子當(dāng)著我的面吹牛稠项,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播鲜结,決...
    沈念sama閱讀 41,235評(píng)論 3 424
  • 文/蒼蘭香墨 我猛地睜開眼展运,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼活逆!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起拗胜,我...
    開封第一講書人閱讀 40,189評(píng)論 0 277
  • 序言:老撾萬榮一對(duì)情侶失蹤蔗候,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后埂软,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體锈遥,經(jīng)...
    沈念sama閱讀 46,712評(píng)論 1 320
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,775評(píng)論 3 343
  • 正文 我和宋清朗相戀三年勘畔,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了所灸。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 40,926評(píng)論 1 353
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡咖杂,死狀恐怖庆寺,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情诉字,我是刑警寧澤懦尝,帶...
    沈念sama閱讀 36,580評(píng)論 5 351
  • 正文 年R本政府宣布,位于F島的核電站壤圃,受9級(jí)特大地震影響陵霉,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜伍绳,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 42,259評(píng)論 3 336
  • 文/蒙蒙 一踊挠、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧冲杀,春花似錦效床、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,750評(píng)論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至旺芽,卻和暖如春沪猴,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背采章。 一陣腳步聲響...
    開封第一講書人閱讀 33,867評(píng)論 1 274
  • 我被黑心中介騙來泰國(guó)打工运嗜, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人悯舟。 一個(gè)月前我還...
    沈念sama閱讀 49,368評(píng)論 3 379
  • 正文 我出身青樓担租,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親抵怎。 傳聞我的和親對(duì)象是個(gè)殘疾皇子翩活,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,930評(píng)論 2 361

推薦閱讀更多精彩內(nèi)容