8位Unicode转换格式(UTF-8)是一种用于编码各种字符的相对较新的代码约定。
它是字符标识的标准,也是各种编程语言和设备的参考,有助于标准化字母,数字和其他字符的显示。
在许多情况下,UTF-8取代了一种名为美国信息交换标准码(ASCII)的旧约定。
ASCII处理英语语言文本所需的所有字符,但UTF-8为不使用英语或罗马字母的其他语言处理更多不同的符号集。UTF-8被认为是与ASCII向后兼容的。
查看文件编码
在Linux中查看文件编码可以通过以下几种方式:
1.在Vim中可以直接查看文件编码。
:set fileencoding。
即可显示文件编码格式。
如果你只是想查看其它编码格式的文件或者想解决用Vim查看文件乱码的问题,那么你可以在。
~/.vimrc 文件中添加以下内容:
set encoding=utf-8 fileencodings=ucs-bom,utf-8,cp936。
这样,就可以让vim自动识别文件编码(可以自动识别UTF-8或者GBK编码的文件),其实就是依照fileencodings提供的编码列表尝试,如果没有找到合适的编码,就用latin-1(ASCII)编码打开。
文件编码转换
1.在Vim中直接进行转换文件编码,比如将一个文件转换成utf-8格式。
:set fileencoding=utf-8。
2. iconv 转换,iconv的命令格式如下:
iconv -f encoding -t encoding inputfile。
比如将一个UTF-8 编码的文件转换成GBK编码。
iconv -f GBK -t UTF-8 file1 -o file2。
iconv -f gbk -t utf8 linux常用命令.txt > linux常用命令.txt.utf8。
文件名编码转换:
从Linux 往 windows拷贝文件或者从windows往Linux拷贝文件,有时会出现中文文件名乱码的情况,出现这种问题的原因是因为,windows的文件名 中文编码默认为GBK,而Linux中默认文件名编码为UTF8,由于编码不一致,所以导致了文件名乱码的问题,解决这个问题需要对文件名进行转码。
在Linux中专门提供了一种工具convmv进行文件名编码的转换,可以将文件名从GBK转换成UTF-8编码,或者从UTF-8转换到GBK。
首先看一下你的系统上是否安装了convmv,如果没安装的话用:。
yum -y install convmv。
安装。
下面看一下convmv的具体用法:
convmv -f 源编码 -t 新编码 [选项] 文件名。
常用参数:
-r 递归处理子文件夹
--notest 真正进行操作,请注意在默认情况下是不对文件进行真实操作的,而只是试验。
--list 显示所有支持的编码。
--unescap 可以做一下转义,比如把%20变成空格。
比如我们有一个utf8编码的文件名,转换成GBK编码,命令如下:
convmv -f UTF-8 -t GBK --notest utf8编码的文件名。
这样转换以后"utf8编码的文件名"会被转换成GBK编码(只是文件名编码的转换,文件内容不会发生变化)
这里的encoding不是说编码,而是告诉计算机这个文件的编码方式。
open文件后,不管你是要进行read操作还是write操作,都将认准编码方式来进行解码或者编码。
适用范围最广的是utf-8,所以一般你只要加上 encoding = ‘utf-8’这句话就可以了。
然后你会发现有不少人不喜欢守规矩,代码里经常没有这一句。
那是因为,macOS和Linux系统默认的编码形式就是utf-8.。
与之相对应的是,Windows的默认编码形式是CP1252.。
所以加上 encoding = ‘utf-8’应该是程序员最基本的素养。如果一项工程是接力完成的话,你用macOS写程序运行不影响,到了使用Windows的小伙伴那里大概率会出现连片的乱码,你最好祈祷你的fellow是一个熟练的老手,要不然他花上一天一夜的时间debug之后可能会想宰了你。
如果你说的“字符”就是指 Java 中的 char,那好,那它就是 16 位,2 字节。
如果你说的“字符”是指我们用眼睛看到的那些“抽象的字符”,那么,谈论它占几个字节是没有意义的。具体地讲,脱离具体的编码谈某个字符占几个字节是没有意义的。
就好比有一个抽象的整数“42”,你说它占几个字节?这得具体看你是用 byte,short,int,还是 long 来存它。
用 byte 存就占一字节,用 short 存就占两字节,int 通常是四字节,long 通常八字节。
当然,如果你用 byte,受限于它有限的位数,有些数它是存不了的,比如 256 就无法放在一个 byte 里了。
字符是同样的道理,如果你想谈“占几个字节”,就要先把编码说清楚。同一个字符在不同的编码下可能占不同的字节。
就以你举的“字”字为例,“字”在 GBK 编码下占 2 字节,在 UTF-16 编码下也占 2 字节,在 UTF-8 编码下占 3 字节,在 UTF-32 编码下占 4 字节。不同的字符在同一个编码下也可能占不同的字节。
“字”在 UTF-8 编码下占3字节,而“A”在 UTF-8 编码下占 1 字节。(因为 UTF-8 是变长编码),而 Java 中的 char 本质上是 UTF-16 编码。而 UTF-16 实际上也是一个变长编码(2 字节或 4字节)。
如果一个抽象的字符在 UTF-16 编码下占 4 字节,显然它是不能放到 char 中的。换言之, char 中只能放 UTF-16 编码下只占 2 字节的那些字符。而 getBytes 实际是做编码转换,你应该显式传入一个参数来指定编码,否则它会使用缺省编码来转换。
你说“ new String("字").getBytes().length 返回的是3 ”,这说明缺省编码是 UTF-8.。
如果你显式地传入一个参数,比如这样“ new String("字").getBytes("GBK").length ”,那么返回就是 2。你可以在启动 JVM 时设置一个缺省编码,假设你的类叫 Main,那么在命令行中用 java 执行这个类时可以通过 file.encoding 参数设置一个缺省编码。
比如这样:java -Dfile.encoding=GBK Main。
这时,你再执行不带参数的 getBytes() 方法时,new String("字").getBytes().length 返回的就是 2 了,因为现在缺省编码变成 GBK 了。
当然,如果这时你显式地指定编码,new String("字").getBytes("UTF-8").length 返回的则依旧是 3.。
否则,会使用所在操作系统环境下的缺省编码。
通常,Windows 系统下是 GBK,Linux 和 Mac 是 UTF-8.。
但有一点要注意,在 Windows 下使用 IDE 来运行时,比如 Eclipse,如果你的工程的缺省编码是 UTF-8,在 IDE 中运行你的程序时,会加上上述的 -Dfile.encoding=UTF-8 参数,这时,即便你在 Windows 下,缺省编码也是 UTF-8,而不是 GBK。
由于受启动参数及所在操作系统环境的影响,不带参数的 getBytes 方法通常是不建议使用的,最好是显式地指定参数以此获得稳定的预期行为。
如何确定文件编码格式的方法??
当我们用System.IO.StreamReader读取包含汉字的txt文件时,经常会读出乱码(StreamWriater写文本文件也有类似的问题),原因很简单,就是文件的编码(encoding)和StreamReader/Writer的encoding不对应。
为了解决这个问题,我写了一个类,来取得一个文本文件的encoding,这样我们就可以创建对应的StreamReader和StreamWriter来读写,保证不会出现乱码现象。其实原理很简单,文本编辑器(比如XP自带的记事本)在生成文本文件时,如果编码格式和系统默认的编码(中文系统下默认为GB2312)不一致时,会在txt文件开头部分添加特定的“编码字节序标识(Encoding Bit Order Madk,简写为BOM)”,类似PE格式的"MZ"文件头。这样它在读取时就可以根据这个BOM来确定该文本文件生成时所使用的Encoding。这个BOM我们用记事本等程序打开默认是看不到的,但是用stream按字节读取时是可以读到的。我的这个TxtFileEncoding类就是根据这个BOM“文件头”来确定txt文件生成时用到的编码的。
// 作者:袁晓辉
// 2005-8-8
// // // // // //。
using System;
using System.Text;。
using System.IO;。
namespace Farproc.Text。
/// <summary>。
/// 用于取得一个文本文件的编码方式(Encoding)。
/// </summary>。
public class TxtFileEncoding。
public TxtFileEncoding()。
//
// TODO: 在此处添加构造函数逻辑。
//
/// <summary>。
/// 取得一个文本文件的编码方式。如果无法在文件头部找到有效的前导符,Encoding.Default将被返回。
/// </summary>。
/// <param name="fileName">文件名。</param>。
/// <returns></returns>。
public static Encoding GetEncoding(string fileName)。
return GetEncoding(fileName, Encoding.Default);。
/// <summary>。
/// 取得一个文本文件流的编码方式。
/// </summary>。
/// <param name="stream">文本文件流。</param>。
/// <returns></returns>。
public static Encoding GetEncoding(FileStream stream)。
return GetEncoding(stream, Encoding.Default);。
?// <summary>。
/// 取得一个文本文件的编码方式。
/// </summary>。
/// <param name="fileName">文件名。</param>。
/// <param name="defaultEncoding">默认编码方式。当该方法无法从文件的头部取得有效的前导符时,将返回该编码方式。</param>。
/// <returns></returns>。
public static Encoding GetEncoding(string fileName, Encoding defaultEncoding)。
FileStream fs = new FileStream(fileName, FileMode.Open);。
Encoding targetEncoding = GetEncoding(fs, defaultEncoding);。
fs.Close();
return targetEncoding;。
/// <summary>。
/// 取得一个文本文件流的编码方式。
/// </summary>。
/// <param name="stream">文本文件流。</param>。
/// <param name="defaultEncoding">默认编码方式。当该方法无法从文件的头部取得有效的前导符时,将返回该编码方式。</param>。
/// <returns></returns>。
public static Encoding GetEncoding(FileStream stream, Encoding defaultEncoding)。
Encoding targetEncoding = defaultEncoding;。
if(stream != null && stream.Length >= 2)。
//保存文件流的前4个字节
byte byte1 = 0;。
byte byte2 = 0;。
byte byte3 = 0;。
byte byte4 = 0;。
//保存当前Seek位置
long origPos = stream.Seek(0, SeekOrigin.Begin);。
stream.Seek(0, SeekOrigin.Begin);。
int nByte = stream.ReadByte();。
byte1 = Convert.ToByte(nByte);。
byte2 = Convert.ToByte(stream.ReadByte());。
if(stream.Length >= 3)。
byte3 = Convert.ToByte(stream.ReadByte());。
if(stream.Length >= 4)。
byte4 = Convert.ToByte(stream.ReadByte());。
//根据文件流的前4个字节判断Encoding。
//Unicode {0xFF, 0xFE};。
//BE-Unicode {0xFE, 0xFF};。
//UTF8 = {0xEF, 0xBB, 0xBF};。
if(byte1 == 0xFE && byte2 == 0xFF)//UnicodeBe。
targetEncoding = Encoding.BigEndianUnicode;。
if(byte1 == 0xFF && byte2 == 0xFE && byte3 != 0xFF)//Unicode。
targetEncoding = Encoding.Unicode;。
if(byte1 == 0xEF && byte2 == 0xBB && byte3 == 0xBF)//UTF8。
targetEncoding = Encoding.UTF8;。
//恢复Seek位置
stream.Seek(origPos, SeekOrigin.Begin);。
return targetEncoding;。
由于在GB2312和UTF7编码都没有BOM,所以需要指定一个默认的Encoding,在找不到合法的BOM时,将返回这个Encoding。有谁知道如何区分GB2312和UTF7编码txt文件的方法,也请告诉我。
由于只是static方法,所以不用new,直接通过类名调用方法,使用起来也很简单。
using System;
using Farproc.Text;。
using System.Text;。
using System.IO;。
namespace ConsoleApplication1。
/// <summary>。
/// Class1 的摘要说明。
/// </summary>。
class Class1
/// <summary>。
/// 应用程序的主入口点。
/// </summary>。
[STAThread]
static void Main(string[] args)。
//
// TODO: 在此处添加代码以启动应用程序。
//
string fileName = @"e:\a.txt";。
//生成一个big endian Unicode编码格式的文本文件。
StreamWriter sw = new StreamWriter(fileName, false, Encoding.BigEndianUnicode);//你可以试试其他编码,比如Encoding.GetEncoding("GB2312")或UTF8。
sw.Write("这是一个String");。
sw.Close();
//读取
Encoding fileEncoding = TxtFileEncoding.GetEncoding(fileName, Encoding.GetEncoding("GB2312"));//取得这txt文件的编码。
Console.WriteLine("这个文本文件的编码为:" + fileEncoding.EncodingName);。
StreamReader sr = new StreamReader(fileName, fileEncoding);//用该编码创建StreamReader。
//用下面的方法虽然可以让系统自动判断文本文件的编码格式,但是我们无法取得该文本文件的编码。
//sr.CurrentEncoding永远为 Unicode(UTF-8)。
//StreamReader sr = new StreamReader(fileName, true);。
//Console.WriteLine("这个文本文件的编码为:" + sr.CurrentEncoding.EncodingName);。
Console.WriteLine("这个文本文件的内容为:" + sr.ReadToEnd());。
sr.Close();
Console.ReadLine();。
.NET下的string永远是Unicode的,所以只能判断txt文件的Encoding。对于byte[],只有自己知道它的Encoding才能转换为string 转换为其他编码的byte[],一个例外是把整个txt文件通过stream读入byte[]后也可以根据它的前几个字节判断Encoding,对于片断,我们就无能为力了:)。