Форум программистов, компьютерный форум, киберфорум
С++ для начинающих
Войти
Регистрация
Восстановить пароль
 
Рейтинг 4.87/15: Рейтинг темы: голосов - 15, средняя оценка - 4.87
0 / 0 / 0
Регистрация: 12.09.2015
Сообщений: 12
1

Ассоциативный контейнер std::map с кириллицей

26.02.2019, 20:45. Показов 2797. Ответов 14
Метки нет (Все метки)

Всем привет! Никак не могу разобраться каким способом решить интересную проблему. В ассоциативном контейнере map<string, string> содержатся элементы типа pair. First-элемент типа pair - символ кириллицы. Second-элемент типа pair - HEX-код символа. Вот пример моего контейнера map:
map<string, string> Hex_decode{ { "А", "%C0" },{ "Б", "%C1" },{ "В", "%C2" },{ "Г", "%C3" },{ "Д", "%C4" },{ "Е", "%C5" },{ " ", "%20" }};
А, Б, В, ... это русские буквы. Последний first-элемент типа pair - пробел.
Мне нужно найти в контейнере map определенный символ. Допустим символ "В". Я ищу так:
Hex_decode.find("В");
Но результат этого выражения - итератор Hex_decode.end(). То есть эта функция-член не может найти символ "В"... Хотя он там есть. Когда я вывожу первый first-элемент типа pair таким образом:
Hex_decode.begin()->first;
То у меня выводится символ пробела. Который вообще последний в списке элементов контейнера. Когда я узнаю размер контейнера, то мне выводится размер 7. То есть будто бы эти все данные добавились. Но функция член begin() возвращает последний элемент почему-то. Кириллицу игнорирует... Но символы кириллицы учитываются, когда узнаешь размер контейнера.

Я установил уже setlocale(LC_CTYPE, "rus"); не помогло. Устанавливал:
SetConsoleCP(1251);
SetConsoleOutputCP(1251);
Тоже не помогло.

Это помогло только при работе с string. То есть если я ищу в контейнере string("абракадабра") нужный мне символ, то у меня находится тот символ кириллицы, который мне нужен. Но с контейнером map непонятки. Подскажите, можно ли решить простенько данную проблему? (PS: уже устанавливал u8 перед first-элементом типа pair (u8"А"), также задавал тип first-элемента как тип wstring, а first-элементы инициализировал так: L"А".
Ничего не помогло. Тут нужно какое-то сложное решение. Подскажите в каком направлении копать.
0

Помощь в написании контрольных, курсовых и дипломных работ здесь.

Лучшие ответы (1)
Programming
Эксперт
94731 / 64177 / 26122
Регистрация: 12.04.2006
Сообщений: 116,782
26.02.2019, 20:45
Ответы с готовыми решениями:

Возможно ли создать контейнер std::map, в котором в качестве значения была бы ссылка на std::map?
Здравствуйте. Возможно ли создать контейнер std::map, в котором в качестве значения была бы...

Ассоциативный контейнер типа map
Привет! Помогите пожалуйста найти ошибку. Программа с Ассоциативным контейнером типа map&lt;string,...

Map как не ассоциативный контейнер
Здравствуйте. Я вложил в контейнер map кучу элементов с ключом String и значением - объектом моего...

Emplace в std::map. Как добавить элемент в std::map без копирования?
здравствуйте... есть ли способ не писать так: std::map&lt;int, char&gt; ksa;...

14
15109 / 8109 / 1958
Регистрация: 30.01.2014
Сообщений: 13,775
26.02.2019, 20:57 2
Цитата Сообщение от Alexxfiles666 Посмотреть сообщение
То у меня выводится символ пробела.
Ну так map - упорядоченный контейнер. Элементы в нем находятся в сортированном порядке.
Если строка "пробел" выводится первой, значит лексикографическии она первая. Тут все верно.

Цитата Сообщение от Alexxfiles666 Посмотреть сообщение
Кириллицу игнорирует...
Можете дать полный, компилируемый пример на котором у вас поведение отличается от ожидаемого? Иначе помочь вам будет гораздо сложнее.
0
15109 / 8109 / 1958
Регистрация: 30.01.2014
Сообщений: 13,775
26.02.2019, 20:58 3
Цитата Сообщение от Alexxfiles666 Посмотреть сообщение
Тут нужно какое-то сложное решение.
Нет, не нужно. Тут нужно понимание. Пока что вы только гадаете, отсюда проблемы.
0
Нарушитель
1486 / 1288 / 485
Регистрация: 16.08.2014
Сообщений: 5,415
Записей в блоге: 1
26.02.2019, 20:58 4
Alexxfiles666, работает:
C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
#include <iostream>
#include <map>
#include <string>
 
int main()
{
    std::map<std::string, std::string> map
    {
        std::make_pair("A", "1"),
        std::make_pair("B", "2"),
        std::make_pair("C", "3"),
    };
 
    auto it = map.find("B");
 
    return 0;
}
локали тут не при чем
0
15109 / 8109 / 1958
Регистрация: 30.01.2014
Сообщений: 13,775
26.02.2019, 20:58 5
del
0
Нарушитель
1486 / 1288 / 485
Регистрация: 16.08.2014
Сообщений: 5,415
Записей в блоге: 1
26.02.2019, 20:58 6
---
0
15109 / 8109 / 1958
Регистрация: 30.01.2014
Сообщений: 13,775
26.02.2019, 21:04 7

Не по теме:

Минутка телепатии,


автор на самом деле заполнил контейнер к коде, но строку, которую он ищет, получает откуда-то извне. При несовпадении кодировок получаем несовпадение при поиске и возврат end(). Т.е. нужно перестать паниковать и пробовать все подряд, а выяснить
* в какой кодировке сохранен файл компилируемого исходника
* в какой кодировке приходит строка для поиска
Далее эти кодировки нужно привести в соответствие и все заработает.
0
_stanislav
26.02.2019, 21:07
  #8

Не по теме:

Цитата Сообщение от DrOffset Посмотреть сообщение
нужно перестать паниковать
утро вечера мудренее, сколько раз с утра задача становилась легче как по волшебству. у меня лично уже не работает место которым нужно думать :)

