std::string 中的 GSOAP 格式错误的 utf-8
GSOAP malforms utf-8 in std::string
我有使用 GSOAP 的 C++ 服务器。其中一个 API 接受字符串。
<message name="concatRequest">
<part name="a" type="ns:password"/><!-- ns__concat::a -->
<part name="b" type="xsd:string"/><!-- ns__concat::b -->
</message>
int billon__concat( struct soap *soap, std::string a, std::string b, std::string &result )
{
// std::cout <<"PACZPAN A:"<<a<<" B:"<<b <<std::endl;
std::cout <<"PACZPAN B[0..3]: " << (int)b[0] << " " << (int)b[1] << " " << (int)b[2] << " " <<(int)b[3] << std::endl;
std::cout <<"PACZPAN B[0..3]: " << (char)b[0] << " " << (char)b[1] << " " << (char)b[2] << " " <<(char)b[3] << std::endl;
result = a + b;
// std::cout <<"PACZPAN res:"<<result <<std::endl;
return SOAP_OK;
}
ns::password
也只是一个字符串。
现在我通过两种不同的方式发送带有参数 B='PŁOCK' 的请求,在 wireshark 中显示为 'PŁOCK' 或 PŁOCK
,所以我认为两者都是正确的。
还记录 gsoap 打印:
POST / HTTP/1.1
Accept-Encoding: gzip,deflate
Content-Type: text/xml;charset=UTF-8
SOAPAction: ""
Content-Length: 471
Host: localhost:8080
Connection: Keep-Alive
User-Agent: Apache-HttpClient/4.1.1 (java 1.5)
<soapenv:Envelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:urn="urn:calc">
<soapenv:Header/>
<soapenv:Body>
<urn:concat soapenv:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/">
<a xsi:type="urn:password"> </a>
<b xsi:type="xsd:string">PŁOCK</b>
</urn:concat>
</soapenv:Body>
</soapenv:Envelope>
当服务器收到它时,它变成PAOCK。 ASCII 之外没有坏字节,只是不同的字母。
PACZPAN B[0..3]: 80 65 79 67
PACZPAN B[0..3]: P A O C
我不在乎 std::string 不能很好地处理 unicode。我希望它按原样处理发送的字节。
我可以在 typemap.dat 中添加映射:xsd__string = | std::wstring
,但我不想使用 std::wstring - 它无论如何都不是 utf-8。
GSOAP 默认不处理拉丁语集之外的字符:doc. It can be changed during initialization of soap context with a flag:
struct soap *soap = soap_new1( SOAP_C_UTFSTRING );
GSOAP 的 UTF-8 处理的主要问题是,它将每个 UTF-8 字符转换为 Latin1 字符并且不关心对话是否可能。
GSOAP 假装通过接受和生成 来正确处理 UTF-8,但事实并非如此。
在 stdsoap2.cpp 中的 GSOAP 方法 soap_pututf8() 和 soap_getutf8() 中,无法进行预期的错误字符转换,因此 所有内容 都将被转换.
因此,UTF-8 字符“Ł”无声无息地变成了绝对错误的字符 'A'。在我看来,这是国家语言支持的噩梦。我已经告诉 GSOAP 的作者,但他认为这个习惯没有问题。
所以在我看来,小型界面的最佳解决方案是使用您提到的 SOAP_C_UTFSTRING 并自行为每个字符串调用 iconv() 。如果结果为 ((size_t) -1) 你的 GSOAP-Methode 可以 return soap_sender_fault(soap, strerror(errno), NULL); (参见 man 3 iconv)
否则,在您的 GSOAP 应用程序之前执行字符集对话的 GSOAP 插件或 Web 服务器过滤器将是一个机会。
现在您有机会正确处理您想要的任何字符集。如果要处理 ISO-8859-16(因为字母“Ł”),请参见以下示例。
https://de.wikipedia.org/wiki/ISO_8859
#include <iconv.h>
#include <stdexcept> // std::invalid_argument
#include <string.h> // strerror
#include <system_error>
class Iconv {
const std::string m_to, m_from; // remember for error messages
const iconv_t m_cd; // the encapsulated conversion descriptor
const size_t m_multiplier; // one UTF-8 character can need up to 4 bytes
public:
Iconv(const char *to, const char *from, size_t multiplier)
: m_to(to), m_from(from), m_cd(iconv_open(to, from)), m_multiplier(multiplier) {
if (m_cd == ((iconv_t) -1))
throw std::system_error(errno, std::system_category(), m_from + " to " + m_to);
}
~Iconv() { iconv_close(m_cd); }
std::string operator()(std::string in) const {
size_t inbytesleft = in.length();
char *inbuf = &in[0];
size_t outbytesleft = m_multiplier * inbytesleft + 1;
std::string out;
out.resize(outbytesleft);
char *outbuf = &out[0];
if (iconv(m_cd, &inbuf, &inbytesleft, &outbuf, &outbytesleft) == ((size_t) -1)) {
if (errno == EILSEQ || errno == EINVAL)
throw std::invalid_argument(m_from + " to " + m_to + ": " + strerror(errno));
else
throw std::system_error(errno, std::system_category(), m_from + " to " + m_to);
}
out.resize(out.length() - outbytesleft);
return out;
}
};
int billon__concat(struct soap *soap, std::string a, std::string b, std::string &result) {
try {
static const Iconv utf8_to_iso885916("ISO-8859-16", "UTF-8", 1);
a = utf8_to_iso885916(a);
b = utf8_to_iso885916(b);
// do your fancy stuff with ISO-8859-16 strings ...
result = a + b;
static const Iconv iso885916_to_utf8("UTF-8", "ISO-8859-16", 4);
result = iso885916_to_utf8(result);
return SOAP_OK;
} catch (const std::invalid_argument& ex) {
return soap_sender_fault(soap, ex.what(), NULL);
} catch (const std::exception& ex) {
return soap_receiver_fault(soap, ex.what(), NULL);
}
}
我有使用 GSOAP 的 C++ 服务器。其中一个 API 接受字符串。
<message name="concatRequest">
<part name="a" type="ns:password"/><!-- ns__concat::a -->
<part name="b" type="xsd:string"/><!-- ns__concat::b -->
</message>
int billon__concat( struct soap *soap, std::string a, std::string b, std::string &result )
{
// std::cout <<"PACZPAN A:"<<a<<" B:"<<b <<std::endl;
std::cout <<"PACZPAN B[0..3]: " << (int)b[0] << " " << (int)b[1] << " " << (int)b[2] << " " <<(int)b[3] << std::endl;
std::cout <<"PACZPAN B[0..3]: " << (char)b[0] << " " << (char)b[1] << " " << (char)b[2] << " " <<(char)b[3] << std::endl;
result = a + b;
// std::cout <<"PACZPAN res:"<<result <<std::endl;
return SOAP_OK;
}
ns::password
也只是一个字符串。
现在我通过两种不同的方式发送带有参数 B='PŁOCK' 的请求,在 wireshark 中显示为 'PŁOCK' 或 PŁOCK
,所以我认为两者都是正确的。
还记录 gsoap 打印:
POST / HTTP/1.1
Accept-Encoding: gzip,deflate
Content-Type: text/xml;charset=UTF-8
SOAPAction: ""
Content-Length: 471
Host: localhost:8080
Connection: Keep-Alive
User-Agent: Apache-HttpClient/4.1.1 (java 1.5)
<soapenv:Envelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:urn="urn:calc">
<soapenv:Header/>
<soapenv:Body>
<urn:concat soapenv:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/">
<a xsi:type="urn:password"> </a>
<b xsi:type="xsd:string">PŁOCK</b>
</urn:concat>
</soapenv:Body>
</soapenv:Envelope>
当服务器收到它时,它变成PAOCK。 ASCII 之外没有坏字节,只是不同的字母。
PACZPAN B[0..3]: 80 65 79 67
PACZPAN B[0..3]: P A O C
我不在乎 std::string 不能很好地处理 unicode。我希望它按原样处理发送的字节。
我可以在 typemap.dat 中添加映射:xsd__string = | std::wstring
,但我不想使用 std::wstring - 它无论如何都不是 utf-8。
GSOAP 默认不处理拉丁语集之外的字符:doc. It can be changed during initialization of soap context with a flag:
struct soap *soap = soap_new1( SOAP_C_UTFSTRING );
GSOAP 的 UTF-8 处理的主要问题是,它将每个 UTF-8 字符转换为 Latin1 字符并且不关心对话是否可能。
GSOAP 假装通过接受和生成
所以在我看来,小型界面的最佳解决方案是使用您提到的 SOAP_C_UTFSTRING 并自行为每个字符串调用 iconv() 。如果结果为 ((size_t) -1) 你的 GSOAP-Methode 可以 return soap_sender_fault(soap, strerror(errno), NULL); (参见 man 3 iconv)
否则,在您的 GSOAP 应用程序之前执行字符集对话的 GSOAP 插件或 Web 服务器过滤器将是一个机会。
现在您有机会正确处理您想要的任何字符集。如果要处理 ISO-8859-16(因为字母“Ł”),请参见以下示例。 https://de.wikipedia.org/wiki/ISO_8859
#include <iconv.h>
#include <stdexcept> // std::invalid_argument
#include <string.h> // strerror
#include <system_error>
class Iconv {
const std::string m_to, m_from; // remember for error messages
const iconv_t m_cd; // the encapsulated conversion descriptor
const size_t m_multiplier; // one UTF-8 character can need up to 4 bytes
public:
Iconv(const char *to, const char *from, size_t multiplier)
: m_to(to), m_from(from), m_cd(iconv_open(to, from)), m_multiplier(multiplier) {
if (m_cd == ((iconv_t) -1))
throw std::system_error(errno, std::system_category(), m_from + " to " + m_to);
}
~Iconv() { iconv_close(m_cd); }
std::string operator()(std::string in) const {
size_t inbytesleft = in.length();
char *inbuf = &in[0];
size_t outbytesleft = m_multiplier * inbytesleft + 1;
std::string out;
out.resize(outbytesleft);
char *outbuf = &out[0];
if (iconv(m_cd, &inbuf, &inbytesleft, &outbuf, &outbytesleft) == ((size_t) -1)) {
if (errno == EILSEQ || errno == EINVAL)
throw std::invalid_argument(m_from + " to " + m_to + ": " + strerror(errno));
else
throw std::system_error(errno, std::system_category(), m_from + " to " + m_to);
}
out.resize(out.length() - outbytesleft);
return out;
}
};
int billon__concat(struct soap *soap, std::string a, std::string b, std::string &result) {
try {
static const Iconv utf8_to_iso885916("ISO-8859-16", "UTF-8", 1);
a = utf8_to_iso885916(a);
b = utf8_to_iso885916(b);
// do your fancy stuff with ISO-8859-16 strings ...
result = a + b;
static const Iconv iso885916_to_utf8("UTF-8", "ISO-8859-16", 4);
result = iso885916_to_utf8(result);
return SOAP_OK;
} catch (const std::invalid_argument& ex) {
return soap_sender_fault(soap, ex.what(), NULL);
} catch (const std::exception& ex) {
return soap_receiver_fault(soap, ex.what(), NULL);
}
}