Universally Unique Identifier,UUID,通用唯一识别码。是用于计算机体系中以识别信息数目的一个128位标识符,这个东西很有用,在分布式系统中经常用于标识一个结点。根据标准方法生成,不依赖中央机构的注册和分配,UUID具有唯一性,这与其他大多数编号方案不同。重复UUID码概率接近零,可以忽略不计。
UUID是128位,16个字节,可以用32个HEX进制的数字表示。标准的表示格式是8-4-4-4-12总共 36 个字符(32 个字母数字字符和 4 个连字符),如下所示:
123E4567-E89B-12D3-A456-426655440F00
其实你自己也可以搞这么长的字符串作为UUID,但是UUID的质量取决于重复率。如果你的UUID质量不行,那么很大可能就会生成重复的UUID,这样子会出问题。
因此,UUID的标准定义了5个版本的UUID,分别如下:
“版本1” UUID 是根据时间和节点 ID(通常是MAC地址)生成;
“版本2” UUID是根据标识符(通常是组或用户ID)、时间和节点ID生成;
“版本3” 和 “版本5” 确定性UUID 通过散列 (hashing) 名字空间 (namespace) 标识符和名称生成;
“版本4” UUID 使用随机性或伪随机性生成。
所以,UUID的好坏最终还是落到了随机生成器的质量上
那么在Windows/Linux下如何生成一个高质量的UUID呢?
可以使用boost准标准库,代码如下:
#include <boost/uuid/uuid.hpp>
#include <boost/uuid/uuid_io.hpp>
#include <boost/uuid/uuid_generators.hpp>
boost::uuids::uuid rand= boost::uuids::random_generator()();
const string uuid_string = boost::uuids::to_string(rand);
这是通用的,下面说一下在Windows和Linux这两个平台下C++程序如何生成UUID。
Windows下利用Windows系统API就好了。有一个结构叫GUID,配合上CoCreateGuid就行。
#include <objbase.h>
HRESULT CoCreateGuid(
GUID *pguid
);
typedef struct _GUID {
unsigned long Data1;
unsigned short Data2;
unsigned short Data3;
unsigned char Data4[ 8 ];
} GUID;
sizeof(GUID) = 16,刚好是128位。
注意,UUID在许多系统上都是大端序编码UUID,但是在Windows下采用混合段格式,即UUID的前三组(8-4-4)用小端序,后两组(4-12)用大端序。
Linux
Linux下利用libuuid这个库来生成,Ubuntu下可用sudo apt-get install uuid-dev来安装,在链接的时候加上-luuid就行了。使用man 3 uuid可以看看支持哪些方法,或者直接在头文件中搜一下uuid_t
这个库支持生成UUID的不同算法,可以对应着UUID的五个版本看看。
sizeof(uuid_t) = 16,刚好128个字节。
跨平台封装
现在Windows下和Linux下生成可靠UUID的代码方法都知道了,我们就自己封装一个跨平台的UUID类Uuid。
// uuid.h
#pragma once
#include <stdint.h>
#include <array>
#include <string>
class Uuid
{
public:
Uuid();
~Uuid();
static Uuid GenerateRandom();
std::string toString(const std::string& split = "-", bool up = true);
private:
typedef uint8_t ValueType;
static const size_t DATA_LENGTH = 16;
friend bool operator==(Uuid const & lhs, Uuid const & rhs) noexcept;
friend bool operator<(Uuid const & lhs, Uuid const & rhs) noexcept;
std::array<ValueType, DATA_LENGTH> _data;
};
// uuid.c
#include "uuid.h"
#include <sstream>
#include <iomanip>
#include <ios>
#ifdef _WIN32
#include <objbase.h>
#else
#include <uuid/uuid.h>
#endif
namespace Zeus
{
Uuid::Uuid()
:_data{ 0 }
{
}
Uuid::~Uuid()
{
}
Uuid Uuid::GenerateRandom()
{
Uuid result;
#ifdef _WIN32
GUID newId;
::CoCreateGuid(&newId);
std::array<ValueType, DATA_LENGTH> bytes =
{ {
// Big-Endian
(ValueType)((newId.Data1 >> 24) & 0xFF),
(ValueType)((newId.Data1 >> 16) & 0xFF),
(ValueType)((newId.Data1 >> 8) & 0xFF),
(ValueType)((newId.Data1) & 0xFF),
(ValueType)((newId.Data2 >> 8) & 0xFF),
(ValueType)((newId.Data2) & 0xFF),
(ValueType)((newId.Data3 >> 8) & 0xFF),
(ValueType)((newId.Data3) & 0xFF),
// Little-Endian
newId.Data4[0],
newId.Data4[1],
newId.Data4[2],
newId.Data4[3],
newId.Data4[4],
newId.Data4[5],
newId.Data4[6],
newId.Data4[7]
} };
result._data = bytes;
#else
uuid_t id;
uuid_generate(id);
std::array<ValueType, DATA_LENGTH> bytes =
{ {
id[0],
id[1],
id[2],
id[3],
id[4],
id[5],
id[6],
id[7],
id[8],
id[9],
id[10],
id[11],
id[12],
id[13],
id[14],
id[15]
} };
result._data = bytes;
#endif
return result;
}
std::string Uuid::toString(const std::string& split, bool up)
{
std::stringstream str;
if (up)
{
str.setf(std::ios_base::uppercase);
}
else
{
str.unsetf(std::ios_base::uppercase);
}
str << std::hex << std::setfill('0')
<< std::setw(2) << (int)_data[0]
<< std::setw(2) << (int)_data[1]
<< std::setw(2) << (int)_data[2]
<< std::setw(2) << (int)_data[3]
<< split
<< std::setw(2) << (int)_data[4]
<< std::setw(2) << (int)_data[5]
<< split
<< std::setw(2) << (int)_data[6]
<< std::setw(2) << (int)_data[7]
<< split
<< std::setw(2) << (int)_data[8]
<< std::setw(2) << (int)_data[9]
<< split
<< std::setw(2) << (int)_data[10]
<< std::setw(2) << (int)_data[11]
<< std::setw(2) << (int)_data[12]
<< std::setw(2) << (int)_data[13]
<< std::setw(2) << (int)_data[14]
<< std::setw(2) << (int)_data[15];
return str.str();
}
bool operator==(Uuid const & lhs, Uuid const & rhs) noexcept
{
return lhs._data == rhs._data;
}
bool operator<(Uuid const & lhs, Uuid const & rhs) noexcept
{
return lhs._data < rhs._data;
}
最后来使用一下:
#include "uuid.h"
int main()
{
std::cout << Uuid::GenerateRandom().toString() << std::endl;
std::cout << Uuid::GenerateRandom().toString("") << std::endl;
std::cout << Uuid::GenerateRandom().toString("", false) << std::endl;
return 0;
}
输出的结果:
9747E266-0367-11E9-B6C5-0800274F3274
8FB2028E036711E9AF550800274F3274
54293386036711e990b40800274f3274