ZPL打印机上的Unicode字符

22

我有一个任务,需要重新设计一个使用联网Zebra GK420T打印运输标签的系统。我已成功发送了ZPL打印作业,但似乎无法打印Unicode字符(如西里尔字母)。我使用Seagull Scientific驱动程序将Lucida Sans Unicode字体下载到打印机上,并使用以下ZPL代码进行测试:

^XA
^LH100,150
^CI28
^FT0,0^A@N,50,50,R:LUCIDASR.FNT^CI28^FDTesting 1 2 3^FS
^FT0,50^A@N,50,50,R:LUCIDASR.FNT^CI28^FDДо свидания^FS
^FT0,100^B3^FDAAA001^FS
^XZ

它将打印“Testing 1 2 3”和条形码,但是在克里尔字母处留下了空白。

我还尝试使用Zebra瑞士Unicode字体,现在它会将俄语字符打印为问号:

^XA
^LH100,150
^CWT,E:TT0003M_.FNT
^CFT,30,30
^CI28
^FT0,0^FDTesting 1 2 3^FS
^FT0,50^FDДо свидания^FS
^FT0,100^B3^FDAAA001^FS
^XZ

我是否做错了什么,比如没有转义字符或者是打印机的问题?

12个回答

26

我刚刚发现,如果要在ASCII之上转义字符,则需要先在可能包含utf字符的任何^FD字段之前放置^FH,并且需要在utf-8十六进制代码前加下划线。

_D0_94将打印为Д。我的最终ZPL代码如下:

^XA
^LH100,150
^CWT,E:TT0003M_.FNT
^CFT,30,30
^CI28
^FT0,0^FH^FDTesting 1 2 3^FS
^FT0,50^FH^FD_D0_94_D0_BE _D1_81_D0_B2_D0_B8_D0_B4_D0_B0_D0_BD_D0_B8_D1_8F^FS
^FT0,100^B3^FDAAA001^FS
^XZ

我只需要想办法生成转义序列,这应该会容易得多!


7
请注意,它可以不需要转义就能正常工作。你只需要将要发送到打印机的文件或字符串编码为 UTF-8 即可。在 Zebra GK420t 上进行了测试。 - Mak Sim
11
值得一提的是,^CI命令确定编码方式。如果你的文本是utf-8格式,请确保你的zpl代码有^CI28 - Lidor
1
@Lidor非常抱歉,但我无法让^CI28输出任何内容!我尝试了一个简单的^XA^CI28^A0N,34^FH^FVR_F6BIN^XZ,应该打印出“RÖBIN”,但实际上只有“RBIN”。有什么建议吗? - Robin Jonsson
1
@RobinJonsson 难道应该是 _C3_96 而不是 _F6 吗?您也可以尝试直接输入 Ö 而不需要转义。 - Lidor
1
@RobinJonsson 试着看看 ^CI 命令文档^CI31 或许是你需要的。 - Lidor
显示剩余5条评论

14

我曾经遇到同样的问题,您应该在任何包含特殊字符的^FD(字段数据)命令之前添加一个^FH(字段十六进制指示器),在我的情况下,我需要使用西班牙字符,因此我必须使用^CI28(更改国际字体/编码)。

UTF 8 HEX代码列表

示例:要打印Alvaro Jesús Pérez Peñaranda,我们需要将这些特殊字符转换为UTF 8 Hex代码,并在每个代码前面添加一个_,结果如下:Alvaro Jes_c3_bas P_c3_a9rez Pe_c3_b1aranda

^XA

^CI28
^FO60,75
^ASN,36,20^FH^FDAlvaro Jes_c3_bas P_c3_a9rez Pe_c3_b1aranda^FS

^XZ

1
这看起来是我需要的。您如何将字符串转换为此特殊十六进制?您是从Android运行它吗? - Angel G. Olloqui
查看ZPL II文档,CI范围仅限于26(在^CI“更改国际字体”下的第109页)。 - Gary Richter