0
0 / 0 / 0
Регистрация: 12.09.2015
Сообщений: 12
26.02.2019, 21:19  [ТС] 9
DrOffset, вы правы! Все так у меня устроено как вы сказали... Приходит строка из переменной string... А как узнать в какой кодировке символ в string-овой переменной?
0
15109 / 8109 / 1958
Регистрация: 30.01.2014
Сообщений: 13,775
26.02.2019, 21:39 10
Цитата Сообщение от Alexxfiles666 Посмотреть сообщение
А как узнать в какой кодировке символ в string-овой переменной?
Ну расскажите, для начала, откуда она приходит.
0
0 / 0 / 0
Регистрация: 12.09.2015
Сообщений: 12
26.02.2019, 22:55  [ТС] 11
DrOffset, схема такова:
1) я получаю указатель типа [SAFEARRAY *] на двумерный массив - в этом массиве данные, добытые из диапазона ячеек Excel (данные получены с помощью COM-интерфейса IDispatch). Тип каждого элемента двумерного массива - строка типа BSTR.
2) затем я работаю с каждым элементом массива, проделывая с каждым элементом такую операцию: создаю объект _bstr_t из переменной VARIANT (элемента массива типа BSTR). И присваиваю значение объекта _bstr_t переменной string. Так у меня получается строка string.
3)Далее я проверяю строку string на наличие символов русского алфавита, и если нахожу такие символы, то найденный символ ищу в контейнере map... чтобы перекодировать символ в url-вид, изменив исходный символ в переменной string на аналог этого символа в HEX-кодировке.

И какую кодировку содержит строка string при всем при этом, как это определить?
0
15109 / 8109 / 1958
Регистрация: 30.01.2014
Сообщений: 13,775
26.02.2019, 23:13 12
Лучший ответ Сообщение было отмечено Alexxfiles666 как решение

Решение

Цитата Сообщение от Alexxfiles666 Посмотреть сообщение
строка типа BSTR.
Скорее всего при преобразовании в char * строка получается в кодировке, которая соответствует текущей локали Windows, в русской Windows - это CP1251.

