Java中常见的编码集问题

news2024/11/18 11:27:52

在这里插入图片描述

收录于热门专栏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. 1个字节:英文,数字,回车符,ASCII中的常用符号+、-、*、/等;
  2. 2个字节:个别特殊符号;
  3. 3个字节:常见汉字,在GBK中存在的汉字;
  4. 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 bytes
            if (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 bytes
            encoding = defaultEnc;
            unread = n;
        }
        //System.out.println("read=" + n + ", unread=" + unread);
        if (unread > 0) {
            internalIn.unread(bom, (n - unread), unread);
        }
        isInited = true;
    }

    @Override
    public void close() throws IOException {
        //init();
        isInited = true;
        internalIn.close();
    }

    @Override
    public int read() throws IOException {
        //init();
        isInited = true;
        return internalIn.read();
    }
}

收录于热门专栏Java基础教程系列(进阶篇)
在这里插入图片描述

Java学习路线总结,搬砖工逆袭Java架构师

10万字208道Java经典面试题总结(附答案)

Java基础教程系列

Java基础教程系列(进阶篇)

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/349843.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Syntax-Aware Aspect-Level Sentiment Classification with PWCN 论文阅读笔记

一、作者 Chen Zhang, Qiuchi Li, and Dawei Song. 2019. Syntax-Aware Aspect-Level Sentiment Classification with Proximity-Weighted Convolution Network. In Proceedings of the 42nd International ACM SIGIR Conference on Research and Development in Information …

jsp游泳馆门票管理系统Myeclipse开发mysql数据库web结构java编程计算机网页项目

一、源码特点 jsp游泳馆门票管理系统 是一套完善的web设计系统&#xff0c;对理解JSP java编程开发语言有帮助&#xff0c;系统具有完整的源代码和数据库&#xff0c;系统主要采用B/S模式开发。开发环境为 TOMCAT7.0,Myeclipse8.5开发&#xff0c;数据库为Mysql&#xff0c;…

MySQL的存储引擎

目录 一.概念 二.分类 操作 修改默认存储引擎 一.概念 数据库存储引擎是数据库底层软件组织&#xff0c;数据库管理系统&#xff08;DBMS&#xff09;使用数据引擎进行创建、查询、更新和删除数据。不同的存储引擎提供不同的存储机制、索引技巧、锁定水平等功能。现在许多不…

数据预处理——数据无量纲化(归一化、标准化)

文章目录1. 数据归一化1.1 数据归一化定义1.2 MinMaxScaler 归一化1.3 MinMaxScaler 使用样例2. 数据标准化2.1 数据标准化定义2.2 StandardScaler 标准化2.3 StandardScaler 使用样例StandardScaler和MinMaxScaler选哪个&#xff1f;在机器学习算法实践中&#xff0c;我们往往…

儿童玩具车扭扭车上架欧盟亚马逊CE认证EN71项目测试

扭扭车又称儿童健身车&#xff0c;摇摆车,主体由工程聚丙烯&#xff0c;经注塑而成&#xff0c;结构稳固&#xff0c;操作简单&#xff0c;无需电瓶和传动装置&#xff0c;只要左右转动方向盘&#xff0c;就可随意前后行驶。是一种环保的绿色玩具&#xff0c;最早出现在中国台湾…

c++:缺省参数,函数重载

今天介绍的是cpp中的缺省参数以及函数重载的知识。 首先我们先看看缺省参数&#xff1a; 缺省参数 缺省参数是声明或定义函数时为函数的参数指定一个缺省值。在调用该函数时&#xff0c;如果没有指定实 参则采用该形参的缺省值&#xff0c;否则使用指定的实参。 例如&#…

项目——博客系统

文章目录项目优点项目创建创建相应的目录&#xff0c;文件&#xff0c;表&#xff0c;导入前端资源实现common工具类实现拦截器验证用户登录实现统一数据返回格式实现加盐加密类实现encrypt方法实现decrypt方法实现SessionUtil类实现注册页面实现前端代码实现后端代码实现登录页…

JS 动态爱心(HTML+CSS+JS)

✅作者简介&#xff1a;2022年博客新星 第八。热爱国学的Java后端开发者&#xff0c;修心和技术同步精进。 &#x1f34e;个人主页&#xff1a;Java Fans的博客 &#x1f34a;个人信条&#xff1a;不迁怒&#xff0c;不贰过。小知识&#xff0c;大智慧。 &#x1f49e;当前专栏…

webstorm开发electron,调试主进程方案

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

