Как сгенерировать уникальный идентификатор для структуры адреса?

У меня есть структура, которая описывает адрес, он выглядит так:

class Address
{
    public string AddressLine1 { get; set; }
    public string AddressLine2 { get; set; }
    public string City { get; set; }
    public string Zip { get; set; }
    public string Country { get; set; }
} 

Я ищу способ создания уникального идентификатора для этой структуры (я предполагаю, что он также должен быть типа string), который зависит от всех свойств структуры (например, изменение AddressLine1 также приведет к изменению идентификатора структуры).

Я знаю, я мог бы просто объединить все свойства вместе, но это дает слишком длинный идентификатор. Я ищу что-то значительно короче этого.

Я также предполагаю, что количество разных адресов не должно превышать 100М.

Любые идеи о том, как этот идентификатор может быть сгенерирован?

Заранее спасибо.

Предыстория этого:

В базе данных есть несколько разных таблиц, которые содержат некоторую информацию + адресные данные. Данные хранятся в формате, аналогичном описанному выше.

К сожалению, перемещение данных адреса в отдельную таблицу сейчас очень дорого, но я надеюсь, что это будет сделано в будущем.

Мне нужно связать некоторые дополнительные свойства с адресными данными, и я собираюсь создать для этого отдельную таблицу. Вот почему мне нужно однозначно идентифицировать адрес данных.

3 ответа

Решение

Сериализуйте все поля в большое двоичное значение. Например, используя конкатенацию с правильным разделением домена.

Затем хэшируйте это значение с помощью криптографического хэша достаточной длины. Я предпочитаю 256 бит, но 128, вероятно, в порядке. Столкновения крайне редки с хорошими хешами, с 256-битным хешем, как SHA-256, они практически невозможны.

Вот возможный способ, о котором думает большинство людей:

  1. Нормализовать адрес
  2. Создать хэш из нормализованного адреса
  3. Сделанный…

Но настоящая проблема возникает, когда вам нужно нормализовать адрес. Например, эти улицы одинаковы:

  • "Площадь Сен-Франсуа 14"
  • "Площадь Сен-Франсуа 14"
  • "Площадь Сен-Франсуа 14"
  • "Площадь Сен-Франсуа 14"
  • "14 Place SaintFrançois"

Вы можете попытаться нормализовать адрес в нижнем регистре текста, удалив акценты/седильи/тире и с ближайшим символом ASCII и проанализировав число, чтобы оставить его в стороне, но все равно будут непредвиденные исключения. И один другой символ создаст совершенно другой хэш.

Если все ваши адреса полностью не нормализованы, я бы посоветовал полагаться на внешний сервис, такой как here.com.

Есть 3 способа использования сервиса

  1. Либо используйте сервис, чтобы найти координаты вашего адреса (долгота, широта, высота), затем используйте их в качестве своего идентификатора (или 3 идентификатора)
  2. Или вы можете использовать сервис, чтобы найти адрес и сохранить свой идентификатор в вашей БД. Недостаток в том, что они не гарантируют, что их ID не изменится.
  3. Последний — использовать их сервис для поиска вашего адреса в их реестре, а затем использовать их запись (которая будет нормализована в соответствии с их стандартом) для создания хэша.

Мой фаворит - 1. так как мы все еще можем найти адрес обратно по координатам (в то время как это невозможно с хешем), более того, адрес может измениться (например, новое название улицы), а координаты - нет. И последнее, но не менее важное: у вас может быть 2 совершенно разных адреса для одного и того же местоположения, их легче согласовать с помощью координат.

Вот полный пример использования сериализации, хеширования sha256 и кодировки base64 (на основе ответа CodesInChaos):

using System;
using System.IO;
using System.Security.Cryptography;
using System.Runtime.Serialization.Formatters.Binary;

namespace Uniq
{
    [Serializable]
    class Address
    {
        public string AddressLine1 { get; set; }
        public string AddressLine2 { get; set; }
        public string City { get; set; }
        public string Zip { get; set; }
        public string Country { get; set; }
    } 
    class MainClass
    {
        public static void Main (string[] args)
        {
            Address address1 = new Address(){AddressLine1 = "a1"};
            Address address2 = new Address(){AddressLine1 = "a1"};
            Address address3 = new Address(){AddressLine1 = "a2"};
            string unique1 = GetUniqueIdentifier(address1);
            string unique2 = GetUniqueIdentifier(address2);
            string unique3 = GetUniqueIdentifier(address3);
            Console.WriteLine(unique1);
            Console.WriteLine(unique2);
            Console.WriteLine(unique3);
        }
        public static string GetUniqueIdentifier(object obj){
            if (obj == null) return "0";
            SHA256 mySHA256 = SHA256Managed.Create ();
            BinaryFormatter formatter = new BinaryFormatter ();
            MemoryStream stream = new MemoryStream();
            formatter.Serialize(stream, obj);
            byte[] hash = mySHA256.ComputeHash(stream.GetArray());
            string uniqId = Convert.ToBase64String(hash);
            return uniqId;
        }
    }
}

Изменить: это версия без использования BinaryFormatter, Вы можете заменить нулевое представление и разделитель полей на все, что вам подходит.

public static string GetUniqueIdentifier(object obj){
    if (obj == null) return "0";
    SHA256 mySHA256 = SHA256Managed.Create ();
    StringBuilder stringRep = new StringBuilder();
    obj.GetType().GetProperties()
                .ToList().ForEach(p=>stringRep.Append(
            p.GetValue(obj, null) ?? '¨'
            ).Append('^'));
    Console.WriteLine(stringRep);
    Console.WriteLine(stringRep.Length);
    byte[] hash = mySHA256.ComputeHash(Encoding.Unicode.GetBytes(stringRep.ToString()));
    string uniqId = Convert.ToBase64String(hash);
    return uniqId;
}
Другие вопросы по тегам