【问题标题】:Convert vector<string> to unsigned char array in C++在 C++ 中将 vector<string> 转换为无符号字符数组
【发布时间】:2017-04-18 06:17:44
【问题描述】:

我有一个包含一些值的字符串向量。这些值应该是十六进制字节,但在此向量中存储为字符串。 字节实际上是从文本文件中读取的,如下所示:

文本文件的内容

<jpeg1>
0xFF,0xD8,0xFF,0xE0,0x00,0x10,0x4A,0x46,0x49,0x46,0x00,0x01,0x01,0x01,0x00,0x60
</jpeg1>

到目前为止,我的代码所做的是,它开始读取 {JPEG1} 标记之后的行,直到 {/jpeg1} 标记,然后使用逗号 ' ,' 作为分隔符,它将字节存储到字符串向量中。

拆分字符串后,此时的向量存储如下值:

vector<string> myString = {"0xFF", "0xD8", "0xFF", "0xE0", "0x00", "0x10", "0x4A", "0x46", "0x49", "0x46", "0x00", "0x01", "0x01", "0x01", "0x00", "0x60"};

        and if i print this i get the following:
            0: 0xFF
            1: 0xD8
            2: 0xFF
            3: 0xE0
            4: 0x00
            5: 0x10
            6: 0x4A
            7: 0x46
            8: 0x49
            9: 0x46

我想要的是,我想将这些字节存储在一个 unsigned char 数组中,这样每个元素都被视为 HEX 字节而不是字符串值。

最好是这样的:

     unsigned char myHexArray[] = {0xFF,0xD8,0xFF,0xE0,0x00,0x10,0x4A,0x46,0x49,0x46,0x00,0x01,0x01,0x01,0x00,0x60};

        if i print this i get:
            0:  
            1: ╪
            2:  
            3: α
            4:
            5: 
            6: J
            7: F
            8: I
            9: F

解决了!
感谢您的帮助,到目前为止“ranban282”解决方案对我有用,我也会尝试其他用户提供的解决方案。

【问题讨论】:

  • 你首先需要字符串向量吗?
  • 这样问,是stackoverflow.com/questions/1070497/…的重复。
  • 您甚至可以从标签之间提取文本源并将其包含在 C++ 源中...
  • @n.m 不需要。我使用向量是因为我使用(从 stackoverflow 复制)分割字符串的函数使用向量。
  • 最终需要的是,从文本文件中读取字节并将它们存储到某种无符号字符数组中。 :)

标签: c++ arrays vector char type-conversion


【解决方案1】:

我什至不会经历std::vector&lt;std::string&gt; 阶段,你不需要它,它会无缘无故地浪费大量分配;只需将字符串解析为“在线”字节即可。

如果您的数据已经有一个 istream,您可以直接从中解析它,尽管我在性能方面的经历很糟糕。

// is is some derived class of std::istream
std::vector<unsigned char> ret;
while(is) {
    int val = 0;
    is>>std::hex>>val;
    if(!is) {
        break; // failed conversion; remember to clean up the stream
               // if you need it later!
    }
    ret.push_back(val);
    if(is.getc()!=',') break;
}

如果你把它放在一个字符串中——就像从 XML 文件中提取数据时经常发生的那样,你可以使用 istringstream 和上面的代码来解析它(一个额外的字符串副本 + 通常很慢),或者解析它直接从字符串使用例如sscanf%i;说你的字符串在const char *sz:

std::vector<unsigned char> ret;
for(; *sz; ++sz) {
    int read = 0;
    int val = 0;
    if(sscanf(sz, " %i %n", &val, &read)==0) break; // format error
    ret.push_back(val):
    sz += read;
    if(*sz && *sz != ',') break; // format error
} 
// now ret contains the decoded string

如果您确定字符串始终是十六进制的,无论0x 前缀是什么,并且不存在空格,那么strtol 会更高效且 IMO 更易于使用:

std::vector<unsigned char> ret;
for( ;*sz;++sz) {
    char *endp;
    long val = strtol(sz, &endp, 16);
    if(endp==sz) break; // format error
    sz = endp;
    ret.push_back(val);
    if(*sz && *sz!=',') break; // format error
}

如果 C++17 可用,您可以使用 std::from_chars 而不是 strtol 来消除语言环境的废话,这会破坏您的解析功能(尽管这对于浮点解析更为典型)并减慢它的速度没有充分的理由。

OTOH,如果性能很关键但 from_chars 不可用(或者如果它可用但您测量它很慢),手动滚动整个解析器可能是有利的。

auto conv_digit = [](char c) -> int {
    if(c>='0' && c<='9') return c-'0';
    // notice: technically not guaranteed to work;
    // in practice it'll work on anything that doesn't use EBCDIC
    if(c>='A' && c<='F') return c-'A'+10;
    if(c>='a' && c<='f') return c-'a'+10;
    return -1;
};
std::vector<unsigned char> ret;
for(; *sz; ++sz) {
    while(*sz == ' ') ++sz;
    if(*sz!='0' || sz[1]!='x' || sz[1]!='X') break; // format error
    sz+=2;
    int val = 0;
    int digit = -1;
    const char *sz_before = sz;
    while((digit = conv_digit(*sz)) >= 0) {
        val=val*16+digit; // or, if you prefer: val = val<<4 | digit;
        ++sz;
    }
    if(sz==sz_before) break; // format error
    ret.push_back(val);
    while(*sz == ' ') ++sz;
    if(*sz && *sz!=',') break; // format error
}

