Cách tạo hướng dẫn xác định


103

Trong ứng dụng của chúng tôi, chúng tôi đang tạo các tệp Xml với một thuộc tính có giá trị Guid. Giá trị này cần phải nhất quán giữa các lần nâng cấp tệp. Vì vậy, ngay cả khi mọi thứ khác trong tệp thay đổi, giá trị hướng dẫn cho thuộc tính vẫn được giữ nguyên.

Một giải pháp rõ ràng là tạo một từ điển tĩnh với tên tệp và Guids để sử dụng cho chúng. Sau đó, bất cứ khi nào chúng tôi tạo tệp, chúng tôi tra cứu từ điển cho tên tệp và sử dụng hướng dẫn tương ứng. Nhưng điều này không khả thi vì chúng tôi có thể mở rộng quy mô đến 100 tệp và không muốn duy trì danh sách lớn các tệp.

Vì vậy, một cách tiếp cận khác là làm cho Hướng dẫn giống nhau dựa trên đường dẫn của tệp. Vì đường dẫn tệp và cấu trúc thư mục ứng dụng của chúng tôi là duy nhất, nên Hướng dẫn phải là duy nhất cho đường dẫn đó. Vì vậy, mỗi lần chúng tôi chạy nâng cấp, tệp sẽ nhận được cùng một hướng dẫn dựa trên đường dẫn của nó. Tôi đã tìm thấy một cách tuyệt vời để tạo ra ' Guid xác định ' như vậy (Cảm ơn Elton Stoneman). Về cơ bản nó thực hiện điều này:

private Guid GetDeterministicGuid(string input) 

{ 

//use MD5 hash to get a 16-byte hash of the string: 

MD5CryptoServiceProvider provider = new MD5CryptoServiceProvider(); 

byte[] inputBytes = Encoding.Default.GetBytes(input); 

byte[] hashBytes = provider.ComputeHash(inputBytes); 

//generate a guid from the hash: 

Guid hashGuid = new Guid(hashBytes); 

return hashGuid; 

} 

Vì vậy, với một chuỗi, Hướng dẫn sẽ luôn giống nhau.

Có bất kỳ cách tiếp cận nào khác hoặc các cách được khuyến nghị để làm điều này không? Ưu nhược điểm của phương pháp đó là gì?

Câu trả lời:


151

Như đã đề cập bởi @bacar, RFC 4122 §4.3 xác định một cách để tạo một UUID dựa trên tên. Ưu điểm của việc làm này (thay vì chỉ sử dụng mã băm MD5) là những thứ này được đảm bảo không va chạm với các UUID không có tên dựa trên và có khả năng va chạm rất (rất nhỏ) với các UUID dựa trên tên khác.

Không có hỗ trợ gốc nào trong .NET Framework để tạo những thứ này, nhưng tôi đã đăng mã trên GitHub để triển khai thuật toán. Nó có thể được sử dụng như sau:

Guid guid = GuidUtility.Create(GuidUtility.UrlNamespace, filePath);

Để giảm nguy cơ va chạm với các GUID khác hơn nữa, bạn có thể tạo một GUID riêng để sử dụng làm ID không gian tên (thay vì sử dụng ID không gian tên URL được xác định trong RFC).


5
@Porges: RFC4122 không chính xác và có errata sửa mã C ( rfc-editor.org/errata_search.php?rfc=4122&eid=1352 ). Nếu việc triển khai này không hoàn toàn tuân thủ RFC4122 và errata của nó, vui lòng cung cấp thêm chi tiết; Tôi muốn làm cho nó theo tiêu chuẩn.
Bradley Grainger

1
@BradleyGrainger: Tôi không nhận thấy điều đó, cảm ơn / xin lỗi! Tôi nên luôn nhớ kiểm tra errata khi đọc RFC ... :)
porges

3
@Porges: Không có vấn đề gì. Nó làm bối rối tâm trí rằng họ không cập nhật RFC tại chỗ với các chỉnh sửa từ errata. Ngay cả một liên kết ở cuối tài liệu cũng sẽ hữu ích hơn rất nhiều so với việc người đọc phải nhớ để tìm kiếm errata (hy vọng trước khi viết một triển khai dựa trên RFC ...).
Bradley Grainger

