Как выбрать правильную кодовую страницу для декодирования контента, закодированного CArchive

В .net я хочу декодировать некоторые необработанные данные, закодированные приложением C ++. Приложение C ++ - 32-разрядное, а приложение C # - 64-разрядное.

Приложение C ++ поддерживает русские и испанские символы, но не поддерживает символы Unicode. Этот двоичный читатель C # не может читать русские или испанские символы и работает только с английскими символами ascii.

CArchive не указывает кодировку, и я не уверен, как ее читать с C #.

Я тестировал это на нескольких простых строках, которые предоставляет C ++ CArchive:

Для «ABC»: «03 41 42 43»

Для «БАМ 7555В»: «0B C1 E5 EB C0 C7 20 37 35 35 35 C2»

Ниже показано, как приложение C ++ записывает двоичный файл.

void CColumnDefArray::SerializeData(CArchive& Archive)
{
    int iIndex;
    int iSize;
    int iTemp;
    CString sTemp;

    if (Archive.IsStoring())
    {
        Archive << m_iBaseDataCol;
        Archive << m_iNPValueCol;

        iSize = GetSize();
        Archive << iSize;
        for (iIndex = 0; iIndex < iSize; iIndex++)
        {
            CColumnDef& ColumnDef = ElementAt(iIndex);
            Archive << (int)ColumnDef.GetColumnType();
            Archive << ColumnDef.GetColumnId();
            sTemp = ColumnDef.GetName();
            Archive << sTemp;
        }
    }
}

И вот как я пытаюсь прочитать это на C #.

Следующее может декодировать "ABC", но не русские символы. Я протестировал this.Encoding со всеми доступными параметрами (Ascii, UTF7 и т. Д.). Русские символы работают только для Encoding.Default. Но очевидно, что это не самый надежный вариант, поскольку кодирование и декодирование обычно происходит на разных компьютерах.

        public override string ReadString()
        {
            byte blen = ReadByte();
            if (blen < 0xff)
            {
                // *** For russian characters it comes here.***
                return this.Encoding.GetString(ReadBytes(blen));
            }

            var slen = (ushort) ReadInt16();
            if (slen == 0xfffe)
            {
                throw new NotSupportedException(ServerMessages.UnicodeStringsAreNotSupported());
            }

            if (slen < 0xffff)
            {
                return this.Encoding.GetString(ReadBytes(slen));
            }

            var ulen = (uint) ReadInt32();
            if (ulen < 0xffffffff)
            {
                var bytes = new byte[ulen];
                for (uint i = 0; i < ulen; i++)
                {
                    bytes[i] = ReadByte();
                }

                return this.Encoding.GetString(bytes);
            }

            //// Not support for 8-byte lengths 
            throw new NotSupportedException(ServerMessages.EightByteLengthStringsAreNotSupported());
        }

Как правильно это расшифровать? Как вы думаете, правильный ли выбор кодовой страницы поможет решить эту проблему? Если да, то как узнать, какая кодовая страница использовалась для кодирования?

Благодарю, если кто-то может показать мне правильное направление, чтобы это сделать.

Изменить

Думаю, этот вопрос и "Абсолютный минимум, что каждый разработчик программного обеспечения должен абсолютно точно знать о Unicode и наборах символов (Нет Извинения!) " статья развеивает некоторые сомнения. По-видимому, нет способа найти правильную кодовую страницу для существующих данных.

Думаю, теперь возникает вопрос: есть ли какая-либо кодовая страница, поддерживающая все испанские, русские и английские символы? Могу ли я указать кодовую страницу в классе C ++ CArchive?


