23

我正在尝试将 unicode 字符串输出为 RTF 格式。(使用 c# 和 winforms)

来自维基百科

如果需要 Unicode 转义,则使用控制字 \u,后跟一个 16 位有符号十进制整数,给出 Unicode 代码点编号。对于不支持 Unicode 的程序,它后面必须跟在指定代码页中该字符的最近表示。例如,\u1576? 将给出阿拉伯字母 beh,指定不支持 Unicode 的旧程序应将其呈现为问号。

我不知道如何将 Unicode 字符转换为 Unicode 代码点(“\u1576”)。转换为 UTF 8、UTF 16 和类似格式很容易,但我不知道如何转换为代码点。

我使用这个的场景:

  • 我将现有的 RTF 文件读入字符串(我正在阅读模板)
  • string.replace #TOKEN# with MyUnicodeString(模板填充数据)
  • 将结果写入另一个 RTF 文件。

问题,当 Unicode 字符到达时出现

4

4 回答 4

29

如果您要处理的所有字符都存在于基本多语言平面中(您不太可能需要更多字符),那么简单的 UTF-16 编码就足够了。

维基百科:

从 U+0000 到 U+10FFFF 的所有可能的代码点,除了代理代码点 U+D800–U+DFFF(它们不是字符),都由 UTF-16 唯一映射,而不管代码点的当前或未来字符分配如何或使用。

以下示例程序说明了按照您想要的方式进行的操作:

static void Main(string[] args)
{
    // ë
    char[] ca = Encoding.Unicode.GetChars(new byte[] { 0xeb, 0x00 });
    var sw = new StreamWriter(@"c:/helloworld.rtf");
    sw.WriteLine(@"{\rtf
{\fonttbl {\f0 Times New Roman;}}
\f0\fs60 H" + GetRtfUnicodeEscapedString(new String(ca)) + @"llo, World!
}"); 
    sw.Close();
}

static string GetRtfUnicodeEscapedString(string s)
{
    var sb = new StringBuilder();
    foreach (var c in s)
    {
        if (c <= 0x7f)
            sb.Append(c);
        else
            sb.Append("\\u" + Convert.ToUInt32(c) + "?");
    }
    return sb.ToString();
}

重要的是Convert.ToUInt32(c)它本质上返回了相关字符的代码点值。unicode 的 RTF 转义需要十进制 unicode 值。根据System.Text.Encoding.UnicodeMSDN 文档,编码对应于 UTF-16。

于 2009-09-02T14:39:12.960 回答
26

已接受答案的固定代码 - 添加特殊字符转义,如此链接中所述

static string GetRtfUnicodeEscapedString(string s)
{
    var sb = new StringBuilder();
    foreach (var c in s)
    {
        if(c == '\\' || c == '{' || c == '}')
            sb.Append(@"\" + c);
        else if (c <= 0x7f)
            sb.Append(c);
        else
            sb.Append("\\u" + Convert.ToUInt32(c) + "?");
    }
    return sb.ToString();
}
于 2012-04-03T07:11:21.807 回答
2

您必须将字符串转换为byte[]数组(使用Encoding.Unicode.GetBytes(string)),然后遍历该数组并将一个\u字符添加到您找到的所有 Unicode 字符中。然后,当您将数组转换回字符串时,您必须将 Unicode 字符保留为数字。

例如,如果您的数组如下所示:

byte[] unicodeData = new byte[] { 0x15, 0x76 };

它会变成:

// 5c = \, 75 = u
byte[] unicodeData = new byte[] { 0x5c, 0x75, 0x15, 0x76 };
于 2009-09-02T14:38:46.657 回答
0

根据规范,这里有一些经过测试和工作的java代码:

  public static String escape(String s){
        if (s == null) return s;

        int len = s.length();
        StringBuilder sb = new StringBuilder(len);
        for (int i = 0; i < len; i++){
            char c = s.charAt(i);
            if (c >= 0x20 && c < 0x80){
                if (c == '\\' || c == '{' || c == '}'){
                    sb.append('\\');
                }
                sb.append(c);
            }
            else if (c < 0x20 || (c >= 0x80 && c <= 0xFF)){
                sb.append("\'");
                sb.append(Integer.toHexString(c));
            }else{
                sb.append("\\u");
                sb.append((short)c);
                sb.append("??");//two bytes ignored
            }
        }
        return sb.toString();
 }

重要的是,您需要在转义的 uncode 之后附加 2 个字符(接近 unicode 字符或只使用 ?)。因为 unicode 占用 2 个字节。

规范还说,如果代码点大于 32767,您应该使用负值,但在我的测试中,如果您不使用负值也没关系。

这是规格:

\uN 此关键字表示单个 Unicode 字符,它没有基于当前 ANSI 代码页的等效 ANSI 表示。N 表示以十进制数表示的 Unicode 字符值。此关键字后紧跟 ANSI 表示中的等效字符。这样,老读者会忽略 \uN 关键字并正确地选择 ANSI 表示。当遇到这个关键字时,读者应该忽略接下来的 N 个字符,其中 N 对应于遇到的最后一个 \ucN 值。

与所有 RTF 关键字一样,可能存在关键字终止空格(在 ANSI 字符之前),该空格不计入要跳过的字符中。虽然这不太可能发生(或推荐),但 \bin 关键字、其参数和随后的二进制数据被视为一个字符以用于跳过目的。如果在扫描可跳过数据时遇到 RTF 范围分隔符(即左大括号或右大括号),则认为可跳过数据在分隔符之前结束。这使得读者可以执行一些基本的错误恢复。要在可跳过数据中包含 RTF 定界符,它必须使用适当的控制符号(即用反斜杠转义)表示为纯文本。出于计算可跳过字符的目的,任何 RTF 控制字或符号都被视为单个字符。

An RTF writer, when it encounters a Unicode character with no corresponding ANSI character, should output \uN followed by the best ANSI representation it can manage. Also, if the Unicode character translates into an ANSI character stream with count of bytes differing from the current Unicode Character Byte Count, it should emit the \ucN keyword prior to the \uN keyword to notify the reader of the change.

RTF control words generally accept signed 16-bit numbers as arguments. For this reason, Unicode values greater than 32767 must be expressed as negative number

于 2016-07-11T00:15:03.930 回答