Однако, сам BSTR - это всегда UTF16, поэтому возможно вообще не стоит связываться с преобразованием, а сделать так:
C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
// ваш код
 
std::map<std::wstring, std::string> Hex_decode{ 
 { L"А", "%C0" },{ L"Б", "%C1" },{ L"В", "%C2" },{ L"Г", "%C3" },{ L"Д", "%C4" },{ L"Е", "%C5" },{ L" ", "%20" }
};
 
BSTR bstrText = /*некая строка*/
 
_bstr_t tmp(bstrText);
 
std::wstring fstr((wchar_t *)tmp, tmp.length());
 
if(Hex_decode.find(fstr) != Hex_decode.end())
{
    //.....
}
 
// ваш код
1
0 / 0 / 0
Регистрация: 12.09.2015
Сообщений: 12
27.02.2019, 00:44  [ТС] 13
DrOffset, спасибо! Покопал в сторону BSTR и _bstr_t. Понял, что класс _bstr_t - оболочка для работы с опасным типом BSTR. Также узнал, что когда я присваиваю переменной string значение объекта _bstr_t, то этот класс _bstr_t внутри себя вызывает функцию ConvertBSTRToString. Соответственно эта функция присваивает созданному временно массиву char * значение объекта _bstr_t...
А BSTR тип это тип OLECHAR *. А Олечар* это тип WCHAR. А символы типа WCHAR глобально имеют кодировку UNICODE. Windows использует кодировку UTF16 для строк типа WCHAR. Ну WCHAR это тоже самое, что и wchar_t... Широкий символ. Грубо говоря, строка типа BSTR оказалась строкой типа wchar_t... И кодировка этой строки - с помощью UTF16. UTF16 берет символы из UNICODE.
Данные из ячейки Excel попадают в строку BSTR. В ячейке Эксель данные - строка в кириллице. Значит ее кодировка - cp1251, а берутся символы из таблицы ASCII. И запихиваются в строку BSTR, которая имеет кодировку UTF16. То есть данные из ячейки эксель закодированы в cp1251, а затем перекодирываются в UTF16... Во время этого действия неизвестно что происходит. Хотя я же считываю нормально данные. Значит перекодирование переменных происходит вроде как гладко.
Затем следующее преобразование из широкого символа в обычный символ char... Тоже что то с данными происходит.

Я проверил - map с типами string string работает. Это я тупил. Значит проблема именно в закодированном символе, который находится внутри переменной string. Он не находит последовательность байт в map. Значит последовательность разная. Это значит, что переменная string владеет символом кириллицы из кодировки Unicode... получается наверное так. А map владеет символами из кодировки cp1251, так как в файле я применил setlocale... Уважаемый DrOffset, вы совершенно правы! Мне надо использовать в map тип широкого символа. Чтобы кириллические символы брались из UNICODE. Ну и соответственно не надо преобразовывать из широкого символа в обычный символ строку-символ BSTR.
Я рассуждаю вслух, чтобы помочь потом кому нибудь в этом. А то все темы о выводе в консоль кириллицы... и ни одной темы про контейнеры с кириллицей... Вот теперь есть такая тема! Я попытался разжевать как смог свою проблему.
0
15109 / 8109 / 1958
Регистрация: 30.01.2014
Сообщений: 13,775
27.02.2019, 09:36 14
Цитата Сообщение от Alexxfiles666 Посмотреть сообщение
В ячейке Эксель данные - строка в кириллице. Значит ее кодировка - cp1251, а берутся символы из таблицы ASCII. И запихиваются в строку BSTR, которая имеет кодировку UTF16. То есть данные из ячейки эксель закодированы в cp1251, а затем перекодирываются в UTF16...
Только наоборот. Внутри данные в юникоде, а при получении строки в байтовом диапазоне происходит перекодирование в одну из локальных кодировок (CP1251 в русской windows).

Цитата Сообщение от Alexxfiles666 Посмотреть сообщение
А map владеет символами из кодировки cp1251, так как в файле я применил setlocale...
Не совсем; кодировка string-строк, заданных в исходнике, зависит не от setlocale, а от кодировки самого исходника (именно поэтому я про нее упомянул выше). Если ваш файл cpp в кодировке 1251, то и строки, записанные в контейнер через строковые литералы, в ней же.