person CharithJ    schedule 05.10.2016    source источник
comment
Кстати, для кода (де) сериализации, если вы храните его в двоичном формате, вам действительно стоит подумать о сохранении только типов фиксированной ширины (например, _ 1_). Подумайте, если вы сохраняете файл в 32-битном приложении, а затем попробуйте загрузить этот файл в 64-битном приложении. sizeof(int) может (и, вероятно, будет) отличаться, поэтому вы будете неправильно разбирать двоичный файл. stackoverflow.com/questions/589575/   -  person Cory Kramer    schedule 05.10.2016
comment
@CoryKramer: Собственно, здесь так и есть. Приложение C ++ - 32-разрядное, а приложение C # - 64-разрядное.   -  person CharithJ    schedule 05.10.2016
comment
Во что должен декодировать БАМК 7555В? Белаз 555В? Если да, используйте Encoding.GetEncoding(866).   -  person Anton Gogolev    schedule 05.10.2016
comment
@AntonGogolev: ÁåëÀÇ 7555 - это простой текст, и CArchive его кодирует, и я не могу прочитать этот закодированный текст на C #. Я попробую с этой кодовой страницей ...   -  person CharithJ    schedule 05.10.2016
comment
Я только что понял, что мой предыдущий ответ был совершенно бесполезным, потому что вы уже сказали, что приложение C ++ ... не поддерживает символы Unicode. Я пропустил это, и когда я спросил вас в комментарии, ваш ответ был нечетким. Во всяком случае, теперь ясно. Я посмотрю, что он делает. Можете ли вы показать больше вашего кода десериализации на C #?   -  person Barmak Shemirani    schedule 07.10.2016
comment
@BarmakShemirani: Я разместил свой код десериализации в вопросе. Посмотрите на метод ReadString в C #. Мы не можем изменить код C ++, поскольку он исходит из нашего устаревшего приложения. Но мы можем изменить наш код C #, чтобы поддерживать любой формат, который использует CArchive. Я пробовал со многими разными кодовыми страницами, но безуспешно. Единственный способ вернуть ту же самую строку 7555A - это использовать кодировку по умолчанию. System.Text.Encoding.Default.GetString (биты);   -  person CharithJ    schedule 07.10.2016
comment
Вот что предоставляет C ++ CArchive: для ABC я получаю 03 41 42 43. Для ÁåëÀÇ 7555 я получаю 0B C1 E5 EB C0 C7 20 37 35 35 35 C2.   -  person CharithJ    schedule 07.10.2016


Ответы (1)


Программа C ++, не поддерживающая Unicode, записывает данные как 0B C1 E5 EB C0 C7 20 37 35 35 35 C2 (длина строки, за которой следует bytes)

"ÁåëÀÇ 7555Â" - это представление bytes на кодовой странице 1252

На англоязычном компьютере следующий код возвращает "ÁåëÀÇ 7555Â". Это работает, если обе программы используют одну и ту же кодовую страницу:

string result = Encoding.Default.GetString(bytes);

Вы также можете напрямую использовать кодовую страницу 1252. Это гарантирует, что результат всегда будет "ÁåëÀÇ 7555Â" для этого конкретного набора байтов:

//result will be `"ÁåëÀÇ 7555Â"`, always
Encoding cp1252 = Encoding.GetEncoding(1252);
string result = cp1252.GetString(bytes);



Однако это не может решить никаких проблем. Рассмотрим пример с греческим текстом:

string greek = "ελληνικά";
Encoding cp1253 = Encoding.GetEncoding(1253);
var bytes = cp1253.GetBytes(greek);

bytes будет аналогичен выводу программы C ++. Вы можете использовать ту же технику для извлечения текста:

//result will be "åëëçíéêÜ"
Encoding cp1252 = Encoding.GetEncoding(1252);
string result = cp1252.GetString(bytes);

Результат "åëëçíéêÜ". Но желаемый результат "ελληνικά"

//result will be "ελληνικά"
Encoding cp1253 = Encoding.GetEncoding(1253);
string greek_decoded = cp1253.GetString(bytes);

Итак, чтобы выполнить правильное преобразование, у вас должна быть исходная кодовая страница, которую программа C ++ использовала (я просто повторяю Ханса Пассанта)

Вы можете внести следующие изменения:

public override string ReadString()
{
    //Default code page if both programs use the same code page
    Encoding encoder = System.Text.Encoding.Default;

    //or find out what code page the C++ program is using
    //Encoding encoder = System.Text.Encoding.GetEncoding(codepage);

    //or use English code page to always get "ÁåëÀÇ 7555Â"...
    //Encoding encoder = System.Text.Encoding.GetEncoding(1252);
    //(not recommended)

    byte blen = ReadByte();
    if (blen < 0xff)
        return encoder.GetString(ReadBytes(blen));

    var slen = (ushort)ReadInt16();
    if (slen == 0xfffe)
        throw new NotSupportedException(
            ServerMessages.UnicodeStringsAreNotSupported());

    if (slen < 0xffff)
        return encoder.GetString(ReadBytes(blen));

    var ulen = (uint)ReadInt32();
    if (ulen < 0xffffffff)
    {
        var bytes = new byte[ulen];
        for (uint i = 0; i < ulen; i++)
            bytes[i] = ReadByte();
        return encoder.GetString(ReadBytes(blen));
    }

    throw new NotSupportedException(
        ServerMessages.EightByteLengthStringsAreNotSupported());
}

Дополнительные комментарии:

Программа MFC, отличная от Unicode, может принимать ввод на английском или русском, но не на обоих языках одновременно. Эти старые программы используют char для хранения до 255 букв на байт. 255 не хватит места для всех алфавитов английского, русского, греческого, арабского ...

Кодовая страница 1252 отображает символы в латинские алфавиты. В то время как кодовая страница 1253 отображает символы в греческий алфавит и так далее.

Следовательно, ваш файл MFC содержит только один язык одной кодовой страницы.

