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

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

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

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

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

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

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

Проблема, возникающая при появлении символов Юникода


person Emir    schedule 02.09.2009    source источник


Ответы (4)


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

person Eric Smith    schedule 02.09.2009
comment
хмммм, очень интересный момент. Если это правда, то, вероятно, где-то в моей логике есть ошибка ... и ответ Яна Кемпа имеет гораздо больше смысла ... Я буду продолжать поиск в Google - person Emir; 02.09.2009

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

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();
}
person Hogan    schedule 03.04.2012

Вам нужно будет преобразовать строку в массив 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 };
person Ian Kemp    schedule 02.09.2009
comment
Привет, спасибо за ответ, я пытался реализовать ваше решение, к сожалению, не работает. Я думаю, это потому, что существует разница между кодировкой Codepoint и UTF16 (Encoding.Unicode). Вы предлагаете мне выводить байты из кодировки UTF16, где исключена Codepoint. (И это работает для многих персонажей, но не для всех) - person Emir; 02.09.2009
comment
Этот ответ также работает, вероятно, у меня была ошибка в моем коде, когда я ее тестировал. Спасибо за ваш ответ и ваше время - person Emir; 03.09.2009
comment
Единственная проблема здесь в том, что при преобразовании в массив байтов вы потеряете свою кодировку. Лучше оставить его как UTF-16 и пропустить через цикл. - person Brain2000; 03.08.2016

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

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

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

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

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

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

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

person Yongtao Wang    schedule 11.07.2016