Кто-нибудь знает какой-нибудь хороший код на C++, который делает это?





CGICC включает методы для кодирования и декодирования URL. form_urlencode и form_urldecode
Вы только что завели достойный разговор в нашем офисе с этой библиотекой.
На самом деле это самый простой и самый правильный код.
Отвечая на свой вопрос ...
libcurl имеет curl_easy_escape для кодирования.
Для декодирования curl_easy_unescape
Вы должны принять этот ответ, чтобы он был показан вверху (чтобы людям было проще).
вам нужно использовать curl, чтобы это работало, и вам нужно освободить память
Связанный вопрос: почему curl unescape не обрабатывает замену '+' на пробел? Разве это не стандартная процедура при декодировании URL?
string urlDecode(string &SRC) {
string ret;
char ch;
int i, ii;
for (i=0; i<SRC.length(); i++) {
if (int(SRC[i])==37) {
sscanf(SRC.substr(i+1,2).c_str(), "%x", &ii);
ch=static_cast<char>(ii);
ret+=ch;
i=i+2;
} else {
ret+=SRC[i];
}
}
return (ret);
}
не самый лучший, но работает нормально ;-)
Конечно, вы должны использовать '%' вместо 37.
Это не преобразует '+' в пробел
Добавление дополнения к рекомендации Билла об использовании libcurl: отличное предложение, которое будет обновлено:
через 3 года функция curl_escape устарела, поэтому для будущего использования лучше использовать curl_easy_escape.
Я столкнулся с этим вопросом при поиске api для декодирования URL-адреса в приложении win32 C++. Поскольку вопрос не совсем определяет платформу, предполагая, что окна - это не плохо.
InternetCanonicalizeUrl - это API для программ Windows. Подробнее здесь
LPTSTR lpOutputBuffer = new TCHAR[1];
DWORD dwSize = 1;
BOOL fRes = ::InternetCanonicalizeUrl(strUrl, lpOutputBuffer, &dwSize, ICU_DECODE | ICU_NO_ENCODE);
DWORD dwError = ::GetLastError();
if (!fRes && dwError == ERROR_INSUFFICIENT_BUFFER)
{
delete lpOutputBuffer;
lpOutputBuffer = new TCHAR[dwSize];
fRes = ::InternetCanonicalizeUrl(strUrl, lpOutputBuffer, &dwSize, ICU_DECODE | ICU_NO_ENCODE);
if (fRes)
{
//lpOutputBuffer has decoded url
}
else
{
//failed to decode
}
if (lpOutputBuffer !=NULL)
{
delete [] lpOutputBuffer;
lpOutputBuffer = NULL;
}
}
else
{
//some other error OR the input string url is just 1 char and was successfully decoded
}
InternetCrackUrl (здесь) также, похоже, имеет флаги, чтобы указать, нужно ли декодировать URL
На днях я столкнулся с половиной этой проблемы с кодировкой. Недовольный доступными опциями, взглянув на этот пример кода C, я решил использовать свою собственную функцию кодирования URL-адресов C++:
#include <cctype>
#include <iomanip>
#include <sstream>
#include <string>
using namespace std;
string url_encode(const string &value) {
ostringstream escaped;
escaped.fill('0');
escaped << hex;
for (string::const_iterator i = value.begin(), n = value.end(); i != n; ++i) {
string::value_type c = (*i);
// Keep alphanumeric and other accepted characters intact
if (isalnum(c) || c == '-' || c == '_' || c == '.' || c == '~') {
escaped << c;
continue;
}
// Any other characters are percent-encoded
escaped << uppercase;
escaped << '%' << setw(2) << int((unsigned char) c);
escaped << nouppercase;
}
return escaped.str();
}
Реализация функции декодирования предоставляется читателю в качестве упражнения. :П
Я считаю, что было бы более общим (в целом правильным) заменить "" на "% 20". Я соответствующим образом обновил код; не стесняйтесь откатывать назад, если не согласны.
Нет, согласен. Также воспользовался шансом удалить этот бессмысленный вызов setw(0) (в то время я думал, что минимальная ширина останется установленной, пока я не верну ее обратно, но на самом деле она сбрасывается после следующего ввода).
На самом деле, это не преобразует '+' в пробел, поэтому не работает.
Разве вы не имеете в виду другое - конвертировать пробел до '+'? В любом случае, прочтите первый комментарий Джоша Келли, пробелы преобразуются в "% 20", что тоже хорошо.
Мне пришлось добавить std :: uppercase к строке «escaped << '%' << std :: uppercase << std :: setw (2) << int ((unsigned char) c);» В случае, если другие люди задаются вопросом, почему это возвращает, например,% 3a вместо% 3A
@gumlym, в этом нет необходимости. Согласно RFC 3984: «Заглавные шестнадцатеричные цифры от« A »до« F »эквивалентны строчным цифрам от« a »до« f », соответственно».
Что ж, мне пришлось сделать это при попытке кодирования URL-адреса для создания подписи для amazon aws, и это не сработало, когда он вернул нижний регистр, он работал в верхнем регистре. Вот почему я разместил это
Фактически, RFC также говорит, что «[f] или согласованность, производители и нормализаторы URI должны использовать шестнадцатеричные цифры верхнего регистра для всех процентных кодировок». Так что это разумное изменение.
Мы должны добавить кое-что для потомков. Этот ответ обрабатывает Юникод! Есть много таких решений, которых нет.
Однако я не вижу смысла проверять "c> = 0" в условном выражении. Символы поступают из текстовой строки, которая должна содержать только допустимые коды символов. Единственный способ, которым он мог бы содержать отрицательные значения, заключался в том, что строка была повреждена или если ввод был намеренно создан для нарушения функции - и в обоих случаях вы могли также хочу завершить программу.
Это выглядит неправильно, потому что строки UTF-8 не поддерживаются (w3schools.com/tags/ref_urlencode.asp). Вроде работает только для Windows-1252
Проблема была как раз isalnum(c), его надо менять на isalnum((unsigned char) c)
Обратите внимание, что тип символа уже параметризован для value_type строки. Если вы хотите поддерживать UTF-8, правильное изменение - заменить ссылки на std::string, например, std::u8string в C++ 20.
инициализацию цикла for можно заменить на for (string::value_type c: value) с разделением первой инструкции
Эта версия является чистой версией C и может дополнительно нормализовать путь к ресурсам. Использование его с C++ тривиально:
#include <string>
#include <iostream>
int main(int argc, char** argv)
{
const std::string src("/some.url/foo/../bar/%2e/");
std::cout << "src=\"" << src << "\"" << std::endl;
// either do it the C++ conformant way:
char* dst_buf = new char[src.size() + 1];
urldecode(dst_buf, src.c_str(), 1);
std::string dst1(dst_buf);
delete[] dst_buf;
std::cout << "dst1=\"" << dst1 << "\"" << std::endl;
// or in-place with the &[0] trick to skip the new/delete
std::string dst2;
dst2.resize(src.size() + 1);
dst2.resize(urldecode(&dst2[0], src.c_str(), 1));
std::cout << "dst2=\"" << dst2 << "\"" << std::endl;
}
Выходы:
src = "/some.url/foo/../bar/%2e/"
dst1 = "/some.url/bar/"
dst2 = "/some.url/bar/"
И собственно функция:
#include <stddef.h>
#include <ctype.h>
/**
* decode a percent-encoded C string with optional path normalization
*
* The buffer pointed to by @dst must be at least strlen(@src) bytes.
* Decoding stops at the first character from @src that decodes to null.
* Path normalization will remove redundant slashes and slash+dot sequences,
* as well as removing path components when slash+dot+dot is found. It will
* keep the root slash (if one was present) and will stop normalization
* at the first questionmark found (so query parameters won't be normalized).
*
* @param dst destination buffer
* @param src source buffer
* @param normalize perform path normalization if nonzero
* @return number of valid characters in @dst
* @author Johan Lindh <[email protected]>
* @legalese BSD licensed (http://opensource.org/licenses/BSD-2-Clause)
*/
ptrdiff_t urldecode(char* dst, const char* src, int normalize)
{
char* org_dst = dst;
int slash_dot_dot = 0;
char ch, a, b;
do {
ch = *src++;
if (ch == '%' && isxdigit(a = src[0]) && isxdigit(b = src[1])) {
if (a < 'A') a -= '0';
else if (a < 'a') a -= 'A' - 10;
else a -= 'a' - 10;
if (b < 'A') b -= '0';
else if (b < 'a') b -= 'A' - 10;
else b -= 'a' - 10;
ch = 16 * a + b;
src += 2;
}
if (normalize) {
switch (ch) {
case '/':
if (slash_dot_dot < 3) {
/* compress consecutive slashes and remove slash-dot */
dst -= slash_dot_dot;
slash_dot_dot = 1;
break;
}
/* fall-through */
case '?':
/* at start of query, stop normalizing */
if (ch == '?')
normalize = 0;
/* fall-through */
case '\0':
if (slash_dot_dot > 1) {
/* remove trailing slash-dot-(dot) */
dst -= slash_dot_dot;
/* remove parent directory if it was two dots */
if (slash_dot_dot == 3)
while (dst > org_dst && *--dst != '/')
/* empty body */;
slash_dot_dot = (ch == '/') ? 1 : 0;
/* keep the root slash if any */
if (!slash_dot_dot && dst == org_dst && *dst == '/')
++dst;
}
break;
case '.':
if (slash_dot_dot == 1 || slash_dot_dot == 2) {
++slash_dot_dot;
break;
}
/* fall-through */
default:
slash_dot_dot = 0;
}
}
*dst++ = ch;
} while(ch);
return (dst - org_dst) - 1;
}
Спасибо. Здесь нет необязательных путей. pastebin.com/RN5g7g9u
Это не соответствует никаким рекомендациям и совершенно неверно по сравнению с тем, что просит автор (например, «+» не заменяется пробелом). Нормализация пути не имеет ничего общего с декодированием URL. Если вы намерены нормализовать свой путь, вы должны сначала разделить свой URL-адрес на части (схема, полномочия, путь, запрос, фрагмент), а затем применить любой алгоритм, который вам нравится, только на части пути.
cpp-netlib имеет функции
namespace boost {
namespace network {
namespace uri {
inline std::string decoded(const std::string &input);
inline std::string encoded(const std::string &input);
}
}
}
они позволяют очень легко кодировать и декодировать строки URL.
Боже, спасибо. документация по cpp-netlib скудна. У вас есть ссылки на хорошие шпаргалки?
В Windows API есть функции UrlEscape / UrlUnescape, экспортируемые shlwapi.dll для этой задачи.
примечание: UrlEscape не кодирует +
Пришлось делать это в проекте без Boost. Итак, я написал свой собственный. Я просто выложу на GitHub: https://github.com/corporateshark/LUrlParser
clParseURL URL = clParseURL::ParseURL( "https://name:[email protected]:80/path/res" );
if ( URL.IsValid() )
{
cout << "Scheme : " << URL.m_Scheme << endl;
cout << "Host : " << URL.m_Host << endl;
cout << "Port : " << URL.m_Port << endl;
cout << "Path : " << URL.m_Path << endl;
cout << "Query : " << URL.m_Query << endl;
cout << "Fragment : " << URL.m_Fragment << endl;
cout << "User name : " << URL.m_UserName << endl;
cout << "Password : " << URL.m_Password << endl;
}
Ваша ссылка ведет к библиотеке, которая анализирует URL-адрес. Он не% -кодирует URL. (Или, по крайней мере, я не мог увидеть% где-либо в источнике.) Таким образом, я не думаю, что это отвечает на вопрос.
[Режим некроманта включен]
Наткнулся на этот вопрос, когда искал быстрое, современное, платформенно-независимое и элегантное решение. Мне не понравилось что-либо из вышеперечисленного, cpp-netlib будет победителем, но он имеет ужасающую уязвимость памяти в "декодированной" функции. Так что я придумал решение для духовной ци и кармы от Boost.
namespace bsq = boost::spirit::qi;
namespace bk = boost::spirit::karma;
bsq::int_parser<unsigned char, 16, 2, 2> hex_byte;
template <typename InputIterator>
struct unescaped_string
: bsq::grammar<InputIterator, std::string(char const *)> {
unescaped_string() : unescaped_string::base_type(unesc_str) {
unesc_char.add("+", ' ');
unesc_str = *(unesc_char | "%" >> hex_byte | bsq::char_);
}
bsq::rule<InputIterator, std::string(char const *)> unesc_str;
bsq::symbols<char const, char const> unesc_char;
};
template <typename OutputIterator>
struct escaped_string : bk::grammar<OutputIterator, std::string(char const *)> {
escaped_string() : escaped_string::base_type(esc_str) {
esc_str = *(bk::char_("a-zA-Z0-9_.~-") | "%" << bk::right_align(2,0)[bk::hex]);
}
bk::rule<OutputIterator, std::string(char const *)> esc_str;
};
Использование вышеуказанного как следующее:
std::string unescape(const std::string &input) {
std::string retVal;
retVal.reserve(input.size());
typedef std::string::const_iterator iterator_type;
char const *start = "";
iterator_type beg = input.begin();
iterator_type end = input.end();
unescaped_string<iterator_type> p;
if (!bsq::parse(beg, end, p(start), retVal))
retVal = input;
return retVal;
}
std::string escape(const std::string &input) {
typedef std::back_insert_iterator<std::string> sink_type;
std::string retVal;
retVal.reserve(input.size() * 3);
sink_type sink(retVal);
char const *start = "";
escaped_string<sink_type> g;
if (!bk::generate(sink, g(start), input))
retVal = input;
return retVal;
}
[Режим некроманта отключен]
EDIT01: исправлено заполнение нулями - особая благодарность Хартмуту Кайзеру
EDIT02: В прямом эфире на CoLiRu
Что такое «ужасающая уязвимость памяти» cpp-netlib? Вы можете дать краткое объяснение или ссылку?
Об этом (проблеме) уже сообщалось, поэтому я не сообщал и на самом деле не помню ... что-то вроде нарушения доступа при попытке проанализировать недопустимую escape-последовательность или что-то в этом роде
о, вот и все github.com/cpp-netlib/cpp-netlib/issues/501
Спасибо за разъяснения!
Обычно добавление "%" к значению int символа при кодировании не работает, значение предполагается в шестнадцатеричном эквиваленте. например, "/" - это "% 2F", а не "% 47".
Я думаю, что это лучшие и краткие решения как для кодирования, так и для декодирования URL-адресов (без особых зависимостей заголовков).
string urlEncode(string str){
string new_str = "";
char c;
int ic;
const char* chars = str.c_str();
char bufHex[10];
int len = strlen(chars);
for(int i=0;i<len;i++){
c = chars[i];
ic = c;
// uncomment this if you want to encode spaces with +
/*if (c==' ') new_str += '+';
else */if (isalnum(c) || c == '-' || c == '_' || c == '.' || c == '~') new_str += c;
else {
sprintf(bufHex,"%X",c);
if (ic < 16)
new_str += "%0";
else
new_str += "%";
new_str += bufHex;
}
}
return new_str;
}
string urlDecode(string str){
string ret;
char ch;
int i, ii, len = str.length();
for (i=0; i < len; i++){
if (str[i] != '%'){
if (str[i] == '+')
ret += ' ';
else
ret += str[i];
}else{
sscanf(str.substr(i + 1, 2).c_str(), "%x", &ii);
ch = static_cast<char>(ii);
ret += ch;
i = i + 2;
}
}
return ret;
}
if (ic < 16) new_str += "%0"; Для чего это питание ?? @tormuto @reliasn
@Kriyen используется для дополнения закодированного HEX нулем в начале, если в результате получается одна буква; поскольку от 0 до 15 в HEX - это от 0 до F.
Мне больше всего нравится такой подход. +1 за использование стандартных библиотек. Хотя есть две проблемы, которые нужно исправить. Я чех и использую букву «ý». Результат: «% 0FFFFFFC3% 0FFFFFFBD». Сначала использовать переключатель 16 не обязательно, так как utf8 гарантирует, что все конечные байты будут начинаться с 10, и, похоже, это не помогло моему многобайту. Вторая проблема - это FF, потому что не все компьютеры имеют одинаковое количество бит на int. Исправление заключалось в том, чтобы пропустить переключатель 16 (не требуется) и захватить последние два символа из буфера. (Я использовал stringstream, поскольку мне удобнее использовать строковый буфер). Еще дал точку. Как и рама
@Volt, не могли бы вы опубликовать обновленный код в новом ответе? Вы упоминаете о проблемах, но этого недостаточно для очевидного решения.
В этом ответе есть некоторые проблемы, потому что он использует strlen. Во-первых, это не имеет смысла, потому что мы уже знаем размер строкового объекта, так что это пустая трата времени. Гораздо хуже то, что строка может содержать 0 байтов, которые могут быть потеряны из-за strlen. Также if (i <16) неэффективен, потому что это может быть покрыто самим printf с помощью "%%% 02X". И, наконец, c должен быть беззнаковым байтом, иначе вы получите эффект, который @Volt описывал с ведущим '0xFFF ...'.
сочные кусочки
#include <ctype.h> // isdigit, tolower
from_hex(char ch) {
return isdigit(ch) ? ch - '0' : tolower(ch) - 'a' + 10;
}
char to_hex(char code) {
static char hex[] = "0123456789abcdef";
return hex[code & 15];
}
отмечая, что
char d = from_hex(hex[0]) << 4 | from_hex(hex[1]);
как в
// %7B = '{'
char d = from_hex('7') << 4 | from_hex('B');
Вдохновленный xperroni, я написал декодер. Спасибо за указатель.
#include <iostream>
#include <sstream>
#include <string>
using namespace std;
char from_hex(char ch) {
return isdigit(ch) ? ch - '0' : tolower(ch) - 'a' + 10;
}
string url_decode(string text) {
char h;
ostringstream escaped;
escaped.fill('0');
for (auto i = text.begin(), n = text.end(); i != n; ++i) {
string::value_type c = (*i);
if (c == '%') {
if (i[1] && i[2]) {
h = from_hex(i[1]) << 4 | from_hex(i[2]);
escaped << h;
i += 2;
}
} else if (c == '+') {
escaped << ' ';
} else {
escaped << c;
}
}
return escaped.str();
}
int main(int argc, char** argv) {
string msg = "J%C3%B8rn!";
cout << msg << endl;
string decodemsg = url_decode(msg);
cout << decodemsg << endl;
return 0;
}
edit: удалены ненужные cctype и iomainip.
Блок «if (c == '%')» требует дополнительной проверки выхода за пределы, i [1] и / или i [2] могут быть за пределами text.end (). Я бы тоже переименовал «сбежавший» в «неэкранированный». "escaped.fill ('0');" вероятно не нужен.
Пожалуйста, посмотрите мою версию. Он более оптимизирован. pastebin.com/g0zMLpsj
Я знаю, что вопрос касается метода C++, но для тех, кому он может понадобиться, я придумал очень короткую функцию на простом C для кодирования строки. Он не создает новую строку, а изменяет существующую, что означает, что он должен иметь достаточный размер, чтобы вместить новую строку. Очень легко угнаться.
void urlEncode(char *string)
{
char charToEncode;
int posToEncode;
while (((posToEncode=strspn(string,"1234567890ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz-_.~"))!=0) &&(posToEncode<strlen(string)))
{
charToEncode=string[posToEncode];
memmove(string+posToEncode+3,string+posToEncode+1,strlen(string+posToEncode));
string[posToEncode]='%';
string[posToEncode+1] = "0123456789ABCDEF"[charToEncode>>4];
string[posToEncode+2] = "0123456789ABCDEF"[charToEncode&0xf];
string+=posToEncode+3;
}
}
Вы можете использовать функцию g_uri_escape_string (), предоставленную glib.h. https://developer.gnome.org/glib/stable/glib-URI-Functions.html
#include <stdio.h>
#include <stdlib.h>
#include <glib.h>
int main() {
char *uri = "http://www.example.com?hello world";
char *encoded_uri = NULL;
//as per wiki (https://en.wikipedia.org/wiki/Percent-encoding)
char *escape_char_str = "!*'();:@&=+$,/?#[]";
encoded_uri = g_uri_escape_string(uri, escape_char_str, TRUE);
printf("[%s]\n", encoded_uri);
free(encoded_uri);
return 0;
}
скомпилируйте его с помощью:
gcc encoding_URI.c `pkg-config --cflags --libs glib-2.0`
Другое решение доступно с использованием Библиотека глупостей Facebook: folly::uriEscape и folly::uriUnescape.
Я не смог найти здесь URI decode / unescape, который также декодирует 2- и 3-байтовые последовательности. Внесу свой вклад в мою собственную высокопроизводительную версию, которая на лету преобразует ввод c sting в wstring:
#include <string>
const char HEX2DEC[55] =
{
0, 1, 2, 3, 4, 5, 6, 7, 8, 9,-1,-1, -1,-1,-1,-1,
-1,10,11,12, 13,14,15,-1, -1,-1,-1,-1, -1,-1,-1,-1,
-1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1,
-1,10,11,12, 13,14,15
};
#define __x2d__(s) HEX2DEC[*(s)-48]
#define __x2d2__(s) __x2d__(s) << 4 | __x2d__(s+1)
std::wstring decodeURI(const char * s) {
unsigned char b;
std::wstring ws;
while (*s) {
if (*s == '%')
if ((b = __x2d2__(s + 1)) >= 0x80) {
if (b >= 0xE0) { // three byte codepoint
ws += ((b & 0b00001111) << 12) | ((__x2d2__(s + 4) & 0b00111111) << 6) | (__x2d2__(s + 7) & 0b00111111);
s += 9;
}
else { // two byte codepoint
ws += (__x2d2__(s + 4) & 0b00111111) | (b & 0b00000011) << 6;
s += 6;
}
}
else { // one byte codepoints
ws += b;
s += 3;
}
else { // no %
ws += *s;
s++;
}
}
return ws;
}
#define __x2d2__(s) (__x2d__(s) << 4 | __x2d__(s+1)), и он должен быть собран с -WError.
Извините, но "высокая производительность" при добавлении одиночных символов в wstring нереальна. По крайней мере, для reserve достаточно места, иначе у вас все время будут массовые перераспределения
вы можете просто использовать функцию AtlEscapeUrl () из atlutil.h, просто просмотрите ее документацию о том, как ее использовать.
это будет работать только с окнами
Да, я пробовал это в Windows.
Как насчет принятия ответа?