有关utf-8的介绍

UTF-8 不需要 BOM,尽管 Unicode 标准允许在 UTF-8 中使用 BOM。
所以不含 BOM 的 UTF-8 才是标准形式,在 UTF-8 文件中放置 BOM 主要是微软的习惯(顺便提一下:把带有 BOM 的小端序 UTF-16 称作「Unicode」而又不详细说明,这也是微软的习惯)。
BOM(byte order mark)是为 UTF-16 和 UTF-32 准备的,用于标记字节序(byte order)微软在 UTF-8 中使用 BOM 是因为这样可以把 UTF-8 和 ASCII 等编码明确区分开,但这样的文件在 Windows 之外的操作系统里会带来问题。

「UTF-8」和「带 BOM 的 UTF-8」的区别就是有没有 BOM。即文件开头有没有 U+FEFF。
UTF-8 的网页代码不应使用 BOM,否则常常会出错。这是一个小例子: 为什么这个网页代码 <head> 内的信息会被浏览器理解为在 <body> 内?

另附《The Unicode Standard, Version 6.0》之 3.10 D95 UTF-8 encoding scheme 的一段话:
While there is obviously no need for a byte order signature when using UTF-8, there are occasions when processes convert UTF-16 or UTF-32 data containing a byte order mark into UTF-8. When represented in UTF-8, the byte order mark turns into the byte sequence. Its usage at the beginning of a UTF-8 data stream is neither required nor recommended by the Unicode Standard, but its presence does not affect conformance to the UTF-8 encoding scheme. Identification of the byte sequence at the beginning of a data stream can, however, be taken as a near-certain indication that the data stream is using the UTF-8 encoding scheme.
http://www.unicode.org/versions/Unicode6.0.0/ch03.pdf

---------------------------------------------------------
首先,BOM是啥。这个就不解释了,Wikipedia上很详细。http://en.wikipedia.org/wiki/Byte_order_mark。
在网页上使用BOM是个错误。BOM设计出来不是用来支持HTML和XML的。要识别文本编码,HTML有charset属性,XML有encoding属性,没必要拉BOM撑场面。虽然理论上BOM可以用来识别UTF-16编码的HTML页面,但实际工程上很少有人这么干。毕竟UTF-16这种编码连ASCII都双字节,实在不适用于做网页。

其实说BOM是个坏习惯也不尽然。BOM也是Unicode标准的一部分,有它特定的适用范围。通常BOM是用来标示Unicode纯文本字节流的,用来提供一种方便的方法让文本处理程序识别读入的.txt文件是哪个Unicode编码(UTF-8,UTF-16BE,UTF-16LE)。Windows相对对BOM处理比较好,是因为Windows把Unicode识别代码集成进了API里,主要是CreateFile()。打开文本文件时它会自动识别并剔除BOM。Windows用这个有历史原因,因为它最初脱胎于多代码页的环境(ANSI环境)。而引入Unicode时Windows的设计者又希望能在用户不注意的情况下同时兼容Unicode和非Unicode(Multiple byte)文本文件,就只能借助这种小trick了。相比之下,Linux这样的系统在多locale的环境中浸染的时间比较短,再加上社区本身也有足够的动力轻装前进(吐槽:微软对兼容性的要求确实是到了非常偏执的地步,任何一点破坏兼容性的做法都不允许,以至于很多时候是自己绑住自己的双手),所以干脆一步到位进入UTF-8。当然中间其实有一段过渡期,比如从最初全UTF-8的GTK+2.0发布到基本上所有GTK开发者都弃用多locale的GTK+1.2,我印象中至少经历了三到四年。

