JAVA读取HDFS的文件数据出现乱码的解决方案

 更新时间:2020年11月16日 10:11:39   作者:BuptWade  
这篇文章主要介绍了JAVA读取HDFS的文件数据出现乱码的解决方案,帮助大家更好的理解和使用Java,感兴趣的朋友可以了解下

使用JAVA api读取HDFS文件乱码踩坑

想写一个读取HFDS上的部分文件数据做预览的接口,根据网上的博客实现后,发现有时读取信息会出现乱码,例如读取一个csv时,字符串之间被逗号分割

  • 英文字符串aaa,能正常显示
  • 中文字符串“你好”,能正常显示
  • 中英混合字符串如“aaa你好”,出现乱码

查阅了众多博客,解决方案大概都是:使用xxx字符集解码。抱着不信的想法,我依次尝试,果然没用。

解决思路

因为HDFS支持6种字符集编码,每个本地文件编码方式又是极可能不一样的,我们上传本地文件的时候其实就是把文件编码成字节流上传到文件系统存储。那么在GET文件数据时,面对不同文件、不同字符集编码的字节流,肯定不是一种固定字符集解码就能正确解码的吧。

那么解决方案其实有两种

  • 固定HDFS的编解码字符集。比如我选用UTF-8,那么在上传文件时统一编码,即把不同文件的字节流都转化为UTF-8编码再进行存储。这样的话在获取文件数据的时候,采用UTF-8字符集解码就没什么问题了。但这样做的话仍然会在转码部分存在诸多问题,且不好实现。
  • 动态解码。根据文件的编码字符集选用对应的字符集对解码,这样的话并不会对文件的原生字符流进行改动,基本不会乱码。

我选用动态解码的思路后,其难点在于如何判断使用哪种字符集解码。参考下面的内容,获得了解决方案

java检测文本(字节流)的编码方式

需求:

某文件或者某字节流要检测他的编码格式。

实现:

基于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提供了检测字节流编码方式的包。那么方案就很明了了,先读一些文件字节流,用工具检测编码方式,再对应进行解码即可。

具体解决代码

pom

<dependency>
	<groupId>net.sourceforge.jchardet</groupId>
	<artifactId>jchardet</artifactId>
	<version>1.0</version>
</dependency>

从HDFS读取部分文件做预览的逻辑

 // 获取文件的部分数据做预览
 public List<String> getFileDataWithLimitLines(String filePath, Integer limit) {
  FSDataInputStream fileStream = openFile(filePath);
  return readFileWithLimit(fileStream, limit);
 }

 // 获取文件的数据流
 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;
 }
 
 // 读取最多limit行文件数据
 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());
 }

 // 从文件数据流中读取字节流
 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();
 }

 // 解码字节流
 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;
 }

 // 根据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的文件数据出现乱码的解决方案的详细内容,更多关于JAVA读取HDFS的文件乱码的资料请关注脚本之家其它相关文章!

相关文章

  • Java算法实战之排一亿个随机数

    Java算法实战之排一亿个随机数

    我们在生活中经常遇见一些这样的需求,随机点名、公司年会抽奖、微信拼手气红包等,还有一些游戏比如打地鼠小游戏、俄罗斯方块等,这些场景中都会用到一种算法:随机,这篇文章主要给大家介绍了关于Java算法实战之排一亿个随机数的相关资料,需要的朋友可以参考下
    2021-11-11
  • Java BigDecimal类用法详解

    Java BigDecimal类用法详解

    本文详细讲解了Java BigDecimal类的用法,文中通过示例代码介绍的非常详细。对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2021-12-12
  • Springboot启动执行特定代码的方式汇总

    Springboot启动执行特定代码的方式汇总

    这篇文章主要介绍了Springboot启动执行特定代码的几种方式,本文通过实例代码给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
    2021-12-12
  • maven继承父工程统一版本号的实现

    maven继承父工程统一版本号的实现

    这篇文章主要介绍了maven继承父工程统一版本号的实现,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2020-08-08
  • Spring常用注解汇总

    Spring常用注解汇总

    这篇文章主要介绍了Spring常用注解汇总,需要的朋友可以参考下
    2014-08-08
  • Java使用TarsosDSP库实现音频的处理和格式转换

    Java使用TarsosDSP库实现音频的处理和格式转换

    在音频处理领域,Java虽然有原生的音频处理类库,但其功能相对基础,而TarsosDSP是一个强大的开源音频处理库,提供了丰富的功能,本文将介绍如何在Java中结合使用TarsosDSP库,来实现音频的处理和格式转换,需要的朋友可以参考下
    2025-04-04
  • Java使用Collections.sort()排序的示例详解

    Java使用Collections.sort()排序的示例详解

    这篇文章主要介绍了Java使用Collections.sort()排序的示例详解,Collections.sort(list, new PriceComparator());的第二个参数返回一个int型的值,就相当于一个标志,告诉sort方法按什么顺序来对list进行排序。对此感兴趣的可以了解一下
    2020-07-07
  • Java中String类getBytes()方法详解与完整实例

    Java中String类getBytes()方法详解与完整实例

    这篇文章主要给大家介绍了关于Java中String类getBytes()方法详解与完整实例的相关资料,getBytes()是Java编程语言中将一个字符串转化为一个字节数组byte[]的方法,需要的朋友可以参考下
    2023-10-10
  • java面向对象编程重要概念继承和多态示例解析

    java面向对象编程重要概念继承和多态示例解析

    这篇文章主要为大家介绍了java面向对象编程的两个重要概念继承和多态示例解析,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2023-05-05
  • Java中继承thread类与实现Runnable接口的比较

    Java中继承thread类与实现Runnable接口的比较

    这篇文章主要介绍了Java中继承thread类与实现Runnable接口的比较的相关资料,需要的朋友可以参考下
    2017-06-06

最新评论