将整数转换为 GUID

57

我需要将Int32转换为Guid,这是我想到的方法。

public static class IntExtensions
{
    public static Guid ToGuid(this Int32 value)
    {
        if (value >= 0) // if value is positive
            return new Guid(string.Format("00000000-0000-0000-0000-00{0:0000000000}", value));
        else if (value > Int32.MinValue) // if value is negative
            return new Guid(string.Format("00000000-0000-0000-0000-01{0:0000000000}", Math.Abs(value)));
        else //if (value == Int32.MinValue)
            return new Guid("00000000-0000-0000-0000-012147483648");  // Because Abs(-12147483648) generates a stack overflow due to being > 12147483647 (Int32.Max)
    }
}

但是这样看起来有点丑陋。有没有更好的想法?

更新:

是的,我知道整个东西看起来很丑陋,但是我已经没有更好的想法了。 问题是:我正在获取数据并将其存储到无法更改的表格中。发送数据的主键是 Int ,而我必须将其存储为 Guid 的表主键。 问题在于我必须理解发送方所说的对象,但只能将其存储为 Guid。

更新2:

好的,我看到我需要在这里提供更多信息。我是一个接收数据的 Web 服务,并且必须将数据传递给一个我也无法控制的接口。因此,我既不能对接收到的数据进行建模,也不能对我必须发送数据的(接口)数据库进行建模。此外,我还必须以某种方式映射这两个事物,以便我可以以某种方式更新项目。叹气


2
一个"INT"是4字节 - 一个GUID是16字节 - 这怎么可能能正常工作?!?! - marc_s
3
这只会变得很丑。当你试图将三轮车改造成摩托车时就会发生这种情况。 - Cody Gray
1
您需要告诉我们您的要求。您是否需要能够反向此过程?这些ID需要在全局范围内还是本地范围内唯一?这是短期解决方案,还是这些ID将永久用于您的数据库中?=) - Jens
2
创建一个包含这两个键的映射表不是更可靠吗? - Filburt
1
可能是从int创建Guid的重复问题。 - StayOnTarget
显示剩余2条评论
5个回答

90

这里是一个简单的实现方式:

public static Guid ToGuid(int value)
{
    byte[] bytes = new byte[16];
    BitConverter.GetBytes(value).CopyTo(bytes, 0);
    return new Guid(bytes);
}

你可以更改复制发生的位置(将索引从0到12变化)。这实际上取决于您想如何定义这种不寻常的“int到Guid”的转换。


3
非常感谢你,Simon。我一直在寻找像这样更清晰的东西,尽管我必须考虑解决我的问题的选项。我缺乏如此多的控制,以至于我可能真的不得不这样做。再次感谢你。 - psibot

56

我遇到了相同的问题,需要将Int转换为Guid再转回Int。旧数据使用的是int ID,但处理函数需要一个Guid。写额外的函数和进行数据库更改要花费很多代码。只需将Int iD以Guid形式传递,因为知道它不会作为最终保存Guid使用,这样更容易。保存是插入操作,所以在最后会获得一个新的Guid。

以下是上面的代码以及另一篇关于Guid转换为int并获取Int的帖子的想法。

public static Guid Int2Guid(int value)
{
    byte[] bytes = new byte[16];
    BitConverter.GetBytes(value).CopyTo(bytes, 0);
    return new Guid(bytes);
}

public static int Guid2Int(Guid value)
{
    byte[] b = value.ToByteArray();
    int bint = BitConverter.ToInt32(b, 0);
    return bint;
}

这个Guid2Int函数只应该传入一个来自Int的Guid。


字符串版本: public static Guid String2Guid(string value) { byte[] bytes = new byte[16]; Encoding.ASCII.GetBytes(value).CopyTo(bytes, 0); return new Guid(bytes); }public static string Guid2String(Guid value) { byte[] b = value.ToByteArray(); ASCIIEncoding encoder = new ASCIIEncoding(); string s = encoder.GetString(b).Trim('\0'); return s; } - user326608

6

你可以从一个整数中提取数字并格式化它们,使其看起来像一个GUID,但这并不意味着结果是一个GUID。GUID基本上是一个16字节的数字,使用一种算法计算得出,保证该数字是唯一的。你可以在全世界的每台计算机上生成GUID,而不会得到重复的结果(至少理论上是这样)。重新格式化的整数不是唯一的,肯定不是GUID。


+1,唯一剩下的就是:http://en.wikipedia.org/wiki/Globally_unique_identifier,去探索生成它们的各种精彩算法。 - Tim Barrass
是的,但在 GUID 的规范中是否有一种将经典标识符转换为 GUID 的方法?我在考虑一个保留字符。比方说当 GUID 以 0 开头时,您可以想要做什么,但不能保证结果。因此,所有以 0 开头的 GUID 表示它们是手动生成的,没有使用算法。 - Bastien Vandamme

3
我也需要将数据库的一个主键(uint类型)编码为GUID。关于是否使用uint作为主键或使用GUID存在很多讨论,我不会在这里深入介绍,但我使用了uint作为主键,并在列中使用了GUID,我想将主键编码到GUID中,并从GUID中获取它。
下面的解决方案将24位主键(uint类型)编码为GUID,并将其解码。它对uint使用了DES加密。我知道问题想要一个32位int,这适用于24位uint,但可以通过连接两个GUID(一个具有最高的24位,另一个具有最低的24位)来实现32位。该算法已经进行了全面测试,并且可以用于int,但未测试负数。
GUID格式
GUID格式为32位数字,可以用连字符分隔,并包括大括号:00000000-0000-0000-0000-000000000000(D),其中8-4-4-12个字符,即4-2-2-6个字节,因为一个字节被编码成2个字符。还存在其他格式的GUID。
将3个字节的uint加密为8个字节的代码,并写入GUID中,从第5个字节(忽略括号和'-'分隔符的第10个字符)开始。让“z”表示编码的int,0表示任何其他十六进制字符。得到的编码GUID为:
00000000-zzzz-zzzz-zzzz-zzzz00000000

需要注意的是,此时生成的GUID可能不是全局唯一的。然而,由于GUID编码了您的主键,它将对于您的主键是唯一的,并且很难在没有加密密钥的情况下从GUID中提取uint值......尽管DES加密非常安全。

public void Test()
{  
    Guid NewG = Guid.NewGuid();
    Guid EnryptedGuid = Utility.EncodeInt24InGUID(NewG, Num);
    uint RestoredUint = Utility.DecodeInt24FromGUID(EnryptedGuid);
}

加密

public static Guid EncodeInt24InGUID(Guid guid, uint x)
{
    if (x >= Math.Pow(2, 24))
            throw new ArgumentOutOfRangeException("Unsigned integer is greater than 24bit");

    string strGuid = guid.ToString();
    strGuid = Utility.RemoveChars(strGuid, "-{}");//Remove any '-' and '{}' characters
    byte[] bytes = BitConverter.GetBytes(x);
    var encryptedarray = Cypher.EncryptDES(bytes, Cypher.Key);
    string EncryptedGuid = WriteBytesToString(strGuid, encryptedarray, 9);
    Guid outg;
    Guid.TryParse(EncryptedGuid, out outg);
    return outg;
}

RemoveChars是从此处复制/改编而来:使用LINQ从字符串中删除字符

WriteBytesToString如下:

public static string WriteBytesToString(string Input, byte[] bytes, int start)
{
    StringBuilder g = new StringBuilder(Input);
    string temp;
    int ByteNum = 0;
    int CharPos = start;
    int NumBytes = (int)bytes.LongLength; 
    for (int i = 0; i < NumBytes; i++)
    {
        temp = string.Format("{0:x2}", bytes[ByteNum++]);
        g[CharPos++] = (temp.ToCharArray())[0];
        g[CharPos++] = (temp.ToCharArray())[1];
    }
    return g.ToString();
}

解密

public static uint DecodeInt24FromGUID(Guid guid)
{
    string strGuid = guid.ToString();
    strGuid = Utility.RemoveChars(strGuid, "-{}");
    byte[] EncryptedBytes = GetBytesFromString(strGuid, 9,8);
    var decrypted = Cypher.DecryptDES(EncryptedBytes, Cypher.Key);
    uint DecryptedUint = BitConverter.ToUInt32(decrypted, 0);
    return DecryptedUint;
}

GetBytesFromString。请注意,字节从第9个索引开始,并且在调用函数中硬编码为8个字节。有关GUID格式的说明,请参见注释。

public static byte[] GetBytesFromString(string Input, int start, int NumBytes)
{
    StringBuilder g = new StringBuilder(Input);
    byte[] Bytes = new byte[NumBytes];
    string temp;
    int CharPos = start;
    for (int i = 0; i < NumBytes; i++)
    {
        temp = g[CharPos++].ToString();
        temp += g[CharPos++].ToString();
        Bytes[i] =  byte.Parse(temp, System.Globalization.NumberStyles.HexNumber);
    }
    return Bytes;
}