【讨论】:

  • @n.m.:处理解析问题的 C++ 惯用方式是什么? std::istringstream? boost::spirit? std::locale::use_face&lt;whatever&gt;::some_other_ridicolous_function_name_that_ultimately_calls_sscanf?不要逗我笑……如果“C++ 方式”是对 C 方式的回归,让我们保留旧的方式。
  • (我认为处理此任务的唯一更好的方法实际上是strtol,尽管它没有%i 那样的“免费基础”优势,或者,如果速度是真的很重要,我们可以去掉语言环境的废话,手动解析器)
  • 为什么ret 类型为std::string 而反对使用std::vector
  • std::istringstream 将是简单的 C++ 方式,boost::spirit 可能会免费为您提供更多错误检查,我不知道为什么要在图片中添加方面和语言环境。
  • 无论如何答案现在有更多代码,所以评论不那么相关。
【解决方案2】:

如果您使用的是 C++11,则可以使用 stoi 函数。

vector<string> myString = {"0xFF", "0xD8", "0xFF", "0xE0", "0x00", "0x10", "0x4A", "0x46", "0x49", "0x46", "0x00", "0x01", "0x01", "0x01", "0x00", "0x60"};
    unsigned char* myHexArray=new unsigned char[myString.size()];
    for (unsigned  i=0;i<myString.size();i++)
    {
            myHexArray[i]=stoi(myString[i],NULL,0);
    }
    for (unsigned i=0;i<myString.size();i++)
    {
            cout<<myHexArray[i]<<endl;
    }

函数 stoi() 是由 C++11 引入的。为了使用 gcc 进行编译,您应该使用标志 -std=c++11 进行编译。

如果您使用的是旧版本的 c++,您可以使用 strtol 而不是 stoi。请注意,您需要先将字符串转换为字符数组。

myHexArray[i]=strtol(myString[i].c_str(),NULL,0);

【讨论】:

  • 这是什么 unsigned char* 废话?字节向量有什么问题?
  • @ranban,我正在使用带有 mingw 4.9.2 的代码块,编译器已经设置为使用 c++11。我得到“Stoi”没有在范围内声明。使用 std::stoi 会出现同样的错误“stoi is not a member of std”
  • 我读到 stoi 不是代码块使用的 minGW 中 std 命名空间的成员。你是如何让这个解决方案发挥作用的?你用过strtol吗?
  • 好吧,显然代码块有一个错误。我在网上搜索,有人建议我使用 TDM-GCC-Mingw 编译器。我从这里下载并安装它:sourceforge.net/projects/tdm-gcc,然后将其用作代码块的编译器。它现在可以工作了:)
  • 太好了,如果您觉得有帮助,请点赞。
【解决方案3】:

您可以在每个值上使用std::stoul,并使用另一个std::vector 构建您的数组,如下所示:

std::vector<std::string> vs {"0xFF", "0xD8", "0xFF" ...};

std::vector<unsigned char> vc;
vc.reserve(vs.size());

for(auto const& s: vs)
    vc.push_back((unsigned char) std::stoul(s, 0, 0));

现在您可以通过以下方式访问您的数组:

vc.data(); // <-- pointer to unsigned char array

【讨论】:

    【解决方案4】:

    这是一个完整的解决方案,包括一个测试和一个基本的解析器(为简单起见,它假设 xml 标记在它们自己的行上)。

    #include <string>
    #include <sstream>
    #include <regex>
    #include <iostream>
    #include <iomanip>
    #include <iterator>
    
    const char test_data[] =
    R"__(<jpeg1>
    0xFF,0xD8,0xFF,0xE0,0x00,0x10,0x4A,0x46,0x49,0x46,0x00,0x01,0x01,0x01,0x00,0x60,
    0x12,0x34,0x56,0x78,0x9a,0xbc,0xde,0xf0
    </jpeg1>)__";
    
    
    struct Jpeg
    {
        std::string name;
        std::vector<std::uint8_t> data;
    };
    
    std::ostream& operator<<(std::ostream& os, const Jpeg& j)
    {
        os << j.name << " : ";
        const char* sep = " ";
        os << '[';
        for (auto b : j.data) {
            os << sep << std::hex << std::setfill('0') << std::setw(2) << std::uint32_t(b);
            sep = ", ";
        }
        return os << " ]";
    
    }
    
    template<class OutIter>
    OutIter read_bytes(OutIter dest, std::istream& source)
    {
        std::string buffer;
        while (std::getline(source, buffer, ','))
        {
            *dest++ = static_cast<std::uint8_t>(std::stoul(buffer, 0, 16));
        }
        return dest;
    }
    
    Jpeg read_jpeg(std::istream& is)
    {
        auto result = Jpeg {};
        static const auto begin_tag = std::regex("<jpeg(.*)>");
        static const auto end_tag = std::regex("</jpeg(.*)>");
        std::string line, hex_buffer;
        if(not std::getline(is, line)) throw std::runtime_error("end of file");
        std::smatch match;
        if (not std::regex_match(line, match, begin_tag)) throw std::runtime_error("not a <jpeg_>");
        result.name = match[1];
    
        while (std::getline(is, line))
        {
            if (std::regex_match(line, match, end_tag)) { break; }
            std::istringstream hexes { line };
            read_bytes(std::back_inserter(result.data), hexes);
        }
    
    
        return result;
    }
    
    int main()
    {
        std::istringstream input_stream(test_data);
        auto jpeg = read_jpeg(input_stream);
    
        std::cout << jpeg << std::endl;
    }
    

    预期输出:

    1 : [ ff, d8, ff, e0, 00, 10, 4a, 46, 49, 46, 00, 01, 01, 01, 00, 60, 12, 34, 56, 78, 9a, bc, de, f0 ]
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2021-11-01
      • 2017-09-17
      • 2018-09-13
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2019-05-29
      相关资源
      最近更新 更多