От setlocale зависит то, как вы сможете их увидеть в консоли windows, по историческим причинам выводимая кодировка консоли отличается от остальной локальной кодировки системы и соответствует локальной кодировке для DOS (в русской windows - CP866).

Цитата Сообщение от Alexxfiles666 Посмотреть сообщение
Я рассуждаю вслух, чтобы помочь потом кому нибудь в этом. А то все темы о выводе в консоль кириллицы... и ни одной темы про контейнеры с кириллицей...
В целом вы почти все верно поняли. Остальным стоит брать с вас пример в умении самостоятельно искать информацию.
1
0 / 0 / 0
Регистрация: 12.09.2015
Сообщений: 12
27.02.2019, 23:08  [ТС] 15
DrOffset, я перечитал вчерашнее сообщение, я конечно сумасбродно объяснил все... и допустил одну ошибку. Когда строка BSTR преобразовывается в тип char*, то "широкий" тип преобразовывается в обычный тип. Широкий тип wchar_t представлен в кодировке UTF16, сама система кодировки UTF16 кодирует символы UNICOD-а. То есть папа wchar_t - UNICODE. Оттуда берутся последовательности байтов.
Я порылся в настройках проекта Visual Studio - и моментально нашел, что коды символов в программе берутся из UNICODE таблицы. Таким образом контейнер map, в котором кириллица - состоит из символов из UNICODE таблицы.
Переменная string, которая получается путем преобразования строки BSTR (с широкими символами) в тип char*(с обычными символами) - состоит из символов, закодированных с помощью одной из кодировок ANSI. А именно: с помощью кодировки Windows-1251. Но кодировка ANSI берет символы из расширенной таблицы ASCII, имеющей кириллические символы, а также символы английского языка и другие необходимый символы.

UNICODE и ASCII - две разные таблицы. Индексы совпадающих элементов-символов тоже разные. Последовательность единичек и нулей, которые представляют конкретный символ в таблице - тоже разная для одного и того же элемента в таблицах UNICODE и ASCII. При взаимодействии двух переменных, содержащих в себе символы из разных таблиц появляются кракозябры в программе, либо программа работает некорректно, как в моем случае.

Мой map содержит символы из таблицы UNICODE, переменная string содержит символы из таблицы ASCII. А я пытался найти символ из таблицы ASCII в переменной, содержащей символы из таблицы UNICODE. Вот умора. Можно считать, что это совершенно разные данные... Лишь графическое представление одинаковое, а вот внутренняя структура совершенно другая. Внутренняя реализация разная. Я сравнивал пирог с коробкой. Коробка была похожа внешне на пирог. Пирог был похож на коробку... Как-то так. Все уложилось за день по полочкам, решил объяснить на своем примере эту довольно сложную проблему... Хотя теперь она мне такой уже не кажется. Спасибо еще раз DrOffset за введение меня в мир кодировок и таблиц символов. Одной краеугольной темой, в которой я не разбираюсь, стало меньше!
0
IT_Exp
Эксперт
87844 / 49110 / 22898
Регистрация: 17.06.2006
Сообщений: 92,604
27.02.2019, 23:08

std::map, std::vector и порядок обхода коллекции
Здравствуйте, уважаемые! Вопрос следующий - если я сохраняю какие-то значения в map или вектор, то...

Стоит ли очищать в деструкторе std::map , std::vecotor?
У меня ещё один нубский вопрос :) Вот если в классе объявлены мапы и вектора, которые по ходу...

Std::unordered_multimap<std::string, int> map
Приветствую. Как можно получить только &quot;уникальный&quot; ключ в контейнере? ...

Ассоциативный контейнер и шаблонный класс
Помогите пожалуйста исправить и дополнить код. Задание: Автоматизированная информационная система...


Искать еще темы с ответами

Или воспользуйтесь поиском по форуму:
15
Ответ Создать тему
Опции темы

КиберФорум - форум программистов, компьютерный форум, программирование
Powered by vBulletin® Version 3.8.9
Copyright ©2000 - 2021, vBulletin Solutions, Inc.