Для западноевропейских языков (английский, испанский, португальский, немецкий, французский, итальянский, шведский и т. Д.) Используется кодовая страница 1252. Если пользователи остаются в этой языковой группе, особых проблем возникнуть не должно. System.Text.Encoding.Default должно решить проблему, а еще лучше System.Text.Encoding.GetEncoding(variable_codepage)

В Windows есть несколько соответствующих кодовых страниц ANSI.

874 – Windows Thai
1250 – Windows Central and East European Latin 2
1251 – Windows Cyrillic
1252 – Windows West European Latin 1
1253 – Windows Greek
1254 – Windows Turkish
1255 – Windows Hebrew
1256 – Windows Arabic
1257 – Windows Baltic
1258 – Windows Vietnamese

Некоторые азиатские языки без Unicode не поддерживаются. Некоторые символы Unicode не поддерживаются в ANSI, с этим ничего не поделаешь.

Можно заставить программу, отличную от Unicode, использовать более одной кодовой страницы. Но это непрактично. Намного проще перейти на Unicode и сделать это правильно.

См. Также Минимум, который разработчики программного обеспечения должны знать о Unicode

person Barmak Shemirani    schedule 06.10.2016
comment
Я добавил код, который выводит двоичные данные в шестнадцатеричном формате. Можете ли вы повторить это? Скопируйте / пропустите первые 50 или около того символов, чтобы мы могли видеть, содержит ли он Unicode или нет. - person Barmak Shemirani; 07.10.2016
comment
Для ABC я получаю 03 41 42 43. Для ÁåëÀÇ 7555 я получаю 0B C1 E5 EB C0 C7 20 37 35 35 35 C2. Это не юникод, можно подумать, что это ASCII, глядя на кодировку ABC. Но это не так, когда мы смотрим на вторую строку, содержащую символы, отличные от ascii. Может быть, мне нужно правильно выбрать кодовую страницу? - person CharithJ; 07.10.2016
comment
Можете ли вы вписать это в свою функцию: string result = Encoding.Default.GetString(buf)? чтобы получить кодировку из текущей кодовой страницы системы ANSI, что и делает MFC. Все в порядке, если две программы находятся на одном компьютере. - person Barmak Shemirani; 07.10.2016
comment
Я попробую твой новый ответ. Encoding.Default работает. Но мы не можем гарантировать, что кодирование и декодирование происходит на компьютерах одного типа. - person CharithJ; 07.10.2016
comment
Я не уверен, как ваше предлагаемое решение может помочь в решении этой проблемы. Когда вы смотрите на мой ответ, такие символы всегда ‹255 и попадают в это условие if. 'байт blen = ReadByte (); if (blen ‹0xff) {вернуть this.Encoding.GetString (ReadBytes (blen)); } ' - person CharithJ; 07.10.2016
comment
Покажите объявление класса за public override string ReadString(){...} Я не могу воспроизвести эту строку this.Encoding.GetString(bytes); - person Barmak Shemirani; 07.10.2016
comment
Он является производным от BinaryReader. BinaryReader.ReadString - это база. - person CharithJ; 08.10.2016
comment
: Итак, если мы напишем идентификатор кодовой страницы в blob. Как это будет работать с разными языками? Пытаюсь понять, возможно ли это вообще. Скажем, например, английский пользователь пишет что-то по-английски, а затем русский пользователь добавляет что-то по-русски и, наконец, испанский пользователь также редактирует это. Есть ли какая-либо кодовая страница, поддерживающая все испанские, русские и английские символы? К сожалению, юникод не подходит. - person CharithJ; 09.10.2016
comment
См. Редактирование для получения дополнительных объяснений, я не мог поместить это в комментарий. - person Barmak Shemirani; 09.10.2016
comment
Для БАМ 7555В массив байтов равен 0B C1 E5 EB C0 C7 20 37 35 35 35 C2. Он отображает ????? для первых 5 символов, когда используется кодировка ASCII. Но если вы посмотрите на таблицу Ascii (asciitable.com ) для C1 (193 в десятичной системе) он должен отображать символ сопоставления из расширенной таблицы ASCII? Но как платформа .net выясняет, что она не может декодировать и предоставлять '?' вместо символа из расширенной таблицы ascii. Мне просто интересно, как он решает, что карта / декодирование набора символов не работает? Как это показывает "?" вместо соответствующего символа для данного значения ascii? - person CharithJ; 10.10.2016
comment
Ссылка, на которую вы ссылаетесь, не определяет стандарт расширенного ASCII. Если вы посмотрите на другие веб-страницы, вы можете найти другое сопоставление для символов выше 128. Вам понадобится ANSI 1252 (кодовая страница по умолчанию для западноевропейских языков) или другие кодовые страницы ANSI. - person Barmak Shemirani; 11.10.2016