使用JAVA API读取HDFS的文件数据出现乱码的解决方案
使用JAVA api读取HDFS文件乱码踩坑
想写一个读取HFDS上的部分文件数据做预览的接口,根据网上的博客实现后,发现有时读取信息会出现乱码,例如读取一个csv时,字符串之间被逗号分割
英文字符串aaa,能正常显示
中文字符串“你好”,能正常显示
中英混合字符串如“aaa你好”,出现乱码
查阅了众多博客,解决方案大概都是:使用xxx字符集解码。抱着不信的想法,我依次尝试,果然没用。
解决思路
因为HDFS支持6种字符集编码,每个本地文件编码方式又是极可能不一样的,我们上传本地文件的时候其实就是把文件编码成字节流上传到文件系统存储。那么在GET文件数据时,面对不同文件、不同字符集编码的字节流,肯定不是一种固定字符集解码就能正确解码的吧。
那么解决方案其实有两种
固定HDFS的编解码字符集。比如我选用UTF-8,那么在上传文件时统一编码,即把不同文件的字节流都转化为UTF-8编码再进行存储。这样的话在获取文件数据的时候,采用UTF-8字符集解码就没什么问题了。但这样做的话仍然会在转码部分存在诸多问题,且不好实现。
动态解码。根据文件的编码字符集选用对应的字符集对解码,这样的话并不会对文件的原生字符流进行改动,基本不会乱码。
我选用动态解码的思路后,其难点在于如何判断使用哪种字符集解码。
好在看到了一篇博客
https://blog.csdn.net/smallnetvisitor/article/details/84682867
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 API读取HDFS的文件数据出现乱码的解决方案的更多相关文章
- Java API 读取HDFS的单文件
HDFS上的单文件: -bash-3.2$ hadoop fs -ls /user/pms/ouyangyewei/data/input/combineorder/repeat_rec_categor ...
- java Api 读取HDFS文件内容
package dao; import org.apache.hadoop.conf.Configuration; import org.apache.hadoop.fs.*; import java ...
- Spark:java api读取hdfs目录下多个文件
需求: 由于一个大文件,在spark中加载性能比较差.于是把一个大文件拆分为多个小文件后上传到hdfs,然而在spark2.2下如何加载某个目录下多个文件呢? public class SparkJo ...
- 用java api读取HDFS文件
import java.io.IOException; import java.io.InputStream; import java.security.PrivilegedExceptionActi ...
- JAVA API 实现hdfs文件操作
java api 实现hdfs 文件操作会出现错误提示: Permission denied: user=hp, access=WRITE, inode="/":hdfs:supe ...
- 分享非常有用的Java程序(关键代码)(八)---Java InputStream读取网络响应Response数据的方法!(重要)
原文:分享非常有用的Java程序(关键代码)(八)---Java InputStream读取网络响应Response数据的方法!(重要) Java InputStream读取数据问题 ======== ...
- 使用Java API操作HDFS文件系统
使用Junit封装HFDS import org.apache.hadoop.conf.Configuration; import org.apache.hadoop.fs.*; import org ...
- 使用Java Api 操作HDFS
如题 我就是一个标题党 就是使用JavaApi操作HDFS,使用的是MAVEN,操作的环境是Linux 首先要配置好Maven环境,我使用的是已经有的仓库,如果你下载的jar包 速度慢,可以改变Ma ...
- C#选择多个文件并读取多个文件数据
原文:C#选择多个文件并读取多个文件数据 版权声明:本文为博主原创文章,转载请附上链接地址. https://blog.csdn.net/ld15102891672/article/details/8 ...
随机推荐
- 从字节码层次看i++和++i
关于的Java的i++和++i的区别,初学者可能会混淆,这时候有经验的同学或同事就会告诉你,++在后,就会立马加值, ++在后则会等会儿再加,所以如果i == 0 ,那么i++ == 0,++i == ...
- java安全编码指南之:输入注入injection
目录 简介 SQL注入 java中的SQL注入 使用PreparedStatement XML中的SQL注入 XML注入的java代码 简介 注入问题是安全中一个非常常见的问题,今天我们来探讨一下ja ...
- 每日一题 LeetCode 679. 24点游戏 【递归】【全排列】
题目链接 https://leetcode-cn.com/problems/24-game/ 题目说明 题解 主要方法:递归 + 全排列 解释说明: 将 4 个数进行组合形成算式,发现除了 (a❈b) ...
- 多测师讲解python_003.2练习题
# 1.分别打印100以内的所有偶数和奇数并存入不同的列表当中# 2.请写一段Python代码实现删除一个list = [1, 3, 6, 9, 1, 8]# 里面的重复元素不能用set# 3.将字符 ...
- day57 Pyhton 前端Jquery09
内容回顾: - 筛选选择器 $('li:eq(1)') 查找匹配的元素 $('li:first') $('li:last') - 属性选择器 - 筛选的方法 - find() 查找后代的元素 - ...
- 【C语言学习笔记】C语言函数执行成功时,返回1和返回0,究竟哪个好?
基本上,没有人会将大段的C语言代码全部塞入 main() 函数,更好的做法是按照复用率高,耦合性低的原则,尽可能的将代码拆分不同的功能模块,并封装成函数.C语言代码的组合千变万化,因此函数的功能可能会 ...
- go 爬取页面保存
package main import ( "bufio" "fmt" "io/ioutil" "net/http" & ...
- faker切换user-agent
import random import requests url = "http://tool.yeves.cn" import faker fake = faker.Faker ...
- 跟我一起学Redis之看完这篇比常人多会三种类型实战(又搞了几个小时)
前言 对于Redis而言,很多小伙伴只关注其关键的五大基础类型:string.hash.list.set.sorted set(有序集合),其实还有三种特殊类型在很多应用场景也比较适合使用,分别是:b ...
- java的回收机制
在java语言中,判断一块内存空间是否符合垃圾收集器收集标准的标准只有两个: 1.给对象赋值为null,以下没有调用过. 2.给对象赋了新的值,重新分配了内存空间.