BOM不受欢迎主要是在UNIX环境下,因为很多UNIX程序不鸟BOM。主要问题出在UNIX那个所有脚本语言通行的首行#!标示,这东西依赖于shell解析,而很多shell出于兼容的考虑不检测BOM,所以加进BOM时shell会把它解释为某个普通字符输入导致破坏#!标示,这就麻烦了。其实很多现代脚本语言,比如Python,其解释器本身都是能处理BOM的,但是shell卡在这里,没办法,只能躺着也中枪。说起来这也不能怪shell,因为BOM本身违反了一个UNIX设计的常见原则,就是文档中存在的数据必须可见。BOM不能作为可见字符被文本编辑器编辑,就这一条很多UNIX开发者就不满意。

顺便说一句,即使脚本语言能处理BOM,随处使用BOM也不是推荐的办法。各个脚本语言对Unicode的处理都有自己的一套,Python的 # -*- coding: utf-8 -*-,Perl的use utf8,都比BOM简单而且可靠。另一个好消息是,即使是必须在Windows和UNIX之间切换的朋友也不会悲催。幸亏在UNIX环境下我们还有VIM这种神器,即使遇到BOM挡道,我们也可以通过 set nobomb; set fileencoding=utf8; w 三条命令解决问题。

最后回头想想,似乎也真就只有Windows坚持用BOM了。

P.S.:本问题是自己的第150个回答。突然发现自己回答得很少很少⋯⋯
P.S. 2:突然想起需要解释一下为什么说VIM去除bomb的操作需要在UNIX下完成。因为VIM在Windows环境下有一个奇怪的bug,总是把UTF-16文件识别成二进制文件,而UNIX(Linux或者Mac都可以)下VIM则无问题。这个问题从VIM 6.8一直跟着我到VIM 7.3。目前尚不清楚这是VIM的bug还是我自己那个.vimrc文件的bug。如有高手解答不胜感激。

---------------------------------------------------------

有bom格式在开头会多出3个字节 EF BB BF ,主要用于识别编码。bom应该是windows特有的,在制作网页时会产生各种意想不到的问题,例如多输出了一个空行,影响PHP的session或者cookies功能(出现 header already sent错误),甚至可能引起页面的乱码(那3个字节影响了浏览器对页面编码的处理),因此总是推荐使用无bom编码。为了处理这个问题我甚至写了一个批量处理的PHP脚本。

---------------------------------------------------------
邸强,软件开发ing
张旭东、Mingyue Zhou、sapjax 赞同
几周前还在为BOM的问题苦恼着。。。
正如@梁海所说,“不含 BOM 的 UTF-8 才是标准形式”,的确是这样,无BOM使用得更多些,所以个人还是推荐一般情况下用无BOM的形式吧,除非有问题的时候,再考虑换有BOM的。Windows系统保存的都是有BOM的,所以你可以看到,用记事本保存一个UTF-8的txt,其实是有BOM的,这一点需要注意。另外不同的文本编辑器对于有无BOM的称呼也略有不同,比如EditPlus,有BOM的称为UTF-8+,无BOM的称为UTF-8,而在Notepad++中,有BOM的被称为标准UTF-8,而无BOM则被称为UTF-8无BOM。

---------------------------------------------------------
武龙飞,c/c++ 程序员,喜欢天文学,数学和心理学。
Weijing Huang、Bill Chan、icky R 赞同
以下文字出自我的博客内容,从另一面解说不一样的bom。
---------------------------------------------------------
字符编码相信是每个程序员的噩梦,只要是有中文的地方,总是会遇到各种编码的问题,并且这种问题还非常难缠,尤其在linux上,因为上面很多软件都是针对英语国家开发的,是不会考虑其他语种编码问题。在遇到编码的无数大坑之后,我决定仔细研究下编码问题,因为这就像一道坎一直横在你面前,每次到这里你都会跌到,每次爬起来之后,你都若无其事,这样的人被称作战士,真正的战士。可惜是个力量战士,做为新时代的智力战士,当然不能在那跌到然后又在这继续跌到。
文件的存储方式:
文件都有自己的存储格式,比如最常见的txt,cpp,h,c,xml ,png, rmvb各种格式,还有自定义格式。这些文件不论是什么格式,都是存储在计算机硬盘里的2进制格存储,对应不同文件格式,有不同的软件解析。这篇文章不谈文件是如何存储的,只谈文件是如何解析的。
文本文件解析:
文本文件对应于人类可以阅读的文本,如何从2进制转换为文本文件呢?起初由于计算机在美国发明,自然大家考虑的是英语如何表示,英语字母总共26个,加上特殊字符,128个字符,7位既一个byte即可表示出来。这个就是大家所熟知的ascill编码。对应关系很简单,一个字符对应一一个byte。
但很快发现,其他非英语国家的文字远远超过ascill码,这时候大家当然想统一一下,不同国家出了自己不同的编码方式,中国的gb2312就是自己做出来的编码方式,这样下去每个国家都有自己的编码方式,来回转换太麻烦了。这时候出现了新的编码方式,unicode编码方式,想将编码统一,所以规定了每个字符对应的unicode码。
1、很多文件都是ascii编码,如果用unicode 太浪费。
2、没有标志位说明该几个字节来解析为一个符号。
这时候拯救世界的utf出现了,utf是unicode的一种实现,只不过更聪明了。utf16是占用两字节,或者四字节,utf32是占用四字节。utf8是很聪明的一种表示方式。
1、对于单字节符号,字节第一位为0,后面7位表示字节编码。
2、对于n字节符号,第一字节的前n位都设为1,第n+1位为0,其余位位编码位置。
对于不同的编码,在文本的最前方有不同的标志,unicode 通常有两位来表示分别是ff fe, 或者feff, fffe表示big-endian 编码feff表示litte-endian编码。utf8是efbbbf来开头的。可以看出来utf-8是自解释的,所以不用带这个标志文件,大多数程序是可以识别的。但有些程序不能识别这个标志,比如php就会直接把这个标志当文本解析,不会忽略。相信很多遇到php输出文本解析乱码或者解析错误的同学都遇到这样的问题。
最后说说如何去掉或者加上bom,如果有vim那最好不过了,去掉命令:
set encoding=utf-8
set nobomb
添加命令:
set encoding=utf-8
set bomb
---------------------------------------------------------
带 BOM 的 UTF-8 就是赤裸裸的流氓!!!!!!!!!

windows总是自做聪明的做一些别人无法理解的事情!!!UTF-8是不需要BOM头的~~~!!

从刚开始学习代码(实在不能称我做的东西为程序)到现在,不晓得被这个BOM头搞了多少次,特别是对于我这种完全自学的人,知道找一个BUG需要多久多久不????

带不带BOM头区别就在于这个BOM头,祥见排名靠前的大神答案。windows特有的奇葩。请使用UTF-8 不带BOM头!!

它产生的BUG包含但不仅限于:
锘 -- 感谢 @飞扬 提供,参考其答案
HTML空白行
div之间莫明的间隔
乱码!
如果你用ssl那么一定会有问题!!!

顺便再鄙视一下 SONY的记忆棒、IPHONE的接口~~

这种吐槽的东西就让它折叠吧
--------------------------------------------------------
带 BOM 的 UTF-8 非常操蛋,经常造成莫名其妙的问题。
---------------------------------------------------------
我都是用的UTF-8 without BOM,带BOM的经常出现乱码
---------------------------------------------------------
notepad++会自动添加为带Bom的utf8比较坑爹
---------------------------------------------------------
建议编程人员能使用 Mac 编程的尽量使用Mac,Window是及其操蛋的操作系统。其次,如果我们要读取三方的文件并以UTF-8格式解析的时候一定要注意去判断这个文件是否有BOM,例如:sql文件的解析执行。
---------------------------------------------------------
网页编程中用不用bom我就不说什么了,因为软件原因无法使用的就更不能用了。