这个网络课程是从这里https://www.codeproject.com/Articles/5719/Simple-encrypting-and-decrypting-data-in-C复制过来的,并经过改编以适用于DES加密。以下是完整的内容:
using System.IO;
using System.Security.Cryptography;
namespace UtilityLib
{
    // This class is adapted from https://www.codeproject.com/Articles/5719/Simple-encrypting-and-decrypting-data-in-C
    public static class Cypher
    {
        public static string Key = "Asd9847Fg85ihkn52s";
        // Encrypt a byte array into a byte array using a key and an IV 
        public static byte[] EncryptDES(byte[] clearData, byte[] Key, byte[] IV)
        {
            // Create a MemoryStream to accept the encrypted bytes 
            MemoryStream ms = new MemoryStream();
            TripleDES alg = TripleDES.Create();
            alg.Key = Key;
            alg.IV = IV;
            CryptoStream cs = new CryptoStream(ms,alg.CreateEncryptor(), CryptoStreamMode.Write);
            cs.Write(clearData, 0, clearData.Length);
            cs.Close();
            byte[] encryptedData = ms.ToArray();
            return encryptedData;
        }

        public static byte[] EncryptDES(byte[] clearData, string Password)
        {
            PasswordDeriveBytes pdb = new PasswordDeriveBytes(Password,
                new byte[] {0x49, 0x76, 0x61, 0x6e, 0x20, 0x4d,
            0x65, 0x64, 0x76, 0x65, 0x64, 0x65, 0x76});
            return EncryptDES(clearData, pdb.GetBytes(24), pdb.GetBytes(8));
        }
        public static byte[] DecryptDES(byte[] cipherData, byte[] Key, byte[] IV)
        {
            MemoryStream ms = new MemoryStream();
            TripleDES alg = TripleDES.Create(); 
            alg.Key = Key;
            alg.IV = IV;
            CryptoStream cs = new CryptoStream(ms, alg.CreateDecryptor(), CryptoStreamMode.Write);
            cs.Write(cipherData, 0, cipherData.Length);
            cs.Close();
            byte[] decryptedData = ms.ToArray();
            return decryptedData;
        }

        public static byte[] DecryptDES(byte[] cipherData, string Password)
        {
            PasswordDeriveBytes pdb = new PasswordDeriveBytes(Password,
                new byte[] {0x49, 0x76, 0x61, 0x6e, 0x20, 0x4d,
            0x65, 0x64, 0x76, 0x65, 0x64, 0x65, 0x76});
            return DecryptDES(cipherData, pdb.GetBytes(24), pdb.GetBytes(8));
        }
    }
}

2

我会更进一步。

有时需要想出一种方法在intGuid之间来回切换。可以添加两个甚至四个int的复合ID来混合使用。

我想到了这个:

[StructLayout(LayoutKind.Explicit, Size = 16)]
public struct GuidToInt32 :
    IEquatable<GuidToInt32>
{
    public static readonly GuidToInt32 Empty;

    [FieldOffset(0)]
    private readonly Guid guidValue;

    [FieldOffset(sizeof(int) * 0)]
    private readonly int int32Value1;

    [FieldOffset(sizeof(int) * 1)]
    private readonly int int32Value2;

    [FieldOffset(sizeof(int) * 2)]
    private readonly int int32Value3;

    [FieldOffset(sizeof(int) * 3)]
    private readonly int int32Value4;

    public GuidToInt32(Guid guidValue)
    {
        this.int32Value1 = default;
        this.int32Value2 = default;
        this.int32Value3 = default;
        this.int32Value4 = default;

        this.guidValue = guidValue;
    }

    public GuidToInt32(int int32Value1, int int32Value2 = default, int int32Value3 = default, int int32Value4 = default)
    {
        this.guidValue = default;

        this.int32Value1 = int32Value1;
        this.int32Value2 = int32Value2;
        this.int32Value3 = int32Value3;
        this.int32Value4 = int32Value4;
    }

    public Guid GuidValue => this.guidValue;

    public int Int32Value1 => this.int32Value1;

    public int Int32Value2 => this.int32Value2;

    public int Int32Value3 => this.int32Value3;

    public int Int32Value4 => this.int32Value4;

    public static bool operator ==(GuidToInt32 leftValue, GuidToInt32 rightValue)
    {
        return leftValue.guidValue == rightValue.guidValue;
    }

    public static bool operator !=(GuidToInt32 leftValue, GuidToInt32 rightValue)
    {
        return leftValue.guidValue != rightValue.guidValue;
    }

    public static bool Equals(GuidToInt32 valueA, GuidToInt32 valueB)
    {
        return valueA.guidValue == valueB.guidValue;
    }

    public bool Equals(GuidToInt32 other)
    {
        return this.guidValue == other.guidValue;
    }

    public override bool Equals(object obj)
    {
        if (obj is GuidToInt32 color)
        {
            return GuidToInt32.Equals(this, color);
        }

        return false;
    }

    public override int GetHashCode()
    {
        return this.guidValue.GetHashCode();
    }
}

这可以很容易地适应其他类型,如uintlongulongfloatdouble等。

我不会在一个全新的项目中使用它,但它非常适合可逆迁移过程。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接