Редактировать - Обновлены до готовых байтов, а не символов
Вместо того, чтобы использовать .substr()
и вызывать C strtol
и приводить к uint8_t
, вы можете просто использовать istringstream
вместе с std::setbase(16)
для чтения байтов как unsigned
значений непосредственно в vector<uint8_t> msg
.См. std :: setbase .
Например, вы можете создать istringstream
из вашей строки, содержащей шестнадцатеричные символы, а затем вместе с вашим вектором uint8_t
и временным unsigned
для непосредственного чтения, прежде чем вставить обратно в вектор, который вы можете сделать, например,
std::string result ("0123456789abcdef"); /* input hex string */
std::string s2; /* string for 2-chars */
std::istringstream ss (result); /* stringstream of result */
std::vector<uint8_t> msg; /* vector of uint8_t */
while ((ss >> std::setw(2) >> s2)) { /* read 2-char at a time */
unsigned u; /* tmp unsigned value */
std::istringstream ss2 (s2); /* create 2-char stringstream */
ss2 >> std::setbase(16) >> u; /* convert hex to unsigned */
msg.push_back((uint8_t)u); /* add value as uint8_t */
}
Таким образом, каждые 2 символа в result
, считываемых с использованием std::setw(2)
, используются для создания 2-символьного потока строкзатем преобразуется в значение unsigned
с использованием std::setbase(16)
.Полный пример:
#include <iostream>
#include <iomanip>
#include <sstream>
#include <string>
#include <vector>
int main (void) {
std::string result ("0123456789abcdef"); /* input hex string */
std::string s2; /* string for 2-chars */
std::istringstream ss (result); /* stringstream of result */
std::vector<uint8_t> msg; /* vector of uint8_t */
while ((ss >> std::setw(2) >> s2)) { /* read 2-char at a time */
unsigned u; /* tmp unsigned value */
std::istringstream ss2 (s2); /* create 2-char stringstream */
ss2 >> std::setbase(16) >> u; /* convert hex to unsigned */
msg.push_back((uint8_t)u); /* add value as uint8_t */
}
std::cout << "string: " << result << "\nmsg: \n";
for (auto& h : msg) /* for each element of msg, output hex value */
std::cout << "\t" << std::setfill('0') << std::hex << std::setw(2)
<< (uint32_t)h << '\n';;
}
( note приведение, требуемое в выходных данных, чтобы явно указать cout
, что нужно обрабатывать значение uint8_t
как значение unsigned
вместоuint8_t
значение, которое по умолчанию соответствует типу символа.
Пример Использование / Вывод
$ ./bin/hexstr2uint8_t
string: 0123456789abcdef
msg:
01
23
45
67
89
ab
cd
ef
( note имеется 8uint8_t
("byte") значения, хранящиеся в этот раз вместо 16-значных символов)
Это просто альтернатива, использующая функции Cost iostream, которая устраняет необходимость разбрасывать вещи вместо непосредственного вызова strtol
(чтов вашем случае, вероятно, должно быть strtoul
для начала).
Ручное шестнадцатеричное преобразование
В своем последнем комментарии вы указываете, что использование iostream и stringstream для преобразованиямедленный. Вы можете попытаться немного оптимизировать, исключив поток строк и используя string::iterator
, чтобы шагать по строке, вручную преобразовывая каждый символ и формируя каждый байт uint8_t
по ходу (защита от окончательного клева или1/2 байта), например,
#include <iostream>
#include <iomanip>
#include <string>
#include <vector>
/* simple manual conversion of hexchar to value */
uint8_t c2hex (const char c)
{
uint8_t u = 0;
if ('0' <= c && c <= '9')
u = c - '0';
else if ('a' <= c && c <= 'f')
u = c - 'W';
else if ('A' <= c && c <= 'F')
u = c - '7';
else
std::cerr << "error: invalid hex char '" << c << "'\n";
return u;
}
int main (void) {
std::string s ("0123456789abcdef");
std::vector<uint8_t> msg;
for (std::string::iterator n = s.begin(); n != s.end(); n += 2) {
uint8_t u = c2hex (*n); /* save high-nibble */
if (n + 1 != s.end()) /* if low-nibble available */
u = (u << 4) | c2hex (n[1]); /* shift high left 4 & or */
msg.push_back(u); /* store byte in msg */
}
std::cout << "string: " << s << "\nmsg:\n";
for (auto& h : msg)
std::cout << "\t" << std::setfill('0') << std::hex
<< std::setw(2) << (unsigned)h << '\n';
}
(вывод такой же, как указано выше)
Если вы можете гарантировать, что в вашей строке всегда будет четное количество символов (только байты)и без 1/2-байта в качестве последнего нечетного символа), вы можете дополнительно оптимизировать, удалив условное выражение и просто используя:
uint8_t u = c2hex (n[1]) | (c2hex (*n) << 4);
Убедитесь, что вы компилируете с полной оптимизацией, например, -O3
(или -Ofast
версия gcc> = 4.6) в gcc / clang и /Ox
с VS.
Попробуйте и сравните производительность, вы можете дополнительно вывести различные версии в сборку и посмотреть, есть лидополнительные подсказки там.