获取使用大端格式 C# 进行编码的 guid
Get a guid to encode using big-endian formatting C#
我有一个不寻常的情况,我有一个使用二进制 (16) 主键的现有 MySQL 数据库,这些是现有 api 中使用的 UUID 的基础。
我的问题是我现在想添加一个用 dotnet 核心编写的替换 api,我 运行 遇到了编码问题 here
具体来说,dotnet 中的 Guid 结构使用混合端格式,生成与现有 api 不同的字符串。由于显而易见的原因,这是不可接受的。
所以我的问题是:是否有一种优雅的方法可以强制 Guid 结构完全使用大端格式进行编码?
如果没有,我只能写一个糟糕的 hack,但我想我会先咨询 SO 社区的集体智慧!
没有;据我所知,没有内置的方法可以做到这一点。是的,Guid
有我目前只能称为 "crazy-endian" 的实现。您需要获取 Guid
排序的位(通过 unsafe
或 Guid.ToByteArray
),然后手动对它们进行排序,找出要反转的块 - 这不是一个简单的 Array.Reverse()
。所以:恐怕非常手动。我建议使用
这样的 guid
00010203-0405-0607-0809-0a0b0c0d0e0f
进行调试;这给了你(我怀疑你知道):
03-02-01-00-05-04-07-06-08-09-0A-0B-0C-0D-0E-0F
所以:
- 反转 4
- 反转 2
- 反转 2
- 直 8
截至 2021 年,在 C# 中仍然没有将 System.Guid
转换为 MySQL 兼容大端字符串的内置方法。
这是我们在工作中遇到这个确切的 C# 混合端 Guid 问题时想到的扩展:
public static string ToStringBigEndian(this Guid guid)
{
// allocate enough bytes to store Guid ASCII string
Span<byte> result = stackalloc byte[36];
// set all bytes to 0xFF (to be able to distinguish them from real data)
result.Fill(0xFF);
// get bytes from guid
Span<byte> buffer = stackalloc byte[16];
_ = guid.TryWriteBytes(buffer);
int skip = 0;
// iterate over guid bytes
for (int i = 0; i < buffer.Length; i++)
{
// indices 4, 6, 8 and 10 will contain a '-' delimiter character in the Guid string.
// --> leave space for those delimiters
if (i is 4 or 6 or 8 or 10)
{
skip++;
}
// stretch high and low bytes of every single byte into two bytes (skipping '-' delimiter characters)
result[(2 * i) + skip] = (byte)(buffer[i] >> 0x4);
result[(2 * i) + 1 + skip] = (byte)(buffer[i] & 0x0Fu);
}
// iterate over precomputed byte array.
// values 0x0 to 0xF are final hex values, but must be mapped to ASCII characters.
// value 0xFF is to be mapped to '-' delimiter character.
for (int i = 0; i < result.Length; i++)
{
// map bytes to ASCII values (a-f will be lowercase)
ref byte b = ref result[i];
b = b switch
{
0xFF => 0x2D, // Map 0xFF to '-' character
< 0xA => (byte)(b + 0x30u), // Map 0x0 - 0x9 to '0' - '9'
_ => (byte)(b + 0x57u) // Map 0xA - 0xF to 'a' - 'f'
};
}
// get string from ASCII encoded guid byte array
return Encoding.ASCII.GetString(result);
}
它有点长,但除了大端字符串之外 returns 它不进行堆分配,因此保证速度很快 :)
我有一个不寻常的情况,我有一个使用二进制 (16) 主键的现有 MySQL 数据库,这些是现有 api 中使用的 UUID 的基础。
我的问题是我现在想添加一个用 dotnet 核心编写的替换 api,我 运行 遇到了编码问题 here
具体来说,dotnet 中的 Guid 结构使用混合端格式,生成与现有 api 不同的字符串。由于显而易见的原因,这是不可接受的。
所以我的问题是:是否有一种优雅的方法可以强制 Guid 结构完全使用大端格式进行编码?
如果没有,我只能写一个糟糕的 hack,但我想我会先咨询 SO 社区的集体智慧!
没有;据我所知,没有内置的方法可以做到这一点。是的,Guid
有我目前只能称为 "crazy-endian" 的实现。您需要获取 Guid
排序的位(通过 unsafe
或 Guid.ToByteArray
),然后手动对它们进行排序,找出要反转的块 - 这不是一个简单的 Array.Reverse()
。所以:恐怕非常手动。我建议使用
00010203-0405-0607-0809-0a0b0c0d0e0f
进行调试;这给了你(我怀疑你知道):
03-02-01-00-05-04-07-06-08-09-0A-0B-0C-0D-0E-0F
所以:
- 反转 4
- 反转 2
- 反转 2
- 直 8
截至 2021 年,在 C# 中仍然没有将 System.Guid
转换为 MySQL 兼容大端字符串的内置方法。
这是我们在工作中遇到这个确切的 C# 混合端 Guid 问题时想到的扩展:
public static string ToStringBigEndian(this Guid guid)
{
// allocate enough bytes to store Guid ASCII string
Span<byte> result = stackalloc byte[36];
// set all bytes to 0xFF (to be able to distinguish them from real data)
result.Fill(0xFF);
// get bytes from guid
Span<byte> buffer = stackalloc byte[16];
_ = guid.TryWriteBytes(buffer);
int skip = 0;
// iterate over guid bytes
for (int i = 0; i < buffer.Length; i++)
{
// indices 4, 6, 8 and 10 will contain a '-' delimiter character in the Guid string.
// --> leave space for those delimiters
if (i is 4 or 6 or 8 or 10)
{
skip++;
}
// stretch high and low bytes of every single byte into two bytes (skipping '-' delimiter characters)
result[(2 * i) + skip] = (byte)(buffer[i] >> 0x4);
result[(2 * i) + 1 + skip] = (byte)(buffer[i] & 0x0Fu);
}
// iterate over precomputed byte array.
// values 0x0 to 0xF are final hex values, but must be mapped to ASCII characters.
// value 0xFF is to be mapped to '-' delimiter character.
for (int i = 0; i < result.Length; i++)
{
// map bytes to ASCII values (a-f will be lowercase)
ref byte b = ref result[i];
b = b switch
{
0xFF => 0x2D, // Map 0xFF to '-' character
< 0xA => (byte)(b + 0x30u), // Map 0x0 - 0x9 to '0' - '9'
_ => (byte)(b + 0x57u) // Map 0xA - 0xF to 'a' - 'f'
};
}
// get string from ASCII encoded guid byte array
return Encoding.ASCII.GetString(result);
}
它有点长,但除了大端字符串之外 returns 它不进行堆分配,因此保证速度很快 :)