【问题标题】:Converting a byte array to a string given encoding将字节数组转换为给定编码的字符串
【发布时间】:2014-06-25 09:33:14
【问题描述】:

我从一个文件读取到一个字节数组:

auto text = cast(immutable(ubyte)[]) read("test.txt");

我可以使用以下函数获取字符编码的类型:

enum EncodingType {ANSI, UTF8, UTF16LE, UTF16BE, UTF32LE, UTF32BE}

EncodingType DetectEncoding(immutable(ubyte)[] data){
  switch (data[0]){
    case 0xEF:
      if (data[1] == 0xBB && data[2] == 0xBF){
        return EncodingType.UTF8;
      } break;
    case 0xFE:
      if (data[1] == 0xFF){
        return EncodingType.UTF16BE;
      } break;
    case 0xFF:
      if (data[1] == 0xFE){
        if (data[2] == 0x00 && data[3] == 0x00){
          return EncodingType.UTF32LE;
        }else{
          return EncodingType.UTF16LE;
        }
      }
    case 0x00:
      if (data[1] == 0x00 && data[2] == 0xFE && data[3] == 0xFF){
        return EncodingType.UTF32BE;
      }
    default:
      break;
  }
  return EncodingType.ANSI;
}

我需要一个接受字节数组并返回文本字符串 (utf-8) 的函数。 如果文本以 UTF-8 编码,则转换是微不足道的。同样,如果编码是 UTF-16 或 UTF-32 系统的本机字节顺序。

string TextDataToString(immutable(ubyte)[] data){
  import std.utf;
  final switch (DetectEncoding(data[0..4])){
    case EncodingType.ANSI:
      return null;/*???*/
    case EncodingType.UTF8:
      return cast(string) data[3..$];
    case EncodingType.UTF16LE:
      wstring result;
      version(LittleEndian) { result = cast(wstring) data[2..$]; }
      version(BigEndian) { result = "";/*???*/ }
      return toUTF8(result);
    case EncodingType.UTF16BE:
      return null;/*???*/
    case EncodingType.UTF32LE:
      dstring result;
      version(LittleEndian) { result = cast(dstring) data[4..$]; }
      version(BigEndian) { result = "";/*???*/ }
      return toUTF8(result);
    case EncodingType.UTF32BE:
      return null;/*???*/
  }
}

但我不知道如何将字节数组与 ANSI 编码文本(例如 windows-1251)或 UTF-16/32 与非原生字节顺序进行转换。 我用/*???*/勾选了代码中的适当位置。

因此,以下代码应该适用于文本文件的任何编码:

string s = TextDataToString(text);
writeln(s);

请帮忙!

【问题讨论】:

    标签: encoding utf-8 d utf-16


    【解决方案1】:

    BOM 是可选的。您不能使用它们来可靠地检测编码。即使有 BOM,使用它来区分 UTF 和代码页编码也是有问题的,因为字节序列通常也是有效的(如果无意义的话)。例如。 0xFE 0xFF 在 Windows-1251 中是“юя”。

    即使您可以将 UTF 与代码页编码区分开来,您也无法将不同的代码页与另一个代码页区分开来。您可以分析整个文本并进行猜测,但这非常容易出错并且不太实用。

    所以,我建议您不要尝试检测编码。相反,需要一个特定的编码,或者添加一个机制来指定它。


    对于不同字节顺序的转码,例如 UTF16BE:

    import std.algorithm: map;
    import std.bitmanip: bigEndianToNative;
    import std.conv: to;
    import std.exception: enforce;
    import std.range: chunks;
    
    alias C = wchar;
    enforce(data.length % C.sizeof == 0);
    auto result = data
        .chunks(C.sizeof)
        .map!(x => bigEndianToNative!C(x[0 .. C.sizeof]))
        .to!string;
    

    【讨论】:

    • 这个模块也可以做一点 ansi 转码:dlang.org/phobos/std_encoding.html 虽然它非常小。此外,我在这里找到的 characterencodings.d github.com/adamdruppe/arsd/blob/master/characterencodings.d 支持更多并且具有相当简单的操作:例如string s = convertToUtf8(data_as_bytes, "windows-1251"); - 将编码作为字符串提供。函数 tryToDetermineEncoding 会寻找像 OP 这样的 BOM,尽管我没有处理大端!你的代码也是一个不错的补充。
    • 感谢代码!是的,我也得出结论,最好在读取文件之前允许指定编码。但是,默认情况下期望 UTF-8 也不正确。我至少需要采取一些步骤来确定编码。
    猜你喜欢
    • 2015-03-15
    • 1970-01-01
    • 2021-11-11
    • 2010-10-26
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-10-31
    • 1970-01-01
    相关资源
    最近更新 更多