繁体   English   中英

将 MAC 地址 std::string 转换为 uint64_t

[英]Convert MAC address std::string into uint64_t

我有一个保存在 std::string 中的十六进制 MAC 地址。 将该 MAC 地址转换为 uint64_t 中保存的整数类型的最佳方法是什么?

我知道 stringstream、sprintf、atoi 等。我实际上用其中的前 2 个编写了一些转换函数,但它们看起来比我想要的更草率。

所以,有人能告诉我一个好的、干净的转换方法吗

std::string mac = "00:00:12:24:36:4f";

进入 uint64_t?

PS:我没有可用的 boost/TR1 设施,也无法将它们安装在实际使用代码的地方(这也是为什么我没有复制粘贴我的尝试之一,对此感到抱歉。)。 所以请保留直接 C/C++ 调用的解决方案。 如果您有一个有趣的 UNIX 系统调用解决方案,我也会感兴趣!

uint64_t string_to_mac(std::string const& s) {
    unsigned char a[6];
    int last = -1;
    int rc = sscanf(s.c_str(), "%hhx:%hhx:%hhx:%hhx:%hhx:%hhx%n",
                    a + 0, a + 1, a + 2, a + 3, a + 4, a + 5,
                    &last);
    if(rc != 6 || s.size() != last)
        throw std::runtime_error("invalid mac address format " + s);
    return
        uint64_t(a[0]) << 40 |
        uint64_t(a[1]) << 32 |
        uint64_t(a[2]) << 24 |
        uint64_t(a[3]) << 16 |
        uint64_t(a[4]) << 8 |
        uint64_t(a[5]);
}

我的解决方案(需要c ++ 11):

#include <string>
#include <cstdint>
#include <algorithm>
#include <stdlib.h>


uint64_t convert_mac(std::string mac) {
  // Remove colons
  mac.erase(std::remove(mac.begin(), mac.end(), ':'), mac.end());

  // Convert to uint64_t
  return strtoul(mac.c_str(), NULL, 16);
}

使用sscanf:

std::string mac = "00:00:12:24:36:4f";
unsigned u[6];
int c=sscanf(mac.c_str(),"%x:%x:%x:%x:%x:%x",u,u+1,u+2,u+3,u+4,u+5);
if (c!=6) raise_error("input format error");
uint64_t r=0;
for (int i=0;i<6;i++) r=(r<<8)+u[i];
// or:  for (int i=0;i<6;i++) r=(r<<8)+u[5-i];

没有输入数据验证的更多C ++ 11方式:

uint64_t stomac( const std::string &mac )
{
    static const std::regex r{ "([\\da-fA-F]{2})(:|$)" };

    auto it = std::sregex_iterator( mac.begin(), mac.end(), r );
    static const auto end = std::sregex_iterator();

    return std::accumulate( it, end, 0, []( uint64_t i, const std::sregex_iterator::value_type &v ) {
            return ( i << 8 ) + std::stol( v.str(1), nullptr, 16 );
    } );
}

实例

您还可以使用ASCII来struct ether_addr转换例程ether_aton ,或其线程安全版本ether_aton_r (GNU扩展名)。

#include <netinet/ether.h>
#include <stdint.h>

#include <string>

#define ETHER_ADDR_ERR UINT64_C(~0)

uint64_t ether_atou64( const std::string& addr_str ) {
    union {
        uint64_t          result;
        struct ether_addr address;
    };
    result = 0;
    struct ether_addr* ptr = ether_aton_r( addr_str.c_str(), &address );
    if( !ptr ) {
        return ETHER_ADDR_ERR;
    }
    return result;
}

我想不出任何魔术。 这是一个随机尝试,可能会或可能不会比你做的更好。 这简洁,但我敢打赌,解决方案要快得多。

uint64_t mac2int(std::string s) {
    uint64_t r=0;
    std::string::iterator i;
    std::string::iterator end = s.end();

    for(i = s.begin; i != end; ++i) {
        char let = *i;
        if (let >= '0' && let <= '9') { 
            r = r*0xf + (let-'0');
        } else if (let >= 'a' && let <= 'f') { 
            r = r*0xf + (let-'a'+10);
        } else if (let >= 'A' && let <= 'F') { 
            r = r*0xf + (let-'A'+10);
        }
    } 
    return r;
}

我的2美分:

uint64_t ParseMac(const std::string& str)
{
  std::istringstream iss(str);
  uint64_t nibble;
  uint64_t result(0);
  iss >> std::hex;
  while(iss >> nibble) {
    result = (result << 8) + nibble;
    iss.get();
  }
  return result;
}

这只会移动十六进制数字直到字符串用完,而不是关心分隔符或总长度。 但它将输入字符串转换为所需的uint64_t格式。

#include <string>
#include <stdint.h>

uint64_t cvt(std::string &v)
{
    std::string::iterator i;
    std::string digits = "0123456789abcdefABCDEF";
    uint64_t result = 0;
    size_t pos = 0;

    i = v.begin();

    while (i != v.end())
    {
        // search for character in hex digits set
        pos = digits.find(*i);

        // if found in valid hex digits
        if (pos != std::string::npos)
        {
            // handle upper/lower case hex digit
            if (pos > 0xf)
            {
                pos -= 6;
            }

            // shift a nibble in
            result <<= 4;
            result |= pos;
        }

        ++i;
    }

    return result;
}

另一个没有调用库函数的更快版本:

inline unsigned read_hex_byte(char const** beg, char const* end) {
    if(end - *beg < 2)
        throw std::invalid_argument("");
    unsigned hi = (*beg)[0], lo = (*beg)[1];
    *beg += 2;
    hi -= hi >= '0' && hi <= '9' ? '0' :
        hi >= 'a' && hi <= 'f' ? 'a' - 10 :
        hi >= 'A' && hi <= 'F' ? 'A' - 10 :
        throw std::invalid_argument("");
    lo -= lo >= '0' && lo <= '9' ? '0' :
        lo >= 'a' && lo <= 'f' ? 'a' - 10 :
        lo >= 'A' && lo <= 'F' ? 'A' - 10 :
        throw std::invalid_argument("");
    return hi << 4 | lo;
}

uint64_t string_to_mac2(std::string const& s) {
    char const *beg = s.data(), *end = beg + s.size();
    uint64_t r;
    try {
        r = read_hex_byte(&beg, end);
        beg += beg != end && ':' == *beg;
        r = r << 8 | read_hex_byte(&beg, end);
        beg += beg != end && ':' == *beg;
        r = r << 8 | read_hex_byte(&beg, end);
        beg += beg != end && ':' == *beg;
        r = r << 8 | read_hex_byte(&beg, end);
        beg += beg != end && ':' == *beg;
        r = r << 8 | read_hex_byte(&beg, end);
        beg += beg != end && ':' == *beg;
        r = r << 8 | read_hex_byte(&beg, end);
    } catch(std::invalid_argument&) {
        beg = end - 1;
    }
    if(beg != end)
        throw std::runtime_error("invalid mac address format " + s);
    return r;
}

对不起,我还没有发表评论。

对于@AB71E5 的回答,您需要将“strtoul”更改为“strtoull”。 例如:01:02:03:04:05:06 = 48 位,但“unsigned long”= 32 位。

最后的结果是:

#include <string>
#include <cstdint>
#include <algorithm>
#include <stdlib.h>


uint64_t convert_mac(std::string mac) {
  // Remove colons
  mac.erase(std::remove(mac.begin(), mac.end(), ':'), mac.end());

  // Convert to uint64_t
  return strtoull(mac.c_str(), NULL, 16);
}

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM