Eindeutige ID aus String erstellen

  • VB.NET

Es gibt 20 Antworten in diesem Thema. Der letzte Beitrag () ist von mrMo.

    Eindeutige ID aus String erstellen

    Hallo, ich habe jetzt schon eine Weile das Internet durchsucht, kam aber leider auf keine Lösung, die mir bei meiner Problemstellung weiterhilft. Vielleicht sehe ich aber auch nur den Wald vor lauter Bäumen nicht und stehe auf dem Schlauch :D

    Ich möchte einem vom Benutzer eingegebenen String in einem Dataset eine eindeutige, 8-stellige ID zuordnen, welche aus dem String berechnet wird. Das Ganze soll keine Verschlüsselung darstellen und muss nicht rückwärtskompatibel sein.
    Da es in der Theorie irgendwann recht viele Datensätze werden könnten, würde ich gerne mehr als nur Dezimalzahlen dafür nutzen. Gibt es eine Möglichkeit, recht simpel so eine ID mit A-Z und 0-9 zu erstellen, die eindeutig ist und das auch auf jedem System? Egal wer und wo etwas eingibt soll immer die gleiche ID erhalten.

    Meine erste Idee war, einen Hash zu nehmen, aber mit Bordmitteln geht leider kein CRC32, was all meine Probleme lösen würde :D MD5, SHA usw sind leider deutlich länger.
    Danach habe ich etwas mit Base64 herumprobiert, aber das enthält natürlich Sonderzeichen.
    Auch die GetHashCode Methode bringt mich nicht weiter, denn die scheint bei verschienenen System verschiedene Werte auszugeben.

    Wäre über jeden kleinen Denkanstoß dankbar :)
    Hallo @typx,

    das geht z.B. so:

    C#-Quellcode

    1. public static string GetIDforString(string str)
    2. {
    3. using (SHA1Managed sha1 = new SHA1Managed())
    4. {
    5. var hash = sha1.ComputeHash(Encoding.UTF8.GetBytes(str));
    6. byte[] guidBytes = SubArray(hash, 0, 16);
    7. Guid IDguid = new Guid(guidBytes);
    8. return IDguid.ToString();
    9. }
    10. }
    11. public static byte[] SubArray<byte>(byte[] data, int index, int length)
    12. {
    13. T[] result = new T[length];
    14. Array.Copy(data, index, result, 0, length);
    15. return result;
    16. }

    Dieser Beitrag wurde bereits 3 mal editiert, zuletzt von „ManuelSoftware“ ()

    Liegt das Problem vielleicht daran, dass es keine 8-Stellige ID ist?

    Spoiler anzeigen

    VB.NET-Quellcode

    1. Option Strict On
    2. Option Explicit On
    3. Imports System.Text
    4. Imports System.Security.Cryptography
    5. Public Module Module1
    6. Public Sub Main()
    7. Dim alphastring = GetAlphaString()
    8. Dim mystr As String = "Bla bla bla"
    9. Dim myid As String
    10. Dim mysalt() As Byte = ComputeHash(mystr)
    11. Using h As New HMACSHA1(mysalt)
    12. Dim b8 = h.ComputeHash(Encoding.UTF8.GetBytes(mystr)).Take(8)
    13. b8 = b8.Select(Function(x) CByte(CDbl((alphastring.Length - 1.0) / 256) * x)).ToArray
    14. myid = b8.Select(Function(x) alphastring(x)).ToArray
    15. End Using
    16. Console.WriteLine(myid)
    17. Console.ReadLine()
    18. End Sub
    19. Private Function ComputeHash(ByVal str As String) As Byte()
    20. Using sha1 As New SHA1Managed
    21. Return sha1.ComputeHash(Encoding.UTF8.GetBytes(str))
    22. End Using
    23. End Function
    24. Private Function GetAlphaString() As String
    25. Dim res As New StringBuilder(62)
    26. Dim z() As Byte = {48, 57, 65, 90, 97, 122}
    27. For i As Int32 = 0 To z.Length - 1 Step 2
    28. For j As Byte = z(i) To z(i + 1)
    29. res.Append(ChrW(j))
    30. Next
    31. Next
    32. If res.Length > 0 Then Return res.ToString
    33. Return String.Empty
    34. End Function
    35. End Module



    Hier noch die Ausführung für 100'000
    Spoiler anzeigen

    VB.NET-Quellcode

    1. Option Strict On
    2. Option Explicit On
    3. Imports System.Text
    4. Imports System.Collections.Generic
    5. Imports System.Security.Cryptography
    6. Public Module Module1
    7. Public Sub Main()
    8. Dim size As Int32 = 100000
    9. Dim sArr = GetStrings(size)
    10. Dim AllStringId = GetID(sArr)
    11. End Sub
    12. Private Function GetID(ByVal sArr() As String) As String()
    13. If sArr.Length > 0 Then
    14. Dim mysalt() As Byte
    15. Dim alphastring = GetAlphaString()
    16. Dim res As New List(Of String)
    17. For i As Int32 = 0 To sArr.Length - 1
    18. If Not String.IsNullOrEmpty(sArr(i)) Then
    19. mysalt = ComputeHash(sArr(i))
    20. Using h As New HMACSHA1(mysalt)
    21. Dim b8 = h.ComputeHash(Encoding.UTF8.GetBytes(sArr(i))).Take(8)
    22. b8 = b8.Select(Function(x) CByte(CDbl((alphastring.Length - 1.0) / 256) * x)).ToArray
    23. res.Add(b8.Select(Function(x) alphastring(x)).ToArray)
    24. End Using
    25. Else : res.Add(String.Empty)
    26. End If
    27. Next
    28. Return res.ToArray
    29. End If
    30. Return Nothing
    31. End Function
    32. Private Function GetStrings(ByVal size As Int32) As String()
    33. If size > 0 Then
    34. Dim res = New String(size - 1) {}
    35. For i As Int32 = 0 To size - 1
    36. res(i) = "MyStringForID_" & i.ToString
    37. Next
    38. Return res
    39. End If
    40. Return Nothing
    41. End Function
    42. Private Function ComputeHash(ByVal str As String) As Byte()
    43. Using sha1 As New SHA1Managed
    44. Return sha1.ComputeHash(Encoding.UTF8.GetBytes(str))
    45. End Using
    46. End Function
    47. Private Function GetAlphaString() As String
    48. Dim res As New StringBuilder(62)
    49. Dim z() As Byte = {48, 57, 65, 90, 97, 122}
    50. For i As Int32 = 0 To z.Length - 1 Step 2
    51. For j As Byte = z(i) To z(i + 1)
    52. res.Append(ChrW(j))
    53. Next
    54. Next
    55. If res.Length > 0 Then Return res.ToString
    56. Return String.Empty
    57. End Function
    58. End Module


    Freundliche Grüsse

    exc-jdbi

    Dieser Beitrag wurde bereits 1 mal editiert, zuletzt von „exc-jdbi“ ()

    Reicht dir nen Integer nicht? Der MaxValue liegt hier bei 2.147.483.647
    "Gib einem Mann einen Fisch und du ernährst ihn für einen Tag. Lehre einen Mann zu fischen und du ernährst ihn für sein Leben."

    Wie debugge ich richtig? => Debuggen, Fehler finden und beseitigen
    Wie man VisualStudio nutzt? => VisualStudio richtig nutzen
    Ja wäre theoretisch natürlich auch eine einfache Möglichkeit, aber es werden auch Ordner mit dem Namen der ID erstellt. Daher wollte ich es vermeiden, dass User einen falschen wählen weil sie sich "zu ähnlich" sehen. 6F67AC und E8BD10 sehen sich halt nicht so ähnlich wie 002345 und 002354.

    Aber mit den ersten 8 Stellen eines MD5 ist das ja machbar und ich hoffe mal, dass es damit keine Kollisionen gibt.

    typx schrieb:

    es werden auch Ordner mit dem Namen der ID erstellt
    Was für Ordner? Ordner haben doch normalerweise eher sprechende Namen als kryptische Nummern/Buchstaben Kombinationen ...

    Vielleich magst du ja Beschreiben was dein Ziel ist. Ich als User wollte nichts auswählen, was E8BD10 heißt ;)
    "Gib einem Mann einen Fisch und du ernährst ihn für einen Tag. Lehre einen Mann zu fischen und du ernährst ihn für sein Leben."

    Wie debugge ich richtig? => Debuggen, Fehler finden und beseitigen
    Wie man VisualStudio nutzt? => VisualStudio richtig nutzen
    Ich versuche es mal zu beschreiben :D

    Es wird ein Editor für ein Managerspiel das ein Freund von mir entwickelt. Die Idee der IDs beruht darauf, dass es sehr editierbar sein soll. Datenbank, Grafiken etc, alles soll der User bearbeiten können. Die IDs kommen dann zB bei Grafiken, Texten oder was auch immer zum tragen, da das Spiel automatisch Dateien mit der ID als Name sucht und einbindet. Auch die Datenbank soll im XML Format auf der Festplatte zugänglich sein, darauf beziehen sich dann die Ordner.

    Hoffe das erklärt es einigermaßen :)
    Ok, ich blicks nicht ganz was du vor hast. Das ganze hört sich für mich nach Datei gewurschtel Workaround an... Geht sicher einfacher und sauberer. Dazu müsste man das Datenmodell (wichtig) und die Funktionen des Programms kennen.

    Wozu muss der User in einer Datenbank / XML rumhantieren?
    "Gib einem Mann einen Fisch und du ernährst ihn für einen Tag. Lehre einen Mann zu fischen und du ernährst ihn für sein Leben."

    Wie debugge ich richtig? => Debuggen, Fehler finden und beseitigen
    Wie man VisualStudio nutzt? => VisualStudio richtig nutzen
    Damit jeder die Spielinhalte für sich modifizieren kann, wenn gewünscht. Das ist gewollt so, kein workaround.

    Die Zielsetzung ist eine möglichst offene Umgebung für alle möglichen Daten zu schaffen, damit eine Community möglichst viel editieren und modifizieren kann. Das ganze soll per mitgeliefertem Editor funktionieren, als auch per Direktzugriff über die XML files bzw die eindeutigen Dateinamen über die IDs, um die es hier geht.
    Eventuell reicht ja schon ein BKDR Hash. Die wäre auch noch sehr schnell berechnet, und ist auch systemunabhängig.
    Wenn man den in Hex umwandert mit string.Format("{0:x}" bekommt man auch immer eine 8-stellig ID.

    VB.NET-Quellcode

    1. Private Function BKDRHashHex(ByVal str As String) As String
    2. If String.IsNullOrEmpty(str) Then Return Nothing
    3. Return String.Format("{0:x}", BKDRHash(str)).ToUpper
    4. End Function
    5. Private Function BKDRHash(ByVal str As String) As UInt64
    6. If String.IsNullOrEmpty(str) Then Return Nothing
    7. Dim hash As UInt64 = 0UL, seed As UInt32 = 131UI
    8. For i As Int32 = 0 To str.Length - 1
    9. hash = ((hash * seed) + CUInt(AscW(str(i)))) And UInt32.MaxValue
    10. Next
    11. Return hash
    12. End Function


    Freundlliche Grüsse

    exc-jdbi

    Dieser Beitrag wurde bereits 1 mal editiert, zuletzt von „exc-jdbi“ ()

    typx schrieb:

    Auch die Datenbank soll im XML Format auf der Festplatte zugänglich sein

    Tja, dann ist es aber keine Datenbank sondern einfach eine XML.

    Also. Dein vorhaben schreit ja förmlich nach einer Datenbank.
    Die kann jeder (wenn du es zur verfügung stellst) über deine App bearbeiten. Was nicht in deiner App zu bearbeiten geht DARF auch nicht bearbeitet werden.

    NIE lässt man den User in Daten (egal ob XML, DB oder sonst was) eingreifen. Ein Tippfehler und die ganze Applikation läuft evtl nicht mehr korrekt.
    Werfe das konzept um und mach ein neues. Vieleicht mit Hilfe des Forums, aber mach bitte ein neues.

    Grüße
    Sascha
    If _work = worktype.hard Then Me.Drink(Coffee)
    Seht euch auch meine Tutorialreihe <WPF Lernen/> an oder abonniert meinen YouTube Kanal.

    ## Bitte markiere einen Thread als "Erledigt" wenn deine Frage beantwortet wurde. ##

    Es ist einfach eine dokumentorientierte Datenbank und da die Zielsetzung wie erwähnt ein offenes System der Datenspeicherung sein soll, ist das auch der richtige Ansatz.
    Natürlich können sich User damit was zerschießen, aber dann ist das halt so, dafür gibt es Backups.

    Das Ziel der einfachen Modifizierbarkeit sehen wir als wichtiger an, da das ein Teil des Konzepts ist.
    Bei allen Ansätzen, bei denen Du einen Hash des Strings verwendest, kannst Du nicht garantieren, dass es zu keinen Kollisionen kommt. Dazu kommt, dass die Strings unterschiedlicher Datensätze unterschiedlich sein müssen, da sonst der gleiche Hash herauskommen würde.

    Um aufeinanderfolgenden Datensätzen möglichst unterschiedlich aussehende Bezeichner zu geben, kannst Du folgendes verwenden:
    ericlippert.com/2013/11/14/a-p…-multiplicative-inverses/
    Du gibst also jedem Datensatz eine stinknormale ID, also eine aufsteigende Zahl. Diese schmeißt Du in diese Funktion rein und heraus kommt (wenn Du 100000000 für m verwendest, natürlich) eine 8-stellige Zahl. Diese ist garantiert eindeutig und man kann sehr einfach wieder auf die ursprüngliche Datensatz-ID zurückrechnen.
    "Luckily luh... luckily it wasn't poi-"
    -- Brady in Wonderland, 23. Februar 2015, 1:56
    Desktop Pinner | ApplicationSettings | OnUtils
    Eine aufsteigende Zahl bringt mir leider nichts, da so eben nicht jeder Datensatz, egal wo und von wem eingegeben, immer die selbe ID hat :)

    Wenn User A den Datensatz "Test" anlegt und die ID 123ABC bekommt, muss User B die selbe ID bekommen, wenn er bei sich "Test" eingibt.

    Werde die Tage mal ein paar Tests mit den gesammelten Vorschlägen aus dem Thread machen und schauen, ob irgendeine Methode "sicher genug" aussieht :D
    Hat "Test" in dem Beispiel dann irgend eine besondere Bedeutung? Warum ist es wichtig, dass die gleiche ID rauskommt, wenn zwei Menschen am anderen Ende der Welt auf separaten Computern Datensätze mit dem gleichen Namen eingeben?
    "Luckily luh... luckily it wasn't poi-"
    -- Brady in Wonderland, 23. Februar 2015, 1:56
    Desktop Pinner | ApplicationSettings | OnUtils
    Wie oben bereits geschrieben handelt es sich um ein Managerspiel, dass durch eine Community editier- und modifizierbar sein soll.

    User A erstellt einen Datensatz, dessen ID sich aus "Test" berechnet, zB 123ABC und speichert es über mein Programm in ein xml File. User B erstellt eine Grafik, die sich darauf beziehen soll und benennt sie passend zur ID von "Test" 123ABC.png, ohne User A nach der ID fragen zu müssen. Nun stellen beide ihre Ergebnisse der Community zur Verfügung und es soll bei jedem User, der sich die Dateien runterläd, ohne Aufwand in die bereits vorhandenen Inhalte einfügen lassen.

    Deshalb die kollisionsfreie, eindeutige ID, die sich aus einmaligen Strings berechnet.

    Ich hoffe, das ist jetzt einigermaßen verständlich beschrieben :)