如何在C#中将字符串转换为RTF格式?

16

问题

如何将字符串 "Européen" 转换为 RTF 格式的字符串 "Europ\'e9en"?

[TestMethod]
public void Convert_A_Word_To_Rtf()
{
    // Arrange
    string word = "Européen";
    string expected = "Europ\'e9en";
    string actual = string.Empty;

    // Act
    // actual = ... // How?

    // Assert
    Assert.AreEqual(expected, actual);
}

我迄今为止找到的内容

RichTextBox

RichTextBox可以用于某些事情。例如:

RichTextBox richTextBox = new RichTextBox();
richTextBox.Text = "Européen";
string rtfFormattedString = richTextBox.Rtf;

但是,rtfFormattedString实际上是整个RTF格式的文档,而不仅仅是字符串“Europ\'e9en”。

Stackoverflow

Google

我在网上还找到了一堆其他资源,但没有一个完全解决我的问题。

答案

Brad Christie的答案

必须添加Trim()以删除result中前面的空格。除此之外,Brad Christie的解决方案似乎有效。

虽然我们必须对RichTextBox进行SubString和Trim处理才能获得RTF格式的字符串,但我现在会使用这个解决方案。

测试用例:

[TestMethod]
public void Test_To_Verify_Brad_Christies_Stackoverflow_Answer()
{
        Assert.AreEqual(@"Europ\'e9en", "Européen".ConvertToRtf());
        Assert.AreEqual(@"d\'e9finitif", "définitif".ConvertToRtf());
        Assert.AreEqual(@"\'e0", "à".ConvertToRtf());
        Assert.AreEqual(@"H\'e4user", "Häuser".ConvertToRtf());
        Assert.AreEqual(@"T\'fcren", "Türen".ConvertToRtf());
        Assert.AreEqual(@"B\'f6den", "Böden".ConvertToRtf());
}

逻辑作为扩展方法:

public static class StringExtensions
{
    public static string ConvertToRtf(this string value)
    {
        RichTextBox richTextBox = new RichTextBox();
        richTextBox.Text = value;
        int offset = richTextBox.Rtf.IndexOf(@"\f0\fs17") + 8; // offset = 118;
        int len = richTextBox.Rtf.LastIndexOf(@"\par") - offset;
        string result = richTextBox.Rtf.Substring(offset, len).Trim();
        return result;
    }
}

可能是一个重复的问题,与将RTF特殊字符输出为Unicode有关。 - Abe Miessler
@Abe Miessler:我看到了那个问题,并已将链接添加到我的问题上方。然而,我不太清楚它如何解决我的问题(它可能会,但我不理解)。您能否提供一个代码片段,使上述测试方法通过? - Lernkurve
请再次查看我的答案,我已经发布了一个(hacky)解决方案来回答你的问题。我希望你只是在翻译一些较小/简单的东西。 - Brad Christie
@Lernkurve:看到有人点赞,就想随便问一下,这个方法还有效吗?(我有点好奇这种方法是否不安全) - Brad Christie
@BradChristie:我无法告诉你这种方法有多不安全。我们没有检查它是否适用于每一种可能的情况。但由于我们不确定,我们为我们感兴趣的所有特殊字符编写了测试,并且对于那些测试,它表现得非常出色。 - Lernkurve
8个回答

9

RichTextBox的页眉/页脚总是相同的吗?您可以根据偏移位置读取内容,并继续使用它进行解析。(我认为?如果我错了,请纠正我)

虽然有可用的库,但个人经验并不好(尽管在完全耗尽可能性之前总是找到另一种方法)。此外,大多数更好的库通常包含名义费用。


编辑
有点hack,但这应该可以帮助您完成您需要完成的工作(我希望如此):

RichTextBox rich = new RichTextBox();
Console.Write(rich.Rtf);

String[] words = { "Européen", "Apple", "Carrot", "Touché", "Résumé", "A Européen eating an apple while writing his Résumé, Touché!" };
foreach (String word in words)
{
    rich.Text = word;
    Int32 offset = rich.Rtf.IndexOf(@"\f0\fs17") + 8;
    Int32 len = rich.Rtf.LastIndexOf(@"\par") - offset;
    Console.WriteLine("{0,-15} : {1}", word, rich.Rtf.Substring(offset, len).Trim());
}

