文本文件字符编码的探查

TXT文件字符存储编码格式,是指文件中的字符是以何种编码方式存储在磁盘中,一切以文本方式存储的文件都涉及这个问题,这些文件包括:.txt、 .PAS、 .HTML、 .XML等文件。如果不能正确识别存储编码,则在显示这些文件的时候会导致乱码。注意要区分字符存储编码和语言显示代码,HTML语言有一个语言显示代码,是指里面的文本以何种语言显示。

在win10系统下的TXT文件存储字符编码有以下几种,ASCII,ANSI、UTF-8、带有BOM的UTF-8 、UTF-16LE、UTF-16BE。

一般情况下默认采用UTF-8编码存储的,这个编码是通用的,可以表达任何语言,但是也有缺点,就是编码长度不等长。下面说说,这几种编码的特点:

(1)   ASCII和ANSI编码:这两个编码是相互兼容的,如果字节的最高位是0(0-7F),二进制形如0XXX XXXX,则表达的是ASCII字符也就是USA-ASCII字符表内容。如果字节的最高位是1(80-FE),则就是ANSI(GBK)编码,注意,这时候是两个字节表达一个汉字,也就是说两个字节的最高位都是1的字节代表一个汉字,二进制形如1XXX XXXX, 1XXX XXXX 。因此ANSI字符编码是兼容ASCII编码的。

 在ANSI编码下,如果文本字符都是西文字符,则也可以认为是ASCII编码,当有连续高为是1的字节串出现的时候,这是可以判定为ANSI或GBK编码。GBK编码一定是两个高位均为1的字节表达一个汉字。ANSI则是一个字节表达一个字符,这个字节高为可以是1或0。

(2) UTF-8编码。utf-8是一种多字节编码的字符集,表示一个Unicode字符时,它可以是1个至多个字节。即在文本全部是ASCII字符时utf-8是和ASCII一致的(utf-8向下兼容ASCII)。最多6个字节表达一个字符,utf-8字节流如下所示:
             1字节:0xxxxxxx 
             2字节:110xxxxx 10xxxxxx 
             3字节:1110xxxx 10xxxxxx 10xxxxxx  ,一般汉字用这个3字节表达
             4字节:11110xxx 10xxxxxx 10xxxxxx 10xxxxxx
             5字节:111110xx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx
        注意,在UTF-8编码中,多种长度是混合存在的,即,一个字符串里面可能有1,2,3,等字节长度表达的字符同时存在 。因此在判断时候带来一定的难度。

(3)带有BOM的UTF-8,这个文本文件的头部带有三个先导标志字节0xEF, 0xBB,0xBF,通过判断这个标志,可以判断出这个文本文件是UTF-8编码。

(4)UTF-16LE,字节流是little endian ,这是标准UNICODE编码文本,是双字节等长编码。文本文件头部带有先导志字节0xFF 0xFE,通过判断这个标志,可以判断出这个文本文件是UTF-16LE编码。

(5) UTF-16BE, 字节流是big  endian,这是另一种UNICODE编码文本,是双字节等长编码。文本文件头部带有先导志字节0xFE  0xFF,通过判断这个标志,可以判断出这个文本文件是UTF-16BE编码。

 通过以上分析,ASCII,ANSI、UTF-8这种编码的文本文件,需要对字符串的字节流进行正确分析才能判断出编码方案,方法复杂,尤其是UTF-8编码。

带有BOM的UTF-8 、UTF-16LE、UTF-16BE这三个编码,只需要分析文本文件的头部字节就可以判断出来编码方案,方法简单。

以下是本人用delphi语言写的分析代码:


type
  TTextFormat=(tfUSAASCII,tfANSI,tfUtf_8,tfUTF_16LE,tfUTF_16BE,tfUtf_8BOM);
    // 返回文本文件编码类型,sText返回标志字
   function GetTextFormat(const FileName: string;  var sText:string):TTextFormat;

   function DetectUTF8Encoding2(const ss: array of byte; var Bn:Integer): TEncodeType;

const
  TextFormatFlag:array[tfUTF_16LE..tfUtf_8BOM] of LongWord=($FFFE,$FEFF,$EFBBBF);

  TextFormatFlagT:array[TTextFormat] of string=('ASCII','ANSI(GBK)','UTF-8','UTF-16LE','UTF-16BE','UTF-8BOM');


implementation

{$R *.dfm}

{ TForm1 }

function TForm1.GetTextFormat(const FileName: string;   var sText: string): TTextFormat;
var

  fTxtStream: TFileStream; //文件流
  w:Word;
  Count,i,NC:Integer;

  context:array[0..127] of byte;
  tt:array[TEncodeType] of boolean;

  aap:boolean;  //方式判别