8

我正在使用斑马ZM400打印机,并使用TT0003M_字体。

这种字体无法打印哈萨克文西里尔字母。

如果您想要打印西里尔字母+哈萨克文西里尔字母+拉丁字母,请使用ARI000.FNT(Arial字体)。

我正在使用以下方法将字符转换为十六进制代码。

希望这有所帮助。

stringConverTextToHex(stringtext)
{
stringnewText="";
char[]charArray=text.ToCharArray();
foreach(charcincharArray)
{
switch(c)
{
case'й':
newText+="_D0_B9";
break;
case'Й':
newText+="_D0_99";
break;
case'ц':
newText+="_D1_86";
break;
case'Ц':
newText+="_D0_A6";
break;
case'у':
newText+="_D1_83";
break;
case'У':
newText+="_D0_A3";
break;
case'к':
newText+="_D0_BA";
break;
case'К':
newText+="_D0_9A";
break;
case'е':
newText+="_D0_B5";
break;
case'Е':
newText+="_D0_95";
break;
case'н':
newText+="_D0_BD";
break;
case'Н':
newText+="_D0_9D";
break;
case'г':
newText+="_D0_B3";
break;
case'Г':
newText+="_D0_93";
break;
case'ш':
newText+="_D1_88";
break;
case'Ш':
newText+="_D0_A8";
break;
case'щ':
newText+="_D1_89";
break;
case'Щ':
newText+="_D0_A9";
break;
case'з':
newText+="_D0_B7";
break;
case'З':
newText+="_D0_97";
break;
case'х':
newText+="_D1_85";
break;
case'Х':
newText+="_D0_A5";
break;
case'ъ':
newText+="_D1_8A";
break;
case'Ъ':
newText+="_D0_AA";
break;
case'ф':
newText+="_D1_84";
break;
case'Ф':
newText+="_D0_A4";
break;
case'ы':
newText+="_D1_8B";
break;
case'Ы':
newText+="_D0_AB";
break;
case'в':
newText+="_D0_B2";
break;
case'В':
newText+="_D0_92";
break;
case'а':
newText+="_D0_B0";
break;
case'А':
newText+="_D0_90";
break;
case'п':
newText+="_D0_BF";
break;
case'П':
newText+="_D0_9F";
break;
case'р':
newText+="_D1_80";
break;
case'Р':
newText+="_D0_A0";
break;
case'о':
newText+="_D0_BE";
break;
case'О':
newText+="_D0_9E";
break;
case'л':
newText+="_D0_BB";
break;
case'Л':
newText+="_D0_9B";
break;
case'д':
newText+="_D0_B4";
break;
case'Д':
newText+="_D0_94";
break;
case'ж':
newText+="_D0_B6";
break;
case'Ж':
newText+="_D0_96";
break;
case'э':
newText+="_D1_8D";
break;
case'Э':
newText+="_D0_AD";
break;
case'я':
newText+="_D1_8F";
break;
case'Я':
newText+="_D0_AF";
break;
case'ч':
newText+="_D1_87";
break;
case'Ч':
newText+="_D0_A7";
break;
case'с':
newText+="_D1_81";
break;
case'С':
newText+="_D0_A1";
break;
case'м':
newText+="_D0_BC";
break;
case'М':
newText+="_D0_9C";
break;
case'и':
newText+="_D0_B8";
break;
case'И':
newText+="_D0_98";
break;
case'т':
newText+="_D1_82";
break;
case'Т':
newText+="_D0_A2";
break;
case'ь':
newText+="_D1_8C";
break;
case'Ь':
newText+="_D0_AC";
break;
case'б':
newText+="_D0_B1";
break;
case'Б':
newText+="_D0_91";
break;
case'ю':
newText+="_D1_8E";
break;
case'Ю':
newText+="_D0_AE";
break;
case'ӑ':
newText+="_D3_91";
break;
case'Ӑ':
newText+="_D3_90";
break;
case'ӓ':
newText+="_D3_93";
break;
case'Ӓ':
newText+="_D3_92";
break;
case'ә':
newText+="_D3_99";
break;
case'Ә':
newText+="_D3_98";
break;
case'ӛ':
newText+="_D3_9B";
break;
case'Ӛ':
newText+="_D3_9A";
break;
case'ӕ':
newText+="_D3_95";
break;
case'Ӕ':
newText+="_D3_94";
break;
case'ґ':
newText+="_D2_91";
break;
case'Ґ':
newText+="_D2_90";
break;
case'ѓ':
newText+="_D1_93";
break;
case'Ѓ':
newText+="_D0_83";
break;
case'ғ':
newText+="_D2_93";
break;
case'Ғ':
newText+="_D2_92";
break;
case'ӷ':
newText+="_D3_B7";
break;
case'Ӷ':
newText+="_D3_B6";
break;
case'ҕ':
newText+="_D2_95";
break;
case'Ҕ':
newText+="_D2_94";
break;
case'ђ':
newText+="_D1_92";
break;
case'Ђ':
newText+="_D0_82";
break;
case'ѐ':
newText+="_D1_90";
break;
case'Ѐ':
newText+="_D0_80";
break;
case'ӗ':
newText+="_D3_97";
break;
case'Ӗ':
newText+="_D3_96";
break;
case'ҽ':
newText+="_D2_BD";
break;
case'Ҽ':
newText+="_D2_BC";
break;
case'ҿ':
newText+="_D2_BF";
break;
case'Ҿ':
newText+="_D2_BE";
break;
case'є':
newText+="_D1_94";
break;
case'Є':
newText+="_D0_84";
break;
case'ӂ':
newText+="_D3_82";
break;
case'Ӂ':
newText+="_D3_81";
break;
case'җ':
newText+="_D2_97";
break;
case'Җ':
newText+="_D2_96";
break;
case'ӝ':
newText+="_D3_9D";
break;
case'Ӝ':
newText+="_D3_9C";
break;
case'ҙ':
newText+="_D2_99";
break;
case'Ҙ':
newText+="_D2_98";
break;
case'ӟ':
newText+="_D3_9F";
break;
case'Ӟ':
newText+="_D3_9E";
break;
case'ӡ':
newText+="_D3_A1";
break;
case'Ӡ':
newText+="_D3_A0";
break;
case'ѕ':
newText+="_D1_95";
break;
case'Ѕ':
newText+="_D0_85";
break;
case'ѝ':
newText+="_D1_9D";
break;
case'Ѝ':
newText+="_D0_8D";
break;
case'ӥ':
newText+="_D3_A5";
break;
case'Ӥ':
newText+="_D3_A4";
break;
case'ӣ':
newText+="_D3_A3";
break;
case'Ӣ':
newText+="_D3_A2";
break;
case'і':
newText+="_D1_96";
break;
case'І':
newText+="_D0_86";
break;
case'ї':
newText+="_D1_97";
break;
case'Ї':
newText+="_D0_87";
break;
case'Ӏ':
newText+="_D3_80";
break;
case'ҋ':
newText+="_D2_8B";
break;
case'Ҋ':
newText+="_D2_8A";
break;
case'ј':
newText+="_D1_98";
break;
case'Ј':
newText+="_D0_88";
break;
case'қ':
newText+="_D2_9B";
break;
case'Қ':
newText+="_D2_9A";
break;
case'ҟ':
newText+="_D2_9F";
break;
case'Ҟ':
newText+="_D2_9E";
break;
case'ҡ':
newText+="_D2_A1";
break;
case'Ҡ':
newText+="_D2_A0";
break;
case'ӄ':
newText+="_D3_84";
break;
case'Ӄ':
newText+="_D3_83";
break;
case'ҝ':
newText+="_D2_9D";
break;
case'Ҝ':
newText+="_D2_9C";
break;
case'ӆ':
newText+="_D3_86";
break;
case'Ӆ':
newText+="_D3_85";
break;
case'љ':
newText+="_D1_99";
break;
case'Љ':
newText+="_D0_89";
break;
case'ӎ':
newText+="_D3_8E";
break;
case'Ӎ':
newText+="_D3_8D";
break;
case'ӊ':
newText+="_D3_8A";
break;
case'Ӊ':
newText+="_D3_89";
break;
case'ң':
newText+="_D2_A3";
break;
case'Ң':
newText+="_D2_A2";
break;
case'ӈ':
newText+="_D3_88";
break;
case'Ӈ':
newText+="_D3_87";
break;
case'ҥ':
newText+="_D2_A5";
break;
case'Ҥ':
newText+="_D2_A4";
break;
case'њ':
newText+="_D1_9A";
break;
case'Њ':
newText+="_D0_8A";
break;
case'ӧ':
newText+="_D3_A7";
break;
case'Ӧ':
newText+="_D3_A6";
break;
case'ө':
newText+="_D3_A9";
break;
case'Ө':
newText+="_D3_A8";
break;
case'ӫ':
newText+="_D3_AB";
break;
case'Ӫ':
newText+="_D3_AA";
break;
case'ҩ':
newText+="_D2_A9";
break;
case'Ҩ':
newText+="_D2_A8";
break;
case'ҧ':
newText+="_D2_A7";
break;
case'Ҧ':
newText+="_D2_A6";
break;
case'ҏ':
newText+="_D2_8F";
break;
case'Ҏ':
newText+="_D2_8E";
break;
case'ҫ':
newText+="_D2_AB";
break;
case'Ҫ':
newText+="_D2_AA";
break;
case'ҭ':
newText+="_D2_AD";
break;
case'Ҭ':
newText+="_D2_AC";
break;
case'ћ':
newText+="_D1_9B";
break;
case'Ћ':
newText+="_D0_8B";
break;
case'ќ':
newText+="_D1_9C";
break;
case'Ќ':
newText+="_D0_8C";
break;
case'ў':
newText+="_D1_9E";
break;
case'Ў':
newText+="_D0_8E";
break;
case'ӳ':
newText+="_D3_B3";
break;
case'Ӳ':
newText+="_D3_B2";
break;
case'ӱ':
newText+="_D3_B1";
break;
case'Ӱ':
newText+="_D3_B0";
break;
case'ӯ':
newText+="_D3_AF";
break;
case'Ӯ':
newText+="_D3_AE";
break;
case'ү':
newText+="_D2_AF";
break;
case'Ү':
newText+="_D2_AE";
break;
case'ұ':
newText+="_D2_B1";
break;
case'Ұ':
newText+="_D2_B0";
break;
case'ҳ':
newText+="_D2_B3";
break;
case'Ҳ':
newText+="_D2_B2";
break;
case'һ':
newText+="_D2_BB";
break;
case'Һ':
newText+="_D2_BA";
break;
case'ҵ':
newText+="_D2_B5";
break;
case'Ҵ':
newText+="_D2_B4";
break;
case'ӵ':
newText+="_D3_B5";
break;
case'Ӵ':
newText+="_D3_B4";
break;
case'ҷ':
newText+="_D2_B7";
break;
case'Ҷ':
newText+="_D2_B6";
break;
case'ӌ':
newText+="_D3_8C";
break;
case'Ӌ':
newText+="_D3_8B";
break;
case'ҹ':
newText+="_D2_B9";
break;
case'Ҹ':
newText+="_D2_B8";
break;
case'џ':
newText+="_D1_9F";
break;
case'Џ':
newText+="_D0_8F";
break;
case'ӹ':
newText+="_D3_B9";
break;
case'Ӹ':
newText+="_D3_B8";
break;
case'ҍ':
newText+="_D2_8D";
break;
case'Ҍ':
newText+="_D2_8C";
break;
case'ӭ':
newText+="_D3_AD";
break;
case'Ӭ':
newText+="_D3_AC";
break;
case'A':
newText+="_41";
break;
case'a':
newText+="_61";
break;
case'B':
newText+="_42";
break;
case'b':
newText+="_62";
break;
case'C':
newText+="_43";
break;
case'c':
newText+="_63";
break;
case'D':
newText+="_44";
break;
case'd':
newText+="_64";
break;
case'E':
newText+="_45";
break;
case'e':
newText+="_65";
break;
case'F':
newText+="_46";
break;
case'f':
newText+="_66";
break;
case'G':
newText+="_47";
break;
case'g':
newText+="_67";
break;
case'H':
newText+="_48";
break;
case'h':
newText+="_68";
break;
case'I':
newText+="_49";
break;
case'i':
newText+="_69";
break;
case'J':
newText+="_4A";
break;
case'j':
newText+="_6A";
break;
case'K':
newText+="_4B";
break;
case'k':
newText+="_6B";
break;
case'L':
newText+="_4C";
break;
case'l':
newText+="_6C";
break;
case'M':
newText+="_4D";
break;
case'm':
newText+="_6D";
break;
case'N':
newText+="_4E";
break;
case'n':
newText+="_6E";
break;
case'O':
newText+="_4F";
break;
case'o':
newText+="_6F";
break;
case'P':
newText+="_50";
break;
case'p':
newText+="_70";
break;
case'R':
newText+="_52";
break;
case'r':
newText+="_72";
break;
case'S':
newText+="_53";
break;
case's':
newText+="_73";
break;
case'T':
newText+="_54";
break;
case't':
newText+="_74";
break;
case'U':
newText+="_55";
break;
case'u':
newText+="_75";
break;
case'V':
newText+="_56";
break;
case'v':
newText+="_76";
break;
case'Y':
newText+="_59";
break;
case'y':
newText+="_79";
break;
case'Z':
newText+="_5A";
break;
case'z':
newText+="_7A";
break;
case'':
newText+="";
break;
default:
newText+=c;
break;
}
}
returnnewText;
}

这是示例代码


^SP ^XA ^PON^FS ^FPH^FO102,63,0 ^A@N,60,60,E:ARIOOO_.FNT ^FH^FD_42_75_72_61_6B _D0_A8_D3_99 ^FS ^XZ



大多数斑马打印机不支持西里尔字母、哈萨克斯坦西里尔字母和拉丁字母。您的函数stringConverTextToHex非常有帮助。感谢您的回答。 - AMIC MING

5
俄语和许多其他字符可以使用免费的Zebra swiss unicode font进行打印。它已经作为TT0003M_包含在大多数打印机中,并支持罗马、西里尔、东欧、土耳其、阿拉伯、希伯来等语言。
对于打印日语或中文等拥有数千个字符的语言,您需要一台至少具有23 MB空闲内存和可以上传(他们称其为下载)TrueType字体文件的打印机。
这个文件可以从Zebra购买(他们说你需要64 MB),但我也在我的Windows 7系统的Fonts文件夹中找到了一个非常古老的TTF文件:ARIALUNI.TTF 1.01(23.275.812字节),Arial Unicode MS。它是由MS Office安装程序安装的,可能没有被许可用于此用途。
很可能您也可以使用其他TTF文件,但我只尝试过这一个。
虽然在这台Zebra打印机上进行ZPL打印时无需任何原始驱动程序(仅通用文本),但对于字体安装,需要驱动程序。如果有人知道如何在没有驱动程序的情况下将TTF文件发送到打印机,请发表评论。
我安装了Zebra设置实用程序,其中包括一个字体下载器。点击“新建”,然后添加字体(必须已安装在系统中),忽略226个字符的消息。还要忽略如果您使用Unicode字符配置测试字符串,它将无法正确显示的信息。您会被询问是否立即下载,这需要花费很长时间。
您可以通过列出目录内容(管理网页或打印输出)来检查安装情况。在我的情况下,该字体显示为ARI000.TTF。

要打印,您需要将ZPL文本作为UTF-8发送。您可以将此示例复制到记事本中,并在保存对话框中选择UTF-8:

^XA
^LH100,150
^CWT,E:ARI000.FNT
^CFT,30,30
^CI28
^FT0,0^FH^FDyour unicode characters here^FS
^XZ

然后,为了测试,您可以使用简单的复制命令将其发送到打印机: 在USB的情况下,您需要首先在网络中共享此打印机。 然后使用net use lpt1: \\localhost\sharenamecopy file.txt lpt1 我们使用许多常见的日语和汉字符号进行了测试,在具有32 MB闪存的ZT230打印机上以高质量工作得非常好。

3

您可以将一个或多个大于一个字节的字符替换为下划线后的UTF-8十六进制字符串,例如"ћ => _D1_9B"。以下是示例代码:

 var zpl_code = "^XA" +
        "^LH100,150" +
        "^CWT,E:TT0003M_.FNT" +
        "^CFT,30,30" +
        "^CI28" +
        "^FT0,0^FDTesting 1 2 3^FS" +
        "^FT0,50^FDДо свидания^FS" +
        "^FT0,100^B3^FDAAA001^FS" +
        "^XZ";
        var unicodeCharacterList = zpl_code.Distinct()
            .Select(c => c.ToString())
            .Select(c => new { key = c, UTF8Bytes = Encoding.UTF8.GetBytes(c) })
            .Where(c => c.UTF8Bytes.Length > 1);

        foreach (var character in unicodeCharacterList)
        {
            var characterHexCode = string.Join("", character.UTF8Bytes.Select(c => "_" + BitConverter.ToString(new byte[] { c }).ToLower()).ToArray());

            zpl_code = zpl_code.Replace(character.key, characterHexCode);
        }

这段代码将zpl_code变量设置为以下输出

^XA
^LH100,150
^CWT,E:TT0003M_.FNT
^CFT,30,30
^CI28
^FT0,0^FDTesting 1 2 3^FS
^FT0,50^FD_d0_94_d0_be _d1_81_d0_b2_d0_b8_d0_b4_d0_b0_d0_bd_d0_b8_d1_8f^FS
^FT0,100^B3^FDAAA001^FS
^XZ

2
在您的示例ZPL代码中,您忘记了^FH语句。第7行应为:^FT0,50^FH^FD_d0_94_d0_be _d1_81_d0_b2_d0_b8_d0_b4_d0_b0_d0_bd_d0_b8_d1_8f^FS - Lutz
为什么输出结果中没有包含 ^FH? - Florjon

3

在最新的固件版本(自 v x.16.x 起),您可以使用 ^CI33 来处理编码为 Windows-1251 的文本(以及其他编码),无需使用 ^FH。请参阅手册


3

如果你想使用:TT0003M_.FNT打印俄语西里尔字母,你需要将命令保存到UTF-8编码的文件中!

^XA
^LH100,150
^CWT,E:TT0003M_.FNT
^CFT,30,30
^CI28
^FT0,0^FH^FDTesting 1 2 3^FS
^FT0,30^FH^FDДо свидания^FS
^FT0,100^B3^FDAAA001^FS
^XZ

然后,使用命令行将其发送到打印机端口。 例如:复制C:\ Users \ xxx \ Desktop \ test_ru.txt com1
我希望这能帮助...

这个方便的复制命令也适用于USB和LAN连接。只需使用“net use lpt1: \server\printer”。 - maf-soft

3

你的“До свидания”可能是以cp1251编码。将其编码为实际的UTF-8并重试。空格是您存在编码问题的良好指示器。

使用v56.17.112固件和^A@N,,,E:TT0003M_.FNT进行验证。


这是最好的答案。真的没有必要使用十六进制代码或其他什么。你可以直接发送真正的Unicode字符,它就能工作。对于日文和中文字符,这种字体不起作用。但我也找到了一个免费的解决方案。让我看看在哪里发布它...会在这里更新。 - maf-soft

2
我采用了Dysnomin的答案并将其转换为Javascript。他的答案是用C#写的。
要运行它,只需取出您的字符串对象并调用variable.zplHexEncode(),其中变量是您的字符串。这默认为下划线作为转义字符。您仍然需要在所有^FD字段前加上^FH命令。个人而言,我使用类似doT模块的东西来创建我的ZPL,并使用转义字符填充值。YMMV。

   const _Last1ByteCode = 0x7E;
   const _First2ByteCode = 0xA0;
   const _Last2ByteCode = 0xBF;
   const _Last3ByteCode = 0xFF;
   const _3ByteOffset = 0x40;
   const _ZplEscapeCharacter = '_';
   const _2BytePre = 'c2';
   const _3BytePre = 'c3';


String.prototype.zplHexEncode  = function(){
    var hex, i, escHex;
    var result = "";
    for (i=0; i< this.length; i++) {
        var charCode = this.charCodeAt(i);

        if (charCode <= _Last1ByteCode)
            result += String.fromCharCode(charCode);
        else if (charCode >= _First2ByteCode && charCode <=_Last2ByteCode) {
            hex = charCode.toString(16);
            escHex =  ("0"+hex).slice(-2);
            result += _ZplEscapeCharacter+_2BytePre+_ZplEscapeCharacter+escHex; 
        }
        else if (charCode > _Last2ByteCode && charCode <=_Last3ByteCode) {
            charCode = charCode - _3ByteOffset;
            hex = charCode.toString(16);
            escHex =  ("0"+hex).slice(-2);
            result += _ZplEscapeCharacter+_3BytePre+_ZplEscapeCharacter+escHex; 
        }
        else
            result += '';
    }
    return result
}

var str = "This is a test with a unicode character¿";

console.log(str.zplHexEncode());


格鲁吉亚语的第一个和最后一个字节码是什么? - SMahdiS

1
正如其他人所指出的,确保使用^CI28(更改国际字体/编码)和^FH(字段十六进制指示符),并使用下划线和它们的十六进制值转义任何非ASCII utf8字符。
然而,另一个答案包括使用巨大的switch-case块格式化utf8字符串的代码。这是我用于编码为utf8的方法,它应该能够格式化任何有效的utf8字节数组。
要从字符串中获取字节数组,请使用Encoding.UTF8.GetBytes(content)
// From the wikipedia page on utf8 encoding - https://en.wikipedia.org/wiki/UTF-8
    private const int _Last1ByteCodePointByte1 = 0x7F;
    private const int _First2ByteCodePointByte1 = 0xC0;
    private const int _Last2ByteCodePointByte1 = 0xDF;
    private const int _Last3ByteCodePointByte1 = 0xEF;
    private const int _Last4ByteCodePointByte1 = 0xF7;
    private const int _FirstMultiByteCodePointByte2 = 0x80;
    private const int _LastMultiByteCodePointByte2 = 0xBF;
    private const char _ZplMultiByteEscapeCharacter = '_';

/// <summary>
/// Encodes a sequence of utf8 bytes for printing with the ZPL language, this means escaping multi-byte characters with an underscore ('_') followed by the hex code
/// for each byte in the multi-byte characters.
/// </summary>
/// <param name="utf8Bytes">The bytes that make up the entire string, including bytes that need to be encoded and bytes that can be printed as-is.</param>
/// <returns>A string for printing with the ZPL language. Ie all multi-byte characters escaped with an underscore ('_') followed by the hex code for each byte.</returns>
/// <throws><see cref="ArgumentException"/> when <paramref name="utf8Bytes"/> isn't a valid utf8 encoding of a string.</throws>
/// <remarks>
/// Plan is to figure out how many bytes this character (code point) takes up, and if it's a 1 byte character, just use the character, but otherwise since it's a multi-byte 
/// character then use an underscore ('_') followed by the hex encoded byte and each other byte in this code point will also be encoded. If we start the loop but have bytes 
/// remaining in the current code point we know to hex encode this byte and continue.
/// </remarks>
private static string EncodeUtf8BytesForZPLIIPrinting(byte[] utf8Bytes)
{
    var contentWithMultiByteCharsEscaped = new List<char>();

    var multiByteCodePoint = new List<char>();
    var remainingBytesInCurrentCodePoint = 0;
    string errorMessage = null;

    foreach (byte utf8Byte in utf8Bytes)
    {
        if (remainingBytesInCurrentCodePoint > 0)
        {
            if (utf8Byte < _FirstMultiByteCodePointByte2 || utf8Byte > _LastMultiByteCodePointByte2)
            {
                errorMessage = $"The byte {utf8Byte.ToString("X2")} is not a valid as the second or later byte of a multi-byte utf8 character (codepoint).";
                break;
            }

            multiByteCodePoint.Add(_ZplMultiByteEscapeCharacter);
            AddHexValuesToListFromByte(multiByteCodePoint, utf8Byte);
            remainingBytesInCurrentCodePoint--;
            continue; // continue since we've dealt with this byte and don't want to flow on.
        }

        if (multiByteCodePoint.Any())
        {
            foreach (char c in multiByteCodePoint) contentWithMultiByteCharsEscaped.Add(c);
            multiByteCodePoint.Clear();
            // flow on to loop to see what to do with the current byte.
        }

        if (utf8Byte <= _Last1ByteCodePointByte1)
        {
            // 1 byte - no escaping
            contentWithMultiByteCharsEscaped.Add((char)utf8Byte);
        }
        else if (utf8Byte >= _First2ByteCodePointByte1 && utf8Byte <= _Last2ByteCodePointByte1)
        {
            // 2 bytes
            multiByteCodePoint.Add(_ZplMultiByteEscapeCharacter);
            AddHexValuesToListFromByte(multiByteCodePoint, utf8Byte);
            remainingBytesInCurrentCodePoint = 1;
        }
        else if (utf8Byte <= _Last3ByteCodePointByte1)
        {
            // 3 bytes
            multiByteCodePoint.Add(_ZplMultiByteEscapeCharacter);
            AddHexValuesToListFromByte(multiByteCodePoint, utf8Byte);
            remainingBytesInCurrentCodePoint = 2;
        }
        else if (utf8Byte <= _Last4ByteCodePointByte1)
        {
            // 4 bytes
            multiByteCodePoint.Add(_ZplMultiByteEscapeCharacter);
            AddHexValuesToListFromByte(multiByteCodePoint, utf8Byte);
            remainingBytesInCurrentCodePoint = 3;
        }
        else
        {
            errorMessage = $"The byte {utf8Byte.ToString("X2")} is not a valid as the first byte of a utf8 character.";
            break;
        }
    }

    // if the last char was multiByte add it now.
    if (multiByteCodePoint.Any())
    {
        foreach (var c in multiByteCodePoint) contentWithMultiByteCharsEscaped.Add(c);
        multiByteCodePoint.Clear();
    }

    if (remainingBytesInCurrentCodePoint != 0 && errorMessage == null)
    {
        errorMessage = $"The last character didn't have enough bytes to finish the codepoint. It was a multi-byte character that needed {remainingBytesInCurrentCodePoint}" + 
            $" more byte{(remainingBytesInCurrentCodePoint == 1 ? null : "s")}.";
    }

    if (errorMessage != null)
    {
        throw new ArgumentException($"The byte array was not a valid byte array for a utf8 string: {errorMessage}", nameof(utf8Bytes));
    }

    return new string(contentWithMultiByteCharsEscaped.ToArray());


    void AddHexValuesToListFromByte(List<char> list, byte @byte)
    {
        // A byte is <= 255 so will always fit in a 2-digit hex number, hence the 2 in "X2". The X means hex.
        foreach (char c in @byte.ToString("X2"))
        {
            list.Add(c);
        }
    }
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接