最近在学习用cocos2d-x,纯C++的编码,如果代码中有中文等的非ascii字符出现。发现会出错。代码是在mac 下用xcode 写的,放到windows 下用vs 编译。
最后把所有的源文件转成了带bom的格式后编译通过了,链接失败,这想这个就不是编码的问题了。

通常情况下,一般都 会认为在写C++代码的时候不要用中文,但是很多时候我们程序员也有想自己看着舒服的时候,为神马就不能写中文了?

于是在windows 下写了一个helloworld.cpp 类型的文件,输出内容用中文,然后存为utf-8 带bom格式,再把它copy到mac 下用g++ 编译,发现成功通过并且可正常运行,用xcode打开源文件也正常显示。

所以,这里建议程序要在windows 和 mac 还有linux 上运行的话,源代码最好保存成utf-8 带bom的格式,这样比较通用一些。而用utf-16 无论大端还是小端,g++ 都不认的。或者用utf-8 不带bom格式,然后代码不要出现非ascii 127以后的字符。

关于说utf-8 不带bom 才是标准的,我想应该是带用个人情绪的说法吧。真正的标准应该是bom是可选的,为什么可选?因为有些时候不带bom会出错,就拿历史较久远的windows来讲吧,很多国家的用户都在用windows ,其文件都是用其本地的ansi 编码来做的,比如大陆的GBK和GB2013,港台的big5,这些编码因为针对当地所用的字符制定的,所以呢,其存储文件较小,所以会大量使用,并且也大量存在着,微软不可能不考虑全球几十亿的用户的文件而盲目地修改解码方式,并且微软也是uncode 制定者之一,所以,带用bom的utf-8也是符合国际标准的。

或许是因为程序编写者的个人原因,也许是考虑到效率,很多的程序无法正确区分一个utf-8文件是否有bom,所以导致了各种乱码的出现。

个人不想说哪个是标准,也不想用语言去攻击哪个公司或团体。微软在坚持使用bom上没有错,因为这是在为用户考虑的。也许给我们这些写程序的带来了不便,但是,计算机最广泛的用户不是程序员。

<think>我们参考了用户提供的3个引用,其中引用[1]介绍了在Linux环境下使用shell脚本转换BOM头的方法,引用[2]和引用[3]则分别用C#代码演示了去除BOM头和转换编码的方法。 用户需求:将C#中带UTF-8 BOM的编码转换为UTF-8编码(即无BOM的UTF-8)。 根据引用[2]和引用[3],我们可以总结出以下两种方法: 方法一:直接去除BOM头(引用[2]) - 读取文件内容,检查文件开头是否有BOM(字节顺序标记:0xEF, 0xBB, 0xBF),如果有,则去掉这三个字节。 - 这种方法适用于已经将整个文件读入字节数组的情况。 方法二:重新写入文件(引用[3]) - 先读取文件内容(使用原始编码读取,包括可能带BOM的UTF-8),然后以无BOM的UTF-8编码重新写入文件。 - 注意:引用[3]中是将文件转换为带BOM的UTF-8,我们需要的是无BOM,所以调整一下即可。 由于用户需要的是无BOM的UTF-8,而C#中默认的`Encoding.UTF8`是带BOM的(注意:在C#中,使用`Encoding.UTF8`创建的编码器会写入BOM,但读取时能自动识别并忽略BOM)。如果我们想要无BOM的UTF-8,可以使用`new UTF8Encoding(false)`。 因此,我们可以这样实现: 1. 读取文件时,使用当前文件的编码(可能带BOM,也可能不带)。 2. 将读取的内容以无BOM的UTF-8编码重新写入。 注意:引用[3]中提到,对于日文转换后可能会乱码,所以我们需要确保源文件的编码正确识别。 步骤: - 读取文件:我们可以使用`StreamReader`,它会自动识别BOM并确定编码。然后读取内容。 - 写入文件:使用`StreamWriter`,并指定编码为`new UTF8Encoding(false)`(即无BOM)。 但是,需要注意的是,如果文件原本不是UTF-8编码(例如是其他编码如GBK、Shift_JIS等),那么直接转换为UTF-8无BOM是可以的,但需要确保读取时使用了正确的编码。 根据用户需求,我们假设文件已经是UTF-8(带BOM)的,现在转换为无BOM的UTF-8。 具体代码实现: 方法一:直接读取并重新写入(无BOM) ```csharp public static void ConvertToUtf8WithoutBom(string filePath) { string content = File.ReadAllText(filePath); // 自动识别BOM并正确读取 // 使用UTF8Encoding(false)表示无BOM File.WriteAllText(filePath, content, new UTF8Encoding(false)); } ``` 但是,这种方法对于大文件可能不是最佳选择,因为它会一次性读取整个文件。我们可以使用流的方式: 方法二:使用流的方式(适合大文件) ```csharp public static void ConvertToUtf8WithoutBom(string filePath) { // 先读取文件内容(自动检测编码,包括BOM) string content; using (StreamReader reader = new StreamReader(filePath, true)) { content = reader.ReadToEnd(); } // 以无BOM的UTF8写入 using (StreamWriter writer = new StreamWriter(filePath, false, new UTF8Encoding(false))) { writer.Write(content); } } ``` 注意:上面的代码会先读取整个文件内容到内存,然后写入。对于非常大的文件,内存占用可能较高。我们可以逐块读写: 方法三:流式转换(减少内存占用) ```csharp public static void ConvertToUtf8WithoutBom(string filePath) { string tempFilePath = Path.GetTempFileName(); // 使用StreamReader自动检测编码(包括BOM) using (var reader = new StreamReader(filePath, true)) using (var writer = new StreamWriter(tempFilePath, false, new UTF8Encoding(false))) { char[] buffer = new char[4096]; int bytesRead; while ((bytesRead = reader.Read(buffer, 0, buffer.Length)) != 0) { writer.Write(buffer, 0, bytesRead); } } // 将临时文件替换原文件 File.Delete(filePath); File.Move(tempFilePath, filePath); } ``` 但是,实际上StreamReader在读取时会自动处理BOM(即读取后不会将BOM包含在内容中),所以写入时就不会再有BOM了。因此,方法二和方法三都可以。 另外,如果我们要处理的文件原本不是UTF-8编码,而是其他编码,那么转换后文件内容可能会乱码。所以,我们假设用户已经知道这些文件是UTF-8带BOM的,只是要去掉BOM。 总结:推荐使用方法二,因为它简单且对于大多数文件足够快。如果文件很大,可以使用方法三(流式处理)来减少内存峰值。 另外,引用[2]中给出了直接操作字节数组的方法,如果用户已经将文件读入字节数组,那么可以这样去除BOM: ```csharp public static byte[] RemoveBom(byte[] data) { if (data.Length >= 3 && data[0] == 0xEF && data[1] == 0xBB && data[2] == 0xBF) { byte[] newData = new byte[data.Length - 3]; Array.Copy(data, 3, newData, 0, newData.Length); return newData; } return data; } ``` 然后可以将去除了BOM的字节数组写入文件(使用无BOM的编码写入也是可以的,但这里我们已经去掉了BOM,所以直接写入即可)。不过,这种方法需要读取整个文件到内存,同样有内存问题。 综上所述,我们提供两种方案: 方案1:针对小文件,使用`File.ReadAllText`和`File.WriteAllText`(见方法一)。 方案2:针对大文件,使用流式处理(见方法三),避免一次性占用过多内存。 请用户根据实际需求选择。 注意:在转换前最好备份文件,以防转换失败导致文件损坏。 最后,我们提供完整的转换代码(针对单个文件,并处理大文件): ```csharp using System; using System.IO; using System.Text; public class BomConverter { public static void ConvertToUtf8WithoutBom(string filePath) { // 创建临时文件路径 string tempFilePath = Path.Combine(Path.GetDirectoryName(filePath), Guid.NewGuid().ToString() + ".tmp"); // 使用StreamReader自动检测编码(包括BOM),然后以无BOM的UTF-8写入临时文件 using (var reader = new StreamReader(filePath, true)) using (var writer = new StreamWriter(tempFilePath, false, new UTF8Encoding(false))) { char[] buffer = new char[4096]; int bytesRead; while ((bytesRead = reader.Read(buffer, 0, buffer.Length)) != 0) { writer.Write(buffer, 0, bytesRead); } } // 替换原文件 File.Delete(filePath); File.Move(tempFilePath, filePath); } } ``` 这个方法可以处理大文件,并且不会一次性占用太多内存。 如果用户需要处理整个目录,可以结合`Directory.GetFiles`方法,遍历每个文件进行转换。 注意:转换前请确保文件是文本文件,并且是UTF-8带BOM的,否则可能破坏文件内容。 相关问题: 1. 如何判断一个文件是否包含UTF-8 BOM? 2. 如何处理整个目录下的所有文件(包括子目录)? 3. 在C#中,除了UTF-8,还有其他编码方式使用BOM吗? </think>### C# 带 UTF-8 BOM 编码转 UTF-8 无 BOM 编码的实现方法 在 C# 中,将带 BOM 的 UTF-8 编码转换为无 BOM 的 UTF-8 编码,主要有以下两种方法: --- #### 方法一:字节操作(直接高效) 通过检查并移除文件开头的 BOM 标记(`0xEF, 0xBB, 0xBF`): ```csharp public static void RemoveUtf8Bom(string filePath) { byte[] data = File.ReadAllBytes(filePath); // 检查并移除BOM if (data.Length >= 3 && data[0] == 0xEF && data[1] == 0xBB && data[2] == 0xBF) { byte[] newData = new byte[data.Length - 3]; Buffer.BlockCopy(data, 3, newData, 0, newData.Length); File.WriteAllBytes(filePath, newData); } } ``` **特点**: - 直接操作字节,高效可靠 - 适用于二进制文件或文本文件 --- #### 方法二:编码重写(自动处理编码) 通过 `Encoding.UTF8`(带 BOM)读取,再用 `UTF8Encoding(false)`(无 BOM)写入: ```csharp public static void ConvertToUtf8WithoutBom(string filePath) { // 读取内容(自动处理BOM) string content = File.ReadAllText(filePath, Encoding.UTF8); // 以无BOM格式重新写入 File.WriteAllText(filePath, content, new UTF8Encoding(false)); } ``` **注意**: - 此方法会隐式移除 BOM - 确保原始文件确实是 UTF-8 编码,否则可能乱码[^3] --- ### 使用场景对比 | **方法** | **适用场景** | **注意事项** | |----------------|----------------------------------|----------------------------------| | 字节操作 | 需精确控制二进制数据 | 需手动验证 BOM | | 编码重写 | 纯文本文件处理 | 非 UTF-8 文件可能导致乱码[^3] | ### 验证 BOM 是否存在 ```csharp public static bool HasUtf8Bom(string filePath) { byte[] header = new byte[3]; using (FileStream fs = new FileStream(filePath, FileMode.Open)) { fs.Read(header, 0, 3); } return header[0] == 0xEF && header[1] == 0xBB && header[2] == 0xBF; } ``` --- ### 关键说明 1. **BOM 的本质**:UTF-8 BOM 是一个 3 字节的头部标识(`EF BB BF`),在 UTF-8 标准中非必需[^1] 2. **兼容性问题**:Linux 系统默认不识别 BOM,Windows 应用(如 VS)可能依赖 BOM 识别编码[^1] 3. **性能建议**:大文件优先使用字节操作(`FileStream` 流式处理),避免内存溢出 ---
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值