Символы UrlUnescape() и unicode
Я пытаюсь написать программу на C++, которая будет декодировать строку, закодированную URL, содержащую некоторые символы юникода, закодированные URL.
#include <windows.h>
#include <string>
#include <shlwapi.h>
#pragma comment(lib, "Shlwapi.lib")
int _tmain(int argc, _TCHAR* argv[])
{
std::wstring test = L"bla+%D0%B1%D0%BB%D0%BE%D1%84+%E6%97%A5%E6%9C%AC%E8%AA%9E";
PWSTR urlencodedStr = const_cast<WCHAR*>(test.c_str());
WCHAR decodedStr[1025];
DWORD size = 1024;
HRESULT hres = UrlUnescape(urlencodedStr, decodedStr, &size, NULL);
if (hres == S_OK)
MessageBox(NULL, decodedStr, L"decoded string", MB_OK);
return 0;
}
Я ожидаю, чтобы получить L"бла блоф 日本語" в decodedStr. Но вместо этого я получаю L"bla+бÐ"оÑ"+æ-¥æèèžž". Я использую кодировку unicode в своей сборке. Что я делаю не так?
2 ответа:
UrlUnescape
преобразует URL-декодированные байты%xx
в символы, используя кодовую страницу по умолчанию (ANSI) по умолчанию. Это почти никогда не то, чего вы хотите.Начиная с Windows 8, вы можете передать флаг UNESCAPE_AS_UTF8, чтобы заставить его работать. Если вы не можете зависеть от Win8, вам придется использовать/написать другой вызов библиотеки декодирования URL, который не страдает от этой проблемы.
Также существует проблема
+
: в простой кодировке URL (например, для использования в части пути) это означает, что плюс, но в кодировке form-url (например, в параметре запроса), которая, похоже, у вас здесь есть, это означает пробел. Хороший декодер URL-адресов даст вам возможность сказать, какой из них вы имеете в виду;UrlUnescape
этого не делает. Альтернативой является ручная замена+
пробелом на входе перед декодированием URL-адреса; это один частный случай, и никакие другие символы не затрагиваются подобным образом.
Хорошо. Поэтому я написал свою собственную функцию для декодирования URL-кодированных строк с помощью символов unicode. Вот оно:
#include <windows.h> #include <string> #include <shlwapi.h> #include <sstream> #include <iostream> #include <wininet.h> // For INTERNET_MAX_URL_LENGTH #pragma comment(lib, "Shlwapi.lib") bool IsHexChar(const WCHAR _char) { return ((_char == L'A') || (_char == L'B') || (_char == L'C') || (_char == L'D') || (_char == L'E') || (_char == L'F') || iswalnum(_char)); } std::wstring UrlDecode(const std::wstring& _encodedStr) { std::string charStr; for (size_t i = 0; i < _encodedStr.length(); ++i) { if ((_encodedStr[i] == L'%') && (IsHexChar(_encodedStr[i+1])) && (IsHexChar(_encodedStr[i+2]))) { std::wstring hexCodeStr = L"0x"; hexCodeStr += _encodedStr[i+1]; hexCodeStr += _encodedStr[i+2]; unsigned int hexCharCode; std::wstringstream ss; ss << std::hex << hexCodeStr; ss >> hexCharCode; charStr += static_cast<char>(hexCharCode); i += 2; } else if (_encodedStr[i] == L'+') charStr += L' '; else charStr += _encodedStr[i]; } WCHAR decodedStr[INTERNET_MAX_URL_LENGTH]; MultiByteToWideChar(CP_UTF8, 0, charStr.c_str(), -1, decodedStr, sizeof(decodedStr)); return decodedStr; }
Используйте так:
std::wstring encodedStr = L"bla+%D0%B1%D0%BB%D0%BE%D1%84+%E6%97%A5%E6%9C%AC%E8%AA%9E"; std::wstring decodedStr = UrlDecode(encodedStr);