begin


  sText:='';       aap:=False;
  tt[etUTF8]:=False;  tt[etANSI]:=False;  tt[etUSASCII]:=False;

  result:=tfAnsi;
  //   NC:=128;


  fTxtStream := TFileStream.Create(FileName, fmOpenRead or fmShareDenyNone);
  try
    NC:=fTxtStream.Size ;  //

     count:=fTxtStream.Read(context, 128); //一次读取不超过128个,返回实际读取字节数

    if not(aap) then
    begin
       if Count>=2 then  //只判断文件长度超过2字节的文件的头部。
       begin

         w:=(word(context[0]) shl 8) or context[1];    //  组合成word
            case w of
              $FFFE:result:=tfUTF_16LE;
              $FEFF:result:=tfUTF_16BE;
              $EFBB: begin if Count>=3 then
                             if context[2]=$BF then  result:=tfUtf_8BOM
                               else  aap:=True  //如果都不是,则需要进一步判断(USASCII, UTF8, ANSI编码 )
                     end;
            else
             aap:=True;; //如果都不是,则需要进一步判断(USASCII, UTF8, ANSI编码 )
            end;


       end  else aap:=True ; //如果都不是,则需要进一步判断(USASCII, UTF8, ANSI编码 )


      
             //格式化标志字
       if Result=tfUtf_8BOM
             then  sText:='前导标志字-'+RightStr(inttohex(TextFormatFlag[Result]),6) ;

       if (Result=tfUTF_16LE) or (Result=tfUTF_16BE)
             then  sText:='前导标志字-'+RightStr(inttohex(TextFormatFlag[Result]),4);

    end;

  //判断Txt文件的编码类型(USASCII, UTF8, ANSI编码
 //如果都不是,则需要进一步判断(USASCII, UTF8, ANSI编码 )
    if aap then
    begin

                //探查编码
               i:=Count;
                case DetectUTF8Encoding2(context,i) of
                  etUTF8:begin
                           tt[etUTF8]:=True; //如果探查到Utf_8  跳出循环
                     //      lbl1.Caption:=TextFormatFlagT[tfUtf_8];
                           //Break ;
                         end;
                  etANSI: begin   tt[etANSI]:=True;  //继续探查
                     //      lbl3.Caption:=TextFormatFlagT[tfANSI];
                          end;
                  etUSASCII:begin  tt[etUSASCII]:=True; //继续探查
                     //        lbl4.Caption:=TextFormatFlagT[tfUSAASCII];
                             end;
                end;

            if tt[etUTF8] then result:=tfUtf_8
               else if tt[etANSI] then  result:=tfANSI
                    else if tt[etUSASCII] then result:=tfUSAASCII;


            sText:='无前导标志字,编码字节长度-'+IntToStr(i);

    end;

  finally
        FreeAndNil(fTxtStream);
  end;





end;



function TForm1.DetectUTF8Encoding2(const ss: array of byte; var Bn:Integer): TEncodeType;
var

  i,sCount:Integer;
  sT:array[TEncodeType] of integer;
begin
  Result := etUSASCII;

  sT[etUSASCII]:=0;  sT[etANSI]:=0; sT[etUTF8]:=0; //初始化

  sCount:=bn;  //字节实际长度

  bn:=0;  i:=0;   //初始化




  if sCount<=Length(ss) then
  while i<sCount do
  begin

    if (ss[i] in [$00..$7F]) then   sT[etUSASCII]:=1 ;   //USAASCII标记

    if (ss[i] in [$80..$FD]) and ((i+1)< sCount)
       then  if (ss[i+1] in [$80..$FD])
                then sT[etANSI]:=2 //两字节ANSI,GBK字符
                else if sT[etANSI]=0 then sT[etANSI]:=1  ;  //单字节ANSI字符


    if (ss[i] in [$C0..$DF]) and ((i+1) < sCount)
       then if (ss[i+1] in [$80..$BF])
               then  if sT[etUTF8]<2 then sT[etUTF8]:=2 ; //是两字节UTF8

    if (ss[i] in [$E0..$EF]) and ((i+2) < sCount)
       then if ((ss[i+1] and ss[i+2]) in [$80..$BF]) //后两个字节是否是10开头·
            then if sT[etUTF8]<3 then sT[etUTF8]:=3; //是三字节UTF8

    if (ss[i] in [$F0..$F7]) and ((i+3) < sCount)
       then if ((ss[i+1] and ss[i+2] and ss[i+3]) in [$80..$BF]) //后三个字节是否是10开头
            then if sT[etUTF8]<4 then sT[etUTF8]:=4  ;//是四字节UTF8

   // lbl4.Caption:=lbl4.Caption+IntTostr(sT[etANSI])  ;
    inc(i); //读下一个字节

  end;

  if sT[etUTF8]>=2
  then begin
    result:=etUTF8 ; bn:=sT[etUTF8];
       end
  else if sT[etANSI]>=1
       then    begin
                  result:=etANSI ; bn:=sT[etANSI];
               end
       else  if sT[etUSASCII]>=1
             then  begin
                      result:=etUSASCII;  bn:=sT[etUSASCII];
                   end;

  • 1
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

孤独的学者

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值