1
@BradleyGrainger: nếu bạn sử dụng phiên bản HTML, nó có một liên kết đến errata từ tiêu đề, ví dụ: tools.ietf.org/html/rfc4122 . Tôi tự hỏi liệu có tiện ích mở rộng trình duyệt nào luôn chuyển hướng đến phiên bản HTML ...
porges

2
Bạn nên cân nhắc việc đóng góp phần này vào .NET .NET repo tại đây: github.com/dotnet/coreclr/tree/master/src/mscorlib/src/System
sapphiremirage

29

Điều này sẽ chuyển đổi bất kỳ chuỗi nào thành Hướng dẫn mà không cần phải nhập một tổ hợp bên ngoài.

public static Guid ToGuid(string src)
{
    byte[] stringbytes = Encoding.UTF8.GetBytes(src);
    byte[] hashedBytes = new System.Security.Cryptography
        .SHA1CryptoServiceProvider()
        .ComputeHash(stringbytes);
    Array.Resize(ref hashedBytes, 16);
    return new Guid(hashedBytes);
}

Có nhiều cách tốt hơn để tạo Hướng dẫn duy nhất nhưng đây là một cách để nâng cấp một cách nhất quán khóa dữ liệu chuỗi thành khóa dữ liệu Hướng dẫn.


Nhận thấy đoạn mã này hữu ích khi sử dụng số nhận dạng duy nhất trong cơ sở dữ liệu để phân phối liên kết.
Gleno

6
Cảnh báo! Mã này không tạo ra các Guids / UUID hợp lệ (như bacar cũng được đề cập bên dưới). Cả phiên bản và trường loại đều không được đặt chính xác.
MarkusSchaber

3
Sẽ không hiệu quả bằng việc sử dụng MD5CryptoServiceProvider thay vì SHA1, vì MD5 đã có độ dài 16 byte?
Brain2000

20

Như Rob đã đề cập, phương pháp của bạn không tạo UUID, nó tạo ra một băm trông giống như UUID.

Các RFC 4122 trên UUIDs đặc biệt cho phép xác định UUIDs (tên-based) - Phiên bản 3 và 5 sử dụng md5 và SHA1 (tương ứng). Hầu hết mọi người có lẽ đã quen thuộc với phiên bản 4, đó là ngẫu nhiên. Wikipedia cung cấp một cái nhìn tổng quan tốt về các phiên bản. (Lưu ý rằng việc sử dụng từ 'phiên bản' ở đây dường như để mô tả một 'loại' UUID - phiên bản 5 không giống với phiên bản 4).

Có vẻ như có một vài thư viện để tạo UUID phiên bản 3/5, bao gồm mô-đun uuid python , boost.uuid (C ++) và OSSP UUID . (Tôi chưa tìm kiếm bất kỳ .net nào)


1
Đây chính xác là những gì áp phích gốc là sau. UUID đã có một thuật toán để bạn bắt đầu với một chuỗi và chuyển đổi nó thành một GUID. UUID phiên bản 3 băm chuỗi với MD5, trong khi phiên bản 5 băm chuỗi với SHA1. Điểm quan trọng trong việc tạo một "hướng dẫn" là làm cho nó trở thành "duy nhất" so với các GUID khác. Các thuật toán định nghĩa hai bit mà phải thiết lập, cũng như một nibble được thiết lập để 3 hoặc 5, tùy thuộc nếu nó phiên bản 3 hoặc 5.
Ian Boyd

2
Về việc sử dụng từ "phiên bản", RFC 4122 §4.1.3 nêu rõ: "Phiên bản chính xác hơn là một loại phụ; một lần nữa, chúng tôi giữ lại thuật ngữ này để tương thích."
Bradley Grainger

11
Tôi đã đăng một số mã C # để tạo HƯỚNG DẪN V3 và v5 trên GitHub: github.com/LogosBible/Logos.Utility/blob/master/src/…
Bradley Grainger

@BradleyGrainger, Tôi nhận được Cảnh báo Bitwise-hoặc toán tử được sử dụng trên toán hạng mở rộng dấu hiệu; xem xét casting cho một loại unsigned nhỏ đầu tiên
Sebastian

1
Điều này đang trở nên lạc đề! Đề xuất chuyển các báo cáo lỗi lib riêng lẻ sang GitHub.
bacar

3

Bạn cần phân biệt giữa các phiên bản của lớp Guidvà các số nhận dạng là duy nhất trên toàn cầu. Một "hướng dẫn xác định" thực sự là một hàm băm (bằng chứng là bạn đã gọi đến provider.ComputeHash). Hàm băm có cơ hội va chạm cao hơn nhiều (hai chuỗi khác nhau xảy ra để tạo ra cùng một hàm băm) so với Guid được tạo thông qua Guid.NewGuid.

Vì vậy, vấn đề với cách tiếp cận của bạn là bạn sẽ phải đồng ý với khả năng hai đường dẫn khác nhau sẽ tạo ra cùng một GUID. Nếu bạn cần một số nhận dạng duy nhất cho bất kỳ chuỗi đường dẫn nhất định nào, thì điều dễ dàng nhất để làm là chỉ cần sử dụng chuỗi . Nếu bạn muốn chuỗi bị che khuất khỏi người dùng của mình, hãy mã hóa chuỗi đó - bạn có thể sử dụng ROT13 hoặc thứ gì đó mạnh mẽ hơn ...

Cố gắng khắc phục thứ gì đó không phải là GUID thuần túy vào loại dữ liệu GUID có thể dẫn đến các vấn đề bảo trì trong tương lai ...


2
Bạn khẳng định "Các mã băm có khả năng va chạm ... cao hơn nhiều so với Hướng dẫn được tạo qua Guid.NewGuid.". bạn có thể giải thích về điều đó không? Từ quan điểm toán học của Chế độ xem, số lượng bit mà người ta có thể đặt là như nhau và cả MD5 và SHA1 đều là hàm băm mật mã, được thiết kế đặc biệt để giảm xác suất xảy ra va chạm băm (vô tình và cố ý).
MarkusSchaber,

Tôi muốn nói sự khác biệt chính là bản đồ băm mật mã từ một không gian vô hạn này sang một không gian cố định khác bằng cách sử dụng một hàm. Hình ảnh một hàm băm ánh xạ các chuỗi có độ dài thay đổi thành 128 bit trong khi Guid tạo ra 128 bit giả ngẫu nhiên. Việc tạo giả ngẫu nhiên không dựa vào đầu vào ban đầu mà là bằng cách tạo ra đầu ra đồng nhất trong không gian đầu ra bằng cách sử dụng tính ngẫu nhiên được gieo từ phần cứng hoặc các phương tiện khác.
Thai Bui

2

MD5 yếu, tôi tin rằng bạn có thể làm điều tương tự với SHA-1 và đạt kết quả tốt hơn.

BTW, chỉ là ý kiến ​​cá nhân, mặc quần áo băm md5 lên làm GUID không làm cho nó trở thành một GUID tốt. GUID về bản chất của chúng là không xác định. điều này cảm thấy giống như một gian lận. Tại sao không chỉ gọi một cái thuổng là một cái thuổng và chỉ nói nó là một chuỗi được hiển thị hàm băm của đầu vào. bạn có thể làm điều đó bằng cách sử dụng dòng này, thay vì dòng hướng dẫn mới:

string stringHash = BitConverter.ToString(hashBytes)

Cảm ơn bạn đã nhập liệu, nhưng điều này vẫn mang lại cho tôi một chuỗi và tôi đang tìm một
HƯỚNG DẪN

Được rồi, hãy gọi hàm băm của bạn là "GUID", vấn đề đã được giải quyết. Hay vấn đề thực sự là bạn cần một Guidđối tượng?
user7116

tôi ước nó chỉ đơn giản như vậy .. :) nhưng có, tôi cần một đối tượng 'GUID'
Punit Vora

5
"Bản chất của chúng là không xác định" - điều này chỉ đúng với một số loại ('phiên bản') của GUID. Tuy nhiên, tôi đồng ý rằng "mặc quần áo băm md5 làm GUID không tạo nên một GUID tốt" vì các lý do khác như @Bradley Grainger và @Rob Fonseca-Ensor đã viết và câu trả lời của tôi cho câu hỏi này.
bacar
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.