编辑2

RTF控制代码的细分如下:

  • 页眉
    • \f0 - 使用0索引字体(列表中的第一个字体,通常是Microsoft Sans Serif(在标头的字体表中注意到:{\fonttbl{\f0\fnil\fcharset0 Microsoft Sans Serif;}}))
    • \fs17 - 字体格式,指定大小为17(17为半点)
  • 页脚
    • \par 指定为段落的结尾。

希望这些能够解决一些问题。;-)


@Brian:我的标题确实会改变。输出“Apple”和“Européen”的区别会导致标题发生变化。 - Brad Christie
@Brad Christie: 我的意思是,如果"\fo\fs17"发生了变化。我承认这很不可能。我想我只是不喜欢依赖于具体实现细节。 - Brian
@Brad Christie:我已经将你的代码添加到我的帖子中,看起来它可以工作。谢谢。:-) 我会在进一步尝试并弄清楚你实际在做什么之后回报。;-) 或许你可以帮助我解释一下@"\f0\fs17"和@"\par"代表什么,因为我不了解RTF规范。 - Lernkurve
没问题,很高兴能帮忙。我认为我使用8的原因是有道理的,只是没有修剪它。 (这两个修复已应用于我的答案)此外,我可以相当自信地说,这应该适用于许多情况。 只要您不涉及回车符,这就可以满足您的需求。 有点取巧,但考虑到最小的努力/费用,我认为这符合要求。;-) - Brad Christie
@Brad Christie:我完全同意你的观点,你说它在很多场景下都应该可以工作让我感到放心。;-) P.S.再看一下我答案中的测试用例:我添加了一些扩展方法的甜味。 - Lernkurve
显示剩余3条评论

5

我的单元测试显示您的转换代码存在问题。 - Lernkurve

5

这是我的做法:

private string ConvertString2RTF(string input)
{
    //first take care of special RTF chars
    StringBuilder backslashed = new StringBuilder(input);
    backslashed.Replace(@"\", @"\\");
    backslashed.Replace(@"{", @"\{");
    backslashed.Replace(@"}", @"\}");

    //then convert the string char by char
    StringBuilder sb = new StringBuilder();
    foreach (char character in backslashed.ToString())
    {
        if (character <= 0x7f)
            sb.Append(character);
        else
            sb.Append("\\u" + Convert.ToUInt32(character) + "?");
    }
    return sb.ToString();
}

我认为使用 RichTextBox 是:
1)过度设计
2)在花费数天时间尝试将其与 Word 中创建的 RTF 文档配合使用后,我不喜欢 RichTextBox


1

以下是一个将字符串转换为RTF字符串的丑陋示例:

class Program
{
    static RichTextBox generalRTF = new RichTextBox();

    static void Main()
    {
        string foo = @"Européen";
        string output = ToRtf(foo);
        Trace.WriteLine(output);
    }

    private static string ToRtf(string foo)
    {
        string bar = string.Format("!!@@!!{0}!!@@!!", foo);
        generalRTF.Text = bar;
        int pos1 = generalRTF.Rtf.IndexOf("!!@@!!");
        int pos2 = generalRTF.Rtf.LastIndexOf("!!@@!!");
        if (pos1 != -1 && pos2 != -1 && pos2 > pos1 + "!!@@!!".Length)
        {
            pos1 += "!!@@!!".Length;
            return generalRTF.Rtf.Substring(pos1, pos2 - pos1);
        }
        throw new Exception("Not sure how this happened...");
    }
}

感谢你花时间发布这段代码。我得好好看看它。那些惊叹号看起来有些吓人... - Lernkurve
2
@Lernkurve: "!!@@!!" 是一个任意的分隔符,真正应该放在 const String 中。 - Brian
谢谢解释。我应该自己看到那个的。 :-) - Lernkurve
1
@Lernkurve:基本上,我的解决方案和 Brad 的解决方案是相同的,但 Brad 使用 RichTextBox 控件的自然文本来分隔文本,而我则产生自己的分隔符。我还添加了一些错误检查,以防出现“不可能”的情况。这种检查是否合适是可以商榷的。 - Brian

1
我知道已经过了一段时间,希望这能有所帮助。
在尝试了我能找到的所有转换代码后,这段代码对我有效:
titleText和contentText是在常规文本框中填写的简单文本。
var rtb = new RichTextBox();
rtb.AppendText(titleText)
rtb.AppendText(Environment.NewLine);
rtb.AppendText(contentText)

rtb.Refresh();

rtb.rtf现在保存了rtf文本。

以下代码将保存rtf文本,并允许您打开文件、编辑它,然后再次将其加载回RichTextBox中:

rtb.SaveFile(path, RichTextBoxStreamType.RichText);

1
这是改进后的 @Vladislav Zalesak 的答案:

public static string ConvertToRtf(string text)
{
    // using default template from wiki
    StringBuilder sb = new StringBuilder(@"{\rtf1\ansi\ansicpg1250\deff0{\fonttbl\f0\fswiss Helvetica;}\f0\pard ");
    foreach (char character in text)
    {
        if (character <= 0x7f)
        {
            // escaping rtf characters
            switch (character)
            {
                case '\\':
                case '{':
                case '}':
                    sb.Append('\\');
                    break;
                case '\r':
                    sb.Append("\\par");
                    break;
            }

            sb.Append(character);
        }
        // converting special characters
        else
        {
            sb.Append("\\u" + Convert.ToUInt32(character) + "?");
        }
    }
    sb.Append("}");
    return sb.ToString();
}

0

这不是最优雅的方法,但相当优化和快速:

public static string PlainTextToRtf(string plainText)
{
    if (string.IsNullOrEmpty(plainText))
        return "";

    string escapedPlainText = plainText.Replace(@"\", @"\\").Replace("{", @"\{").Replace("}", @"\}");
    escapedPlainText = EncodeCharacters(escapedPlainText);

    string rtf = @"{\rtf1\ansi\ansicpg1250\deff0{\fonttbl\f0\fswiss Helvetica;}\f0\pard ";
    rtf += escapedPlainText.Replace(Environment.NewLine, "\\par\r\n ") + ;
    rtf += " }";
    return rtf;
}

.

编码字符(波兰字符)方法:

private static string EncodeCharacters(string text)
{
    if (string.IsNullOrEmpty(text))
        return "";

    return text
        .Replace("ą", @"\'b9")
        .Replace("ć", @"\'e6")
        .Replace("ę", @"\'ea")
        .Replace("ł", @"\'b3")
        .Replace("ń", @"\'f1")
        .Replace("ó", @"\'f3")
        .Replace("ś", @"\'9c")
        .Replace("ź", @"\'9f")
        .Replace("ż", @"\'bf")
        .Replace("Ą", @"\'a5")
        .Replace("Ć", @"\'c6")
        .Replace("Ę", @"\'ca")
        .Replace("Ł", @"\'a3")
        .Replace("Ń", @"\'d1")
        .Replace("Ó", @"\'d3")
        .Replace("Ś", @"\'8c")
        .Replace("Ź", @"\'8f")
        .Replace("Ż", @"\'af");
}

通过你的解决方案,你如何知道已经涵盖了所有可能的特殊字符?如果一个字符不在你的EncodeCharacters的替换列表中,那么它会变得错误,对吗? - Lernkurve
在一些罕见的情况下,这个小技巧可能是最后的救命稻草。 - Jan and RESTless

0
private static string ConvertToRtf(string text)
{
    // Create a regular expression pattern to match non-ASCII characters
    string pattern = "[^\x00-\x7F]";
    // Use Regex.Replace to escape non-ASCII characters
    return Regex.Replace(text, pattern, m => m.Value[0] > 255 ? @"\u" + ((int)m.Value[0]).ToString() + "?" : @"\'" + ((int)m.Value[0]).ToString("X2").ToLowerInvariant());
}

目前你的回答不够清晰,请编辑并添加更多细节,以帮助其他人理解它如何回答问题。你可以在帮助中心找到有关如何编写好答案的更多信息。 - Community

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