恭喜山东翰林“智慧园区管理系统”获易知微可视化设计大赛二等奖

数字化经济发展是全球经济发展的重中之重&#xff0c;“数字孪生&#xff08;Digital Twin&#xff09;”这一词汇正在成为学术界和产业界的一个热点。数字孪生作为近年来的新兴技术&#xff0c;其与国民经济各产业融合不断深化&#xff0c;推动着各大产业数字化、网络化、智能…

关于服务连接器(Servlet)你了解多少?

Servlet 1 简介 Servlet是JavaWeb最为核心的内容&#xff0c;它是Java提供的一门动态web资源开发技术。 使用Servlet就可以实现&#xff0c;根据不同的登录用户在页面上动态显示不同内容。 Servlet是JavaEE规范之一&#xff0c;其实就是一个接口&#xff0c;将来我们需要定义…

血液透析过滤芯气密性检测装置中的高精度多段压力控制解决方案

摘要&#xff1a;针对目前血液过滤芯气密性检测过程中存在的自动化水平较低、多个检测压力之间需人工切换和压力控制精度较差的问题&#xff0c;为满足客户对高精度和自动化气密性检测的要求&#xff0c;本文提出了相应的解决方案。解决方案的主要特点是全过程的可编程压力控制…

Git的使用方法(保姆级)

一、安装git二、创建凭据 ①打开电脑的凭据管理器git:https://gitee.com是固定写法用户名、密码是你创建gitee的用户名、密码三、在gitee中创建一个仓库四、项目提交到仓库的方法①选择一个项目交由git管理按照步骤一中召唤小黑窗口输入 git init 就可以出现.git文件夹②右键选…

Golang基础 函数详解 匿名函数与闭包

文章目录01 匿名函数1.1 定义匿名函数1.2 匿名函数使用场景02 闭包2.1 闭包实现公有变量2.2 闭包实现缓存效果参考资料匿名函数是指不需要定义函数名的一种函数实现方式&#xff08;即没有名字的函数&#xff09;。匿名函数多用于实现回调函数和闭包。 01 匿名函数 Golang 支持…

财报解读:营收增长、亏损扩大,Shopify如何度过阵痛期?

后疫情时代&#xff0c;Shopify阵痛不断。 图源&#xff1a;Shopify 北京时间2023年2月16日&#xff0c;Shopify披露了2022年四季度财报&#xff0c;营收17.3亿美元&#xff0c;同比增长25.4%&#xff0c;高于分析师预期的16.5亿美元&#xff1b;净亏损为6.24亿美元&#xff0…

用上Visual Studio后,我的世界游戏的构建时间减少了一半

今天我们讲述一个使用 Visual Studio 提升工作效率的案例。 我的世界(Minecraft) 游戏开发商 Mojang Studios 近日联系了 Visual Studio C 团队&#xff0c;因为他们需要将 C 开发扩展到新平台&#xff08;Linux&#xff09;&#xff0c;同时还希望保留他们现有的技术基础&…

同源页面间的跨页面通信之BroadCast Channel

LocalStorage 通过LocalStorage存储内容&#xff0c;并且在改变某个tab页面后&#xff0c;另外一个tab页面监听变动这种方式应该大家都比较熟悉了。 监听变动的代码如下&#xff1a; 第二个tab页面监听如下 window.addEventListener(storage, function (e) {if (e.key ctc…

LeetCode-77. 组合

目录回溯法剪枝优化题目来源 77. 组合 回溯法 1.递归函数的返回值以及参数 在这里要定义两个全局变量&#xff0c;一个用来存放符合条件单一结果&#xff0c;一个用来存放符合条件结果的集合。 List<List<Integer>> result new ArrayList<>();LinkedList…

数据分析就要选择这款免费报表工具

对于一家企业来说&#xff0c;在日常运营的过程中本身就会产出很多的数据&#xff0c;那么这些数据本身就应该形成报表。可是如果只是选择手工的一种操作&#xff0c;确实需要浪费大量的人力物力。伴随着科技进入到快速发展的阶段&#xff0c;市面上更是出现了很多报表工具可以…

九龙证券|可转债一级市场回暖 14家上市公司可转债发行集中获批

可转债商场悄然升温。春节假期后&#xff0c;可转债新券上市体现普遍不错&#xff0c;多只个券首日涨幅打破30%&#xff0c;更有3个买卖日就实现翻倍的案例。一起&#xff0c;本周初可转债打新户数本年以来也首度站上1000万户大关。 因为新券盈余效应明显&#xff0c;可转债一级…