2011-03-01 3 views
38

에릭 Lippert의에 의해 Guidelines and rules for GetHashCode에서 인용 :데이터베이스에 안전하게 저장할 수있는 문자열에 대해 .net (C#)으로 HashCode를 만드는 방법은 무엇입니까?

규칙 : GetHashCode의 소비자는 시간이 지남에 안정되고 의지 또는에서

것은 당신이 한 무리가있는 고객 오브젝트 가 있다고 가정 응용 프로그램 도메인 수 없습니다 이름, 주소 등과 같은 필드 두 개의 서로 다른 프로세스에서 동일한 데이터를 가진 두 개의 객체를 으로 만들면 은 동일한 해시 코드를 반환 할 필요가 없습니다. 화요일에 화요일에 이러한 객체를 만들 경우, 을 종료하고 수요일에 프로그램을 다시 실행하십시오. 해시 코드는 일 수 있습니다.

이것은 과거에 사람을 물었습니다. 는 System.String.GetHashCode에 대한 문서는 두 개의 동일한 문자열이 그들이 실제로 서로 다른 해시 코드 CLR은 서로 다른 버전의 및 을 가질 수 특별히 을 말한다. 데이터베이스에 문자열 해시를 저장하지 마십시오. 문자열 해시가 데이터베이스에 저장되지 않기 때문에 영원히 같을 것으로 기대하십시오.

그래서 데이터베이스에 저장할 수있는 문자열의 HashCode를 만드는 올바른 방법은 무엇입니까?

(I 소프트웨어에서이 버그 내가 작성한을 떠난 최초의 사람이 아니라고 말해주세요!)

+2

음, 필자는 GetHashCode에 의존하지 않는다. 나는 다른 사람들이 더 잘하지 않는다고 믿는다 ...-) –

+3

당신이 작성한 소프트웨어에서이 버그를 떠난 사람은 당신이 처음이 아니다. – Bobby

+2

Dbase 엔진은 이미 해싱 문자열에 매우 능숙합니다. 그냥 열의 색인을 만드십시오. –

답변

64

해시 할 속성이 무엇인가에 따라 달라집니다. 그래서 당신이 문서 그 해시 계산하는 방법이다, 즉 유효의로

public int HashString(string text) 
{ 
    // TODO: Determine nullity policy. 

    unchecked 
    { 
     int hash = 23; 
     foreach (char c in text) 
     { 
      hash = hash * 31 + c; 
     } 
     return hash; 
    } 
} 

예를 들어, 당신은 바로 이런 일을 작성할 수 있습니다. 암호로 보호되거나 결코 그런 것은 아니지만 문제없이 유지할 수 있습니다. 서양의 의미에서 절대적으로 동일한 두 문자열 (즉, 문화적 평등 등을 적용하지 않고 정확하게 문자별로)은이 코드와 동일한 해시를 생성합니다.

문제는 당신이 의지 할 때 올 문서화되지 않은 해싱에 - GetHashCode() 순종하지만 string.GetHashCode()을 같은 ... 버전으로 버전에서 동일하게 유지 보장 어떠한 방식으로 즉 뭔가.

이와 같이 자신 만의 해쉬를 작성하고 문서화하는 것은 "이 중요한 정보는 MD5 (또는 무엇이든)로 해쉬됩니다."라고 말하는 것과 같습니다. 잘 정의 된 해쉬 인 한, 괜찮습니다.

EDIT : SHA-1 또는 MD5와 같은 암호화 해시를 사용하는 것이 좋습니다.우리는 단지 안정성보다는 암호 보안에 대한 요구 사항이 있다는 것을 알기 전까지는 문자열을 바이트 배열로 변환하고이를 해시하는 방법에 대해서는 언급 할 필요가 없습니다. 물론 해시 이 보안 관련 작업에 사용되도록 의도 된 인 경우 업계 표준 해시는 정확히에 도달해야합니다. 그러나 그것은 그 질문에서 언급되지 않았다.

+3

23과'* 31'에 대한 마법이 있습니까? 오히려 다른 가치보다 더 많이 선택하는 이유는 무엇입니까? ... 다른 [문서화 된] 해싱 방법보다? 나는 ASCII 형식의 인쇄물보다 31 점이 불필요하게 의심 스러울지라도 31을 추측하고 있습니다. – ruffin

+10

@ruffin : Josh Bloch가 권장하는 값입니다. 31을 곱하는 것은 시프트와 뺄셈으로 수행 할 수 있기 때문에 효율적입니다. 이것에 대해 이야기하는 다른 여러 가지 질문이 있습니다. 그것은 솔직히 말해서 약간 어두운 예술입니다. –

+15

깔끔함! [Effective Java (2008), 48 페이지] (https://books.google.com/books?id=ka2VUBqHiWkC) : * 값 31이 홀수 소수이기 때문에 선택되었습니다. 그것이 짝수이고 곱셈이 오버플로되면 곱셈은 쉬프트와 동일하므로 정보가 손실됩니다. 프라임을 사용하는 이점은 덜 명확하지만 전통적입니다. 좋은 속성 31은 더 나은 성능을 위해 곱셈을 시프트와 뺄셈으로 대체 할 수 있다는 것입니다. 31 * i == (i << 5) - i'. 현대 VM은 이런 종류의 최적화를 자동으로 수행합니다. * 재미있는 독서처럼 보입니다. 다시 한 번 감사드립니다. – ruffin

1

대답은 그냥 자신의 해시 함수를 작성하는 것입니다. 댓글에있는 링크를 따라 게시 한 기사에 대한 소스를 찾을 수 있습니다. 또는 원래 암호화 (MD5, SHA1 등) 용으로 의도 된 내장 해시 함수를 사용할 수 있으며 모든 비트를 사용하지 않아도됩니다.

6

여기에 the current way .NET calculates it's string hash code for 64 bit systems의 재 구현이 있습니다. 이것은 실제 GetHashCode()과 같은 포인터를 사용하지 않으므로 약간 느려지지만 string의 내부 변경 사항에보다 탄력적으로 적용됩니다. Jon Skeet's version보다 균등하게 분산 된 해시 코드를 제공하므로 사전에서 조회 시간이 더 길어질 수 있습니다. .

public static class StringExtensionMethods 
{ 
    public static int GetStableHashCode(this string str) 
    { 
     unchecked 
     { 
      int hash1 = 5381; 
      int hash2 = hash1; 

      for(int i = 0; i < str.Length && str[i] != '\0'; i += 2) 
      { 
       hash1 = ((hash1 << 5) + hash1)^str[i]; 
       if (i == str.Length - 1 || str[i+1] == '\0') 
        break; 
       hash2 = ((hash2 << 5) + hash2)^str[i+1]; 
      } 

      return hash1 + (hash2*1566083941); 
     } 
    } 
} 
관련 문제