Подтвердить что ты не робот

Как выводить строку Unicode в RTF (используя С#)

Я пытаюсь вывести строку unicode в формат RTF. (используя С# и winforms)

Из википедии:

Если требуется побег в Unicode, используется управляющее слово \u, за которым следует 16-разрядное знаковое десятичное целое число, указывающее номер кода Unicode. Для программ без поддержки Unicode это должно сопровождаться ближайшим представлением этого символа на указанной кодовой странице. Например, \u1576? даст арабский буквенный характер, указав, что более старые программы, не поддерживающие Unicode, должны сделать это как знак вопроса.

Я не знаю, как преобразовать символ Unicode в код Unicode ( "\ u1576" ). Преобразование в UTF 8, UTF 16 и тому подобное легко, но я не знаю, как преобразовать в код.

Сценарий, в котором я использую это:

  • Я читаю существующий файл RTF в строку (я читаю шаблон)
  • string.replace # TOKEN # с MyUnicodeString (шаблон заполняется данными)
  • записать результат в другой RTF файл.

Проблема возникает при появлении символов Unicode

4b9b3361

Ответ 1

При условии, что все символы, которые вы обслуживаете, существуют в Basic Multilingual Plane (вряд ли вам понадобится что-то еще), тогда достаточно простой кодировки 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), который по существу возвращает значение кодовой точки для рассматриваемого символа. Вывод RTF для юникода требует десятичного значения юникода. Кодировка System.Text.Encoding.Unicode соответствует UTF-16 в соответствии с документацией MSDN.

Ответ 2

Исправлен код из принятого ответа - добавлен специальный символ escaping, как описано в этой ссылка

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

Ответ 3

Вам нужно будет преобразовать строку в массив 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 };

Ответ 4

Основываясь на спецификации, вот какой код в 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 символа (рядом с символом юникода или просто использовать?) после того, как экранированный экран исчез. потому что юникод занимает 2 байта.

Также спецификация говорит, что вы должны использовать отрицательное значение, если точка кода больше 32767, но в моем тесте это нормально, если вы не используете отрицательное значение.

Вот спецификация:

\ uN Это ключевое слово представляет собой единственный символ Юникода, который не имеет эквивалентного представления ANSI на основе текущей кодовой страницы ANSI. N представляет собой символьное значение Unicode, выраженное в виде десятичного числа. Это ключевое слово немедленно сопровождается эквивалентным символом в представлении ANSI. Таким образом, старые читатели будут игнорировать ключевое слово \uN и правильно отображать представление ANSI. Когда это ключевое слово встречается, читатель должен игнорировать следующие N символов, где N соответствует последнему найденному значению \ucN.

Как и во всех ключевых словах RTF, может присутствовать слово-заканчивающееся пространство (перед символами ANSI), которое не засчитывается в символы для пропуска. Хотя это вряд ли произойдет (или рекомендуется), ключевое слово \bin, его аргумент и последующие двоичные данные считаются одним символом для пропущенных целей. Если при сканировании сбрасываемых данных встречается символ ограничителя области RTF (то есть открывающая или закрывающая скобка), данные с возможностью пропуска, как считается, заканчиваются перед разделителем. Это позволяет читателю выполнить некоторое рудиментарное восстановление ошибок. Чтобы включить разделитель RTF в пропускаемые данные, он должен быть представлен с использованием соответствующего символа управления (то есть экранированного с обратной косой чертой), как в обычном тексте. Любое текстовое слово или символ RTF считается единственным символом для подсчета пропускаемых символов.

Писатель RTF, когда он встречает символ Unicode без соответствующего символа ANSI, должен выводить \uN, за которым следует лучшее представление ANSI, которое он может управлять. Кроме того, если символ Юникода переводится в поток символов ANSI с количеством байтов, отличным от текущего символьного байта символов Юникода, он должен генерировать ключевое слово \ucN до ключевого слова \uN, чтобы уведомить читателя об этом изменении.

Управляющие слова RTF обычно принимают подписанные 16-битные числа в качестве аргументов. По этой причине значения Unicode, превышающие 32767, должны быть выражены как отрицательные числа