文件,文本文件以及编码——乱码探源(1)

本文探讨了文件名与文件内容的编码方式,包括不同操作系统下的文件名编码,以及文本文件与非文本文件中的字符集编码问题。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

在前面的字符集编码系列中,已经探讨了几大主要的字符集编码。在此基础之上,这里将进一步探讨编码的应用及乱码的根源,我们先从基本的文件说起。

文件

文件(内容)就是字节序列。文本文件也是文件,所以它也是字节序列。

文件名与文件内容

通常说到文件时,指的是文件内容,但文件还有文件名,文件名与文件内容是分开存储的。

你可以在硬盘上新建一个文件,它的大小为0.如下:

image

但它是有文件名的,比如上述的“新建文本文档.txt“,保存这些名字自然也要占用空间,只不过它与文件内容是分离的。

这些由操作系统的文件系统模块负责。

文件名是一段文本,因此它会涉及字符集编码。

文件内容则视情况而定:

1. 文本文件,肯定会涉及字符集编码。

常见的比如txt,html,xml以及各种源代码文件等等。

2. 非文本文件,比如图片文件jpg,gif之类,自然跟字符集编码无关了。

有些文件,比如Word的doc之类的,混合了图片跟文本在里面,可以想像,其中的文本部分自然也会牵涉到字符集编码的问题,只不过这些编码不由我们去控制,我们通常也无须去关心。

文件名编码

文件名是一串文本,因此它必然涉及某种字符集编码,只不过这种编码是由操作系统决定的,我们无权干预。

那么,它用的是什么编码呢?在Windows下,可以简单做些实验。我们可以弄些奇怪的文件名如“★★★★.txt”,如下:

image

结果也能保存。这些字符只在Unicode中才有,所以它肯定不是用的GBK之类的。

Windows下NTFS架构文件名使用UTF-16编码。但对于FAT之类的,则是所谓的“OEM character set”。

MSDN上的原文如下:“NTFS stores file names in Unicode. In contrast, the older FAT12, FAT16, and FAT32 file systems use the OEM character set”(NTFS使用Unicode存储文件名。与此相对,老的FAT12,FAT16和FAT32文件系统使用OEM字符集).参见http://msdn.microsoft.com/en-us/library/windows/desktop/dd317748%28v=vs.85%29.aspx

注:在Windows语境中,UTF-16通常叫成Unicode。

结合实验的结果,可以确定,Windows使用UTF-16对文件名进行编码。(我的系统是Win 7,文件系统为NTFS)

不过,不同的系统平台可能使用了不同的编码。比如最新的Linux平台对文件名采用了UTF-8编码,但早期的则不好说,甚至没有一个标准。

如果你不是Windows平台,你也可以简单做些实验来大致猜测一下文件系统使用的编码。

文件的上传下载与文件名乱码

由于对文件名没有一个统一的编码,不同系统平台间交换文件时,中文文件名极易发生乱码现象。比如FTP上传,网页文件上传及下载等情况下经常能遇到文件名乱码。

不过,需要注意的是,交换过程中,文件内容不会发生任何改变。即便是文本文件,也完全是字节传送,不会涉及任何的编解码。

你可能碰到过这样的事,把一个文本文件从Windows平台上传到Linux平台,并在Linux平台下打开时发现乱码了,但这不意味着文件内容有了什么变化,通常的原因是你的文件是用GBK编码的,但Linux平台下打开时它缺省可能用的是UTF-8编码去读取,因此,你只要调整成正确的编码去读取即可。

在这里,我们讨论了文件名的编码,之后,如无特别说明,谈到编码时均指对文件内容的编码。通常,这是我们更为关心的内容。

非文本文件中的字符集编码

通常,说到字符集编码都是对文本文件而言的,但非文本文件也是可能用到字符集编码的。

比如,Word用什么编码?word生成的doc或者docx虽然不是文本文件,但我们可以想像,它里面可能有图像,又有文字。其中的文字自然也会用到某种编码。只不过,这些都不需要我们去操心。

下面是一个实验,新建一个空白的doc文档,录入几个简单字符”Hello你好”

image

保存成doc文件,再用notepad++打开,以十六进制形式查看:

image

如上图,搜索到hello几个关键字,我们知道,“H”的码点是U+0048,而“你”的码点则是U+4F60,所以,很显然,用的是UTF-16 LE(Little Endian,小端序)

关于端序及BOM的相关话题,可参见字符集与编码(七)——BOM

注:这只是我个人在本机测试的结果,不代表普遍的结论,不同平台不同版本下的可能会有差异,谁知道呢?我没有去研究过doc文件格式的规范,这个doc我还是用WPS生成的!

又比如,Java中的class文件,它也不是文本文件,通常称为字节码文件。但它里面也会保存String的常量,这自然又要牵涉到编码。实际用的是所谓的“modified UTF-8”编码。

简单建立一个java文件,定义一个string常量”Hello你好“:

public class Foo {
    static final String HI = "Hello你好";
}

保存并用javac命令编译得到class文件,再次用notepad++打开并以十六进制形式查看:

image

搜索到Hello几个关键字,紧接在它们后面的”e4 bd a0“就是”你“的UTF-8编码了。

在前面的字符集与编码(四)——Unicode中,曾提到过,汉字的UTF-8编码通常都是以e打头,形如ex xx xx这样,这是常用汉字UTF-8编码的一个重要特征。

这个”modified UTF-8”编码与UTF-8类似,但有一些差别,它的名字也暗示了这一点。

比如对于U+0000它用了两字节来编码;

还有对U+FFFF以上的字符它采用了6字节编码而非正常UTF-8的四字节编码,实质是对代理对(surrogate pairs)的值进行编码。

详情可参见http://docs.oracle.com/javase/7/docs/api/java/io/DataInput.html#modified-utf-8

文本文件中的字符集编码

文本文件也是文件,所以它也是字节序列。当读取一个文本文件时,最重要的是确定它所使用的编码,只有这样才能正确的解码。

由于牵涉的情况较多,我们将在下一篇讨论这一问题。

转载于:https://my.oschina.net/goldenshaw/blog/411022

### 数据仓库中的数据溯源方法与工具 #### 数据溯源的重要性 数据溯源是指追踪和记录数据在其生命周期内的流动路径、转换过程以及与其他数据的关系的过程。对于数据仓库而言,数据溯源可以帮助企业理解数据的来源、处理方式及其影响范围,从而提升数据质量和决策可靠性。 #### 常见的数据溯源方法 1. **手动文档记录法** 这种方法通常用于小型项目或初期阶段,通过人工编写文档来描述数据流和转换逻辑。尽管简单易行,但在复杂环境中容易出错且难以维护[^1]。 2. **基于日志的自动跟踪** 利用系统日志文件捕获每次数据操作的时间戳、执行者及具体动作。这种方法适用于实时性强的应用场景,但可能增加系统的性能开销[^2]。 3. **元数据驱动的方法** 使用专门设计的元数据管理系统(如 Apache Atlas 或 DataHub),捕捉并存储关于数据资产的各种属性信息,包括但不限于其物理位置、业务含义和技术细节。此类平台支持自动生成血缘图谱,清晰展示各表之间如何相互关联。 4. **嵌入式审计功能** 在应用程序内部集成额外的功能模块,用来标记每条记录的状态变化历史。比如,在数据库查询语句中加入特定字段以标识原始出处或者附加版本号等辅助标志位。 #### 推荐使用的工具 - **Apache Atlas**: 提供全面的企业级元模型定义能力,并能无缝对接Hadoop生态组件完成复杂的跨域分析需求。 - **DataHub (LinkedIn 开发)**: 更侧重于社交网络环境下大规模协作环境下的资源共享机制优化。 - **Alation**: 结合自然语言处理技术和机器学习算法增强用户体验的同时还具备强大的搜索推荐引擎。 - **Collibra**: 主打合规性管理和分类分级标签体系构建方面的优势明显。 #### 实施最佳实践建议 - 明确目标受众群体的需求差异点,定制化开发相应的可视化界面; - 定期审查现有流程是否存在冗余环节需精简调整之处; - 鼓励全员参与贡献反馈意见形成良性循环改进氛围; - 对敏感个人信息采取脱敏加密措施保障合法依规运作前提下最大化利用潜在商业价值; ```python import atlasclient as ac def get_data_lineage(entity_guid): client = ac.AtlasClient('http://localhost:21000') entity = client.entity.get_entity_by_guid(entity_guid) lineage_info = [] if 'relationshipAttributes' in entity['referredEntities'][entity_guid]: relationships = entity['referredEntities'][entity_guid]['relationshipAttributes'] for rel_key, rel_value in relationships.items(): if rel_value['typeName'] == 'DataSet': lineage_info.append({ 'source': rel_value['displayText'], 'target': entity['attributes']['name'] }) return lineage_info ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值