如何将unicode字符串输出到RTF(使用C#)

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

来自维基百科 :

如果需要Unicode转义,则使用控制字\ u,然后使用16位带符号十进制整数,给出Unicode代码点编号。 为了没有Unicode支持的程序的好处,必须在指定的代码页中跟随此字符的最近表示。 例如,\ u1576? 会给出阿拉伯字母beh,指明没有Unicode支持的旧程序应该将其作为问号呈现。

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

我使用它的场景:

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

当Unicode字符到达时出现问题

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

维基百科:

除了代理代码点U + D800-U + DFFF(不是字符)之外,U + 0000到U + 10FFFF的所有可能代码点都由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值。 根据MSDN文档, System.Text.Encoding.Unicode编码对应于UTF-16。

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

 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(); } 

您必须将字符串转换为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 }; 

根据规范,这里有一些经过测试和工作的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(); } 

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

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

这是规格:

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

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

当RTF编写器遇到没有相应ANSI字符的Unicode字符时,应该输出\ uN,然后输出它可以管理的最佳ANSI表示。 此外,如果Unicode字符转换为ANSI字符流,其字节数与当前Unicode字符字节数不同,则应在\ uN关键字之前发出\ ucN关键字,以通知读者更改。

RTF控制字通常接受带符号的16位数字作为参数。 因此,必须将大于32767的Unicode值表示为负数