Java中常见的编码集问题
收录于热门专栏Java基础教程系列(进阶篇)
一、遇到一个问题
1、读取CSV文件
package com.guor.demo.charset;import java.io.BufferedReader;
import java.io.FileReader;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;public class CommonUtils {public static List<Map<String, Object>> readCSVToList(String filePath) throws Exception {List<Map<String, Object>> list = new ArrayList<Map<String, Object>>();BufferedReader reader = null;try {reader = new BufferedReader(new FileReader(filePath));String[] headtilte = reader.readLine().split(",");String line = null;while ((line = reader.readLine()) != null) {HashMap<String, Object> hashMap = new HashMap<String, Object>();String[] itemArray = line.split(",");for (int i = 0; i < itemArray.length; i++) {hashMap.put(headtilte[i], itemArray[i]);}list.add(hashMap);}} catch (Exception e) {e.printStackTrace();} finally {if (null != reader) {reader.close();}}return list;}public static void main(String[] args) throws Exception {String filePath = "H:\\CSDN\\netty\\编码集问题\\test.csv";System.out.println(readCSVToList(filePath));}
}
2、控制台输出
这是什么鬼?
原来我的csv文件的编码集是带BOM的UTF-8,没听过啊,众里寻他千百度。
二、带有BOM的UTF-8
1、BOM
在UCS 编码中有一个叫做 “Zero Width No-Break Space” ,中文译名作“零宽无间断间隔”的字符,它的编码是 FEFF。而 FEFF 在 UCS 中是不存在的字符,所以不应该出现在实际传输中。UCS 规范建议我们在传输字节流前,先传输字符 “Zero Width No-Break Space”。这样如果接收者收到 FEFF,就表明这个字节流是 Big-Endian 的;如果收到FFFE,就表明这个字节流是 Little- Endian 的。因此字符 “Zero Width No-Break Space” (“零宽无间断间隔”)又被称作 BOM。
2、UTF-8
UTF-8(8位元,Universal Character Set/Unicode Transformation Format)是针对Unicode的一种可变长度字符编码。它可以用来表示Unicode标准中的任何字符,而且其编码中的第一个字节仍与ASCII相容,使得原来处理ASCII字符的软件无须或只进行少部分修改后,便可继续使用。因此,它逐渐成为电子邮件、网页及其他存储或传送文字的应用中,优先采用的编码。
3、UTF-8 BOM
UTF-8 不需要 BOM 来表明字节顺序,但可以用 BOM 来表明编码方式。字符 “Zero Width No-Break Space” 的 UTF-8 编码是 EF BB BF。所以如果接收者收到以 EF BB BF 开头的字节流,就知道这是 UTF-8编码了。Windows 就是使用 BOM 来标记文本文件的编码方式的。
4、CSV文件乱码问题
类似WINDOWS自带的记事本等软件,在保存一个以UTF-8编码的文件时,会在文件开始的地方插入UTF-8 BOM头。记事本等编辑器通过它来识别这个文件是否以UTF-8编码(当然即便没有UTF-8 BOM头记事本也能通过其它方式正确识别UTF-8编码)。
三、编码解码
在计算机发展初期,美国等少数国家最先给自己的语言设置了一套编码,即ASCII。因为英语只有26个英文字母及一些常见的符号即可,因此只需要一个字节的 7 位(即 128 个整数)就能完全表示英文字符。
但随着计算机的发展,欧洲一些国家也需要为自己的语言设置一套编码,ASCII的128个字符不够用了,因此就产生了第二套编码类型 ISO-8859-1 ~ ISO-8859-15,其中使用最广泛的是ISO-8859-1,ISO-8859-1使用了一个字节的 8 位,可以表示256个字符。为了避免乱码问题,ISO-8859-1完全兼容ASCII,ISO-8859-1的前128位和ASCII完全一致,后128个字符才是ISO-8859-1自身新扩展的字符编码。
后来,中国也给汉语设置了一套编码,提出了适合汉语的编码集GB2312。GB2312包含了682个英文、字母等符号及常见的6763个简体中文,我勒个去,中华上下五千年,博大精深啊。
再后来,为了将简体中文和繁体中文兼容到字符集里,又发布了新的编码集GBK。GBK实际是GB2312的扩展,使用1个字节存储ASCII中的字符,使用2个字节存储一个中文汉字。
最后,为了给世界上的所有字符设置一套统一的编码集,出台了统一的字符集规范Unicode(国际标准字符集)。其中就包含最著名的UTF-8。
UTF-8是ASCII的超集,ASCII中每个字符的编码与UTF-8是完全一致的,因此当用UTF-8存储汉字或其他字符时,可能会使用2个、3个或4个字节。
- 1个字节:英文,数字,回车符,ASCII中的常用符号+、-、*、/等;
- 2个字节:个别特殊符号;
- 3个字节:常见汉字,在GBK中存在的汉字;
- 4个字节:中日韩等超大字符集里面的汉字;
在java.nio.charset.Charset类中,提供了一些编码及常用方法。
四、解决读取“带有BOM的UTF-8文件乱码”问题
1、读取文件编码集
按照给定的字符集存储文件时,在文件的最开头的三个字节中就有可能存储着编码信息,所以,基本的原理就是只要读出文件前三个字节,判定这些字节的值,就可以得知其编码的格式。其实,如果项目运行的平台就是中文操作系统,如果这些文本文件在项目内产生,即开发人员可以控制文本的编码格式。
2、用指定的编码格式读取文件流,写入文件流
(1)读取CSV文件
public static List<String[]> readCSVToListByUnicode(String filePath) {FileInputStream fis = null;UnicodeInputStream uin = null;InputStreamReader in= null;try {//logger.info("读取文件"+filePath+"编码集");List<String[]> list = new ArrayList<String[]>();//URL url = new URL(filePath);File f = new File(filePath);fis = new FileInputStream(f);uin = new UnicodeInputStream(fis,enc); //如果是本地将url.openStream -> new FileInputStream(f)enc = uin.getEncoding(); // check and skip possible BOM bytesif (enc == null){in = new InputStreamReader(uin);}else {in = new InputStreamReader(uin, enc);}long start = System.currentTimeMillis();//logger.info("读取文件"+filePath+"查看耗时开始");BufferedReader reader = new BufferedReader(in);//BufferedReader reader = new BufferedReader(new InputStreamReader(new FileInputStream("D:/tags.txt"),"utf-8"));//String tmp =reader.readLine();String[] headtilte = reader.readLine().split(",");list.add(headtilte);String line = null;String[] itemArray = null;while ((line = reader.readLine()) != null) {itemArray = line.split(",");list.add(itemArray);}long end = System.currentTimeMillis();//logger.info("读取文件"+filePath+"查看耗时="+(end-start)+"毫秒");return list;}catch (Exception e){logger.info("读取文件"+filePath+",异常:", e);return null;}finally {try {if(in!=null){in.close();}if(uin!=null){uin.close();}if(fis!=null){fis.close();}}catch (Exception e){logger.info("读取文件"+filePath+",关闭流异常:", e);}}}
(2)获取文件编码集
package com.guor.demo.utils;import org.slf4j.Logger;
import org.slf4j.LoggerFactory;import java.io.IOException;
import java.io.InputStream;
import java.io.PushbackInputStream;public class UnicodeInputStream extends InputStream {private static final Logger logger = LoggerFactory.getLogger(UnicodeInputStream.class);PushbackInputStream internalIn;boolean isInited = false;String defaultEnc;String encoding;private static final int BOM_SIZE = 4;public UnicodeInputStream(InputStream in, String defaultEnc) {internalIn = new PushbackInputStream(in, BOM_SIZE);this.defaultEnc = defaultEnc;}public String getDefaultEncoding() {return defaultEnc;}public String getEncoding() {if (!isInited) {try {init();} catch (IOException ex) {IllegalStateException ise = new IllegalStateException("Init method failed.");ise.initCause(ise);throw ise;}}return encoding;}/*** Read-ahead four bytes and check for BOM marks. Extra bytes are* unread back to the stream, only BOM bytes are skipped.*/protected void init() throws IOException {if (isInited) {return;}byte bom[] = new byte[BOM_SIZE];int n, unread;n = internalIn.read(bom, 0, bom.length);//logger.info("文件的前四个字符==="+bom[0]+","+bom[1]+","+bom[2]+","+bom[3]);if ( (bom[0] == (byte)0x00) && (bom[1] == (byte)0x00) &&(bom[2] == (byte)0xFE) && (bom[3] == (byte)0xFF) ) {encoding = "UTF-32BE";unread = n - 4;} else if ( (bom[0] == (byte)0xFF) && (bom[1] == (byte)0xFE) &&(bom[2] == (byte)0x00) && (bom[3] == (byte)0x00) ) {encoding = "UTF-32LE";unread = n - 4;} else if ( (bom[0] == (byte)0xEF) && (bom[1] == (byte)0xBB) &&(bom[2] == (byte)0xBF) ) {encoding = "UTF-8";unread = n - 3;} else if ( (bom[0] == (byte)0xFE) && (bom[1] == (byte)0xFF) ) {encoding = "UTF-16BE";unread = n - 2;} else if ( (bom[0] == (byte)0xFF) && (bom[1] == (byte)0xFE) ) {encoding = "UTF-16LE";unread = n - 2;} else {// Unicode BOM mark not found, unread all bytesencoding = defaultEnc;unread = n;}//System.out.println("read=" + n + ", unread=" + unread);if (unread > 0) {internalIn.unread(bom, (n - unread), unread);}isInited = true;}@Overridepublic void close() throws IOException {//init();isInited = true;internalIn.close();}@Overridepublic int read() throws IOException {//init();isInited = true;return internalIn.read();}
}
收录于热门专栏Java基础教程系列(进阶篇)
Java学习路线总结,搬砖工逆袭Java架构师
10万字208道Java经典面试题总结(附答案)
Java基础教程系列
Java基础教程系列(进阶篇)
相关文章:

Java中常见的编码集问题
收录于热门专栏Java基础教程系列(进阶篇) 一、遇到一个问题 1、读取CSV文件 package com.guor.demo.charset;import java.io.BufferedReader; import java.io.FileReader; import java.util.ArrayList; import java.util.HashMap; import java.util.L…...

数据结构与算法(Java版) | 就让我们来看看几个实际编程中遇到的问题吧!
上一讲,我给大家简单介绍了一下数据结构,以及数据结构与算法之间的关系,照理来说,接下来我就应该要给大家详细介绍线性结构和非线性结构了,但是在此之前,我决定还是先带着大家看几个实际编程中遇到的问题&a…...

【C++算法】dfs深度优先搜索(上) ——【全面深度剖析+经典例题展示】
💃🏼 本人简介:男 👶🏼 年龄:18 📕 ps:七八天没更新了欸,这几天刚搞完元宇宙,上午一直练🚗,下午背四级单词和刷题来着,还在忙一些学弟…...

总结高频率Vue面试题
目录 什么是三次握手? 什么是四次挥手?(close触发) 什么是VUEX? 什么是同源----跨域? 什么是Promise? 什么是fexl布局? 数据类型 什么是深浅拷贝? 什么是懒加载&…...

IP协议详解
目录 前言: IP协议 提出问题 解决方案 地址管理 子网掩码 路由选择 小结: 前言: IP协议作为网络层知名协议。当数据经过传输层使用TCP或者UDP对数据进行封装,然后当数据到达网络层,基于TCP或UDP数据包继续进行…...

webpack5 基础配置
在开发中,我们会使用 vue、react、less、scss等语法进行开发项目,但是浏览器只能识别 js、css,或者说在js中使用了es6中的import 导入 这时候也需要打包工具去转换成浏览器可以识别的语句。 一、使用webpack 1.初始化package.json npm i…...

IDEA入门安装使用教程
一、背景 作为一个Java开发者,有非常多编辑工具供我们选择,比如Eclipse、IntelliJ IDEA、NetBeans、Visual Studio Code、Sublime Text等等,这些有免费也有收费的,但是就目前市场占比来说普遍使用Eclipse和IntelliJ IDEA这两款主…...
Lambda表达式使用及详解
一 Lambda表达式的简介 Lambda表达式(闭包):java8的新特性,lambda运行将函数作为一个方法的参数,也就是函数作为参数传递到方法中。使用lambda表达式可以让代码更加简洁。 Lambda表达式的使用场景:用以简…...
JAVA练习52-打家劫舍
提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档 目录 前言 一、题目-打家劫舍 1.题目描述 2.思路与代码 2.1 思路 2.2 代码 总结 前言 提示:这里可以添加本文要记录的大概内容: 2月16日练习内容 提…...
简单谈一谈幂等测试
1、什么是幂等测试 幂等是一个抽象的概念,在编程中一个幂等操作的特点是其任意多次执行所产生的影响均与一次执行的影响相同,即多次调用方法或者接口不会改变业务状态,可以保证重复调用的结果和单次调用的结果一致。幂等测试,则主…...
typescript复习笔记
数组类型-限定每一项的类型 //写法一 const arrNumber: number[] [1, 2, 3] const arrString: string[] [a, b, c] //写法二 const arrNumber2: Array<number> [1, 2, 3] const arrString2: Array<string> [a, b, c]联合类型 符号是 | //数组可以存放字符串或…...

webstorm开发electron,调试主进程方案
官网教程地址:https://www.electronjs.org/zh/docs/latest/tutorial/debugging-main-process 我只能说官网太看得起人了,整这么简易的教程…… 命令行开关 第一步还是要按要求在我们的package.json里加上端口监听:–inspect5858 我的命令…...

2W字正则表达式基础知识总结,这一篇就够了!!(含前端常用案例,建议收藏)
正则表达式 (Regular Expression,简称 RE 或 regexp ) 是一种文本模式,包括普通字符(例如,a 到 z 之间的字母)和特殊字符(称为"元字符")正则表达式使用单个字符串来描述、匹配一系列匹…...

自学web前端觉得好难,可能你遇到了这些困境
好多人跟我说上学的时候也学过前端,毕业了想从事web前端开发的工作,但自学起来好难,快要放弃了,所以我总结了一些大家遇到的困境,希望对你会有所帮助。 目录 1. 意志是否坚定 2. 没有找到合适自己的老师 3. 为了找…...

ASEMI中低压MOS管18N20参数,18N20封装,18N20尺寸
编辑-Z ASEMI中低压MOS管18N20参数: 型号:18N20 漏极-源极电压(VDS):200V 栅源电压(VGS):30V 漏极电流(ID):18A 功耗(PD&#x…...

[NetBackup]客户端安装后server无法连通client
client name处填写客户端主机名,server to use for backups and restores处填写server端名字,与hosts文件内保持一致;source client for restores处填写client主机名,与server端hosts文件中保持一致,与主机实际名称保持…...

黑马Java后端项目实战--在线聊天交友
【课程简介】 越来越多的系统都有消息推送的功能,如聊天室、邮件推送、系统消息推送等; 要实现消息推送就需要服务端在数据有变化时主动推送消息给客户端,本次课程将带大家使用websocket实现消息推送。 【主讲内容】 1.方法:如…...
【实战系列 2】Yapi接口管理平台Getshell-Linux后门权限维持与痕迹清除
文章目录 前言一、网站主页到Getshell二、SSH软链接后门三、Linux权限维持 --隐藏踪迹3.1 隐藏远程SSH登陆记录3.2、ssh软链接后门连接失败的原因以及解决办法3.3、隐藏踪迹-痕迹清楚3.3.1、隐藏历史操作命令3.3.2、隐藏文件/文件夹3.3.3、修改文件时间戳3.3.4、隐藏权限3.3.5、…...

设计模式之抽象工厂模式(C++)
作者:翟天保Steven 版权声明:著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处 一、抽象工厂模式是什么? 抽象工厂模式是一种创建型的软件设计模式,该模式相当于升级版的工厂模式。 如果…...

Kotlin新手教程一(Kotlin简介及环境搭建)
目录一、 什么是Kotlin?二、为什么要使用Kotlin?三、使用IntelliJ IDEA搭建Kotlin四、Kotlin使用命令行编译一、 什么是Kotlin? Kotlin 是一种在 Java 虚拟机上运行的静态类型编程语言,它也可以被编译成为 JavaScript 源代码&…...
挑战杯推荐项目
“人工智能”创意赛 - 智能艺术创作助手:借助大模型技术,开发能根据用户输入的主题、风格等要求,生成绘画、音乐、文学作品等多种形式艺术创作灵感或初稿的应用,帮助艺术家和创意爱好者激发创意、提高创作效率。 - 个性化梦境…...

css实现圆环展示百分比,根据值动态展示所占比例
代码如下 <view class""><view class"circle-chart"><view v-if"!!num" class"pie-item" :style"{background: conic-gradient(var(--one-color) 0%,#E9E6F1 ${num}%),}"></view><view v-else …...
C++:std::is_convertible
C++标志库中提供is_convertible,可以测试一种类型是否可以转换为另一只类型: template <class From, class To> struct is_convertible; 使用举例: #include <iostream> #include <string>using namespace std;struct A { }; struct B : A { };int main…...

Appium+python自动化(十六)- ADB命令
简介 Android 调试桥(adb)是多种用途的工具,该工具可以帮助你你管理设备或模拟器 的状态。 adb ( Android Debug Bridge)是一个通用命令行工具,其允许您与模拟器实例或连接的 Android 设备进行通信。它可为各种设备操作提供便利,如安装和调试…...

MFC内存泄露
1、泄露代码示例 void X::SetApplicationBtn() {CMFCRibbonApplicationButton* pBtn GetApplicationButton();// 获取 Ribbon Bar 指针// 创建自定义按钮CCustomRibbonAppButton* pCustomButton new CCustomRibbonAppButton();pCustomButton->SetImage(IDB_BITMAP_Jdp26)…...

Docker 运行 Kafka 带 SASL 认证教程
Docker 运行 Kafka 带 SASL 认证教程 Docker 运行 Kafka 带 SASL 认证教程一、说明二、环境准备三、编写 Docker Compose 和 jaas文件docker-compose.yml代码说明:server_jaas.conf 四、启动服务五、验证服务六、连接kafka服务七、总结 Docker 运行 Kafka 带 SASL 认…...
渲染学进阶内容——模型
最近在写模组的时候发现渲染器里面离不开模型的定义,在渲染的第二篇文章中简单的讲解了一下关于模型部分的内容,其实不管是方块还是方块实体,都离不开模型的内容 🧱 一、CubeListBuilder 功能解析 CubeListBuilder 是 Minecraft Java 版模型系统的核心构建器,用于动态创…...
工程地质软件市场:发展现状、趋势与策略建议
一、引言 在工程建设领域,准确把握地质条件是确保项目顺利推进和安全运营的关键。工程地质软件作为处理、分析、模拟和展示工程地质数据的重要工具,正发挥着日益重要的作用。它凭借强大的数据处理能力、三维建模功能、空间分析工具和可视化展示手段&…...
基于数字孪生的水厂可视化平台建设:架构与实践
分享大纲: 1、数字孪生水厂可视化平台建设背景 2、数字孪生水厂可视化平台建设架构 3、数字孪生水厂可视化平台建设成效 近几年,数字孪生水厂的建设开展的如火如荼。作为提升水厂管理效率、优化资源的调度手段,基于数字孪生的水厂可视化平台的…...
Java入门学习详细版(一)
大家好,Java 学习是一个系统学习的过程,核心原则就是“理论 实践 坚持”,并且需循序渐进,不可过于着急,本篇文章推出的这份详细入门学习资料将带大家从零基础开始,逐步掌握 Java 的核心概念和编程技能。 …...