2009-12-07 10 views
9

मुझे उच्च-प्रदर्शन केस-असंवेदनशील स्ट्रिंग तुलनाओं की बहुत कुछ करने की आवश्यकता है और मुझे एहसास हुआ कि यह करने का मेरा तरीका। छोटा()। ट्रिम() वास्तव में बेवकूफ था क्योंकि सभी नए तारString.comparison प्रदर्शन (ट्रिम के साथ)

तो मैं एक छोटे से चारों ओर खोदा आवंटित किया जा रहा और इस तरह से बेहतर लगता है:

String.Compare(txt1,txt2, StringComparison.OrdinalIgnoreCase) 

समस्या सिर्फ यहाँ है कि मैं आगे या रिक्त स्थान, यानी ट्रिम() अनुगामी अनदेखी करने के लिए अगर मैं ट्रिम मैं उपयोग करना चाहते हैं, लेकिन स्ट्रिंग आवंटन के साथ एक ही समस्या है। मुझे लगता है कि मैं प्रत्येक स्ट्रिंग की जांच कर सकता हूं और देख सकता हूं कि यह स्टार्ट्सविथ ("") या एंड्स विथ ("") और केवल तब ट्रिम करें। या तो उस या सूचकांक, प्रत्येक स्ट्रिंग के लिए लंबाई यह पता लगाने और ओवरराइड

public static int Compare 
(
    string strA, 
    int indexA, 
    string strB, 
    int indexB, 
    int length, 
    StringComparison comparisonType 
) 

string.Compare को पारित लेकिन वह नहीं बल्कि गन्दा लगता है और मैं शायद कुछ पूर्णांकों का उपयोग करने के लिए अगर मैं कर न एक वास्तव में बड़ी है, तो-और कुछ दोनों तारों पर पीछे और अग्रणी रिक्त स्थान के हर संयोजन के लिए बयान ... तो एक सुरुचिपूर्ण समाधान के किसी भी विचार?

public bool IsEqual(string a, string b) 
    { 
     return (string.Compare(a, b, StringComparison.OrdinalIgnoreCase) == 0); 
    } 

    public bool IsTrimEqual(string a, string b) 
    { 
     if (Math.Abs(a.Length- b.Length) > 2) // if length differs by more than 2, cant be equal 
     { 
      return false; 
     } 
     else if (IsEqual(a,b)) 
     { 
      return true; 
     } 
     else 
     { 
      return (string.Compare(a.Trim(), b.Trim(), StringComparison.OrdinalIgnoreCase) == 0); 
     } 
    } 
+5

आपको क्या लगता है कोई समस्या है बनाता है? समयपूर्व अनुकूलन एक बुरा विचार है - जब तक आपका एप्लिकेशन "बहुत धीमा" न हो जाए तब तक अनुकूलित करने की कोई आवश्यकता नहीं है। इस बीच, फास्ट कोड पर स्पष्ट कोड पर ध्यान केंद्रित करें। –

+0

क्या आप सुनिश्चित कर सकते हैं कि संकलक आपके लिए ऐसे मामले को अनुकूलित नहीं कर रहा है? –

+0

मैं यह भी पूछूंगा कि क्या इसे वास्तव में माइक्रो-अनुकूलन की आवश्यकता है? क्या आपके पास वास्तव में इस क्षेत्र में एक प्रदर्शन मुद्दा है? मैं वहाँ अन्य क्षेत्रों जहां प्रदर्शन में एक बड़ा सौदा अधिक सुधार हो सकता है – AdaTheDev

उत्तर

5

कुछ इस तरह यह करना चाहिए:

public static int TrimCompareIgnoreCase(string a, string b) { 
    int indexA = 0; 
    int indexB = 0; 
    while (indexA < a.Length && Char.IsWhiteSpace(a[indexA])) indexA++; 
    while (indexB < b.Length && Char.IsWhiteSpace(b[indexB])) indexB++; 
    int lenA = a.Length - indexA; 
    int lenB = b.Length - indexB; 
    while (lenA > 0 && Char.IsWhiteSpace(a[indexA + lenA - 1])) lenA--; 
    while (lenB > 0 && Char.IsWhiteSpace(b[indexB + lenB - 1])) lenB--; 
    if (lenA == 0 && lenB == 0) return 0; 
    if (lenA == 0) return 1; 
    if (lenB == 0) return -1; 
    int result = String.Compare(a, indexA, b, indexB, Math.Min(lenA, lenB), true); 
    if (result == 0) { 
     if (lenA < lenB) result--; 
     if (lenA > lenB) result++; 
    } 
    return result; 
} 

उदाहरण:

string a = " asdf "; 
string b = " ASDF \t "; 

Console.WriteLine(TrimCompareIgnoreCase(a, b)); 

आउटपुट:

0 

आप इसे एक सरल ट्रिम के खिलाफ प्रोफ़ाइल और कुछ वास्तविक डेटा के साथ तुलना करें, अगर वहाँ वास्तव में क्या आप के लिए इसका इस्तेमाल करने के लिए जा रहे हैं के लिए किसी भी अंतर नहीं है देखने के लिए करना चाहिए।

+0

दिलचस्प, धन्यवाद! मैं विभिन्न तरीकों से कुछ तुलना करूंगा और देख सकता हूं कि कौन सा शीर्ष पर आता है – Homde

+3

@ कोनराड इस समाधान की तुलना ट्रिम के साथ करने के आपके परिणाम क्या थे? –

2

सबसे पहले यह सुनिश्चित करें कि आप वास्तव में इस कोड का अनुकूलन करने की जरूरत है:

यहाँ मेरी वर्तमान प्रस्ताव है। शायद तारों की प्रतियां बनाना आपके कार्यक्रम को ध्यान से प्रभावित नहीं करेगा।

यदि आपको वास्तव में अनुकूलित करने की आवश्यकता है, तो आप स्ट्रिंग को संसाधित करने का प्रयास कर सकते हैं जब आप उन्हें पहली बार स्टोर करते हैं, जब आप उन्हें तुलना करते हैं (यह मानते हुए कि यह प्रोग्राम के विभिन्न चरणों में होता है)। उदाहरण के लिए, स्ट्रिंग के ट्रिम किए गए और लोअरकेस संस्करणों को स्टोर करें, ताकि जब आप उनकी तुलना करें तो आप आसानी से समकक्षता की जांच कर सकते हैं।

+0

ठीक है, इस मामले में एक और अधिक कुशल विधि का उपयोग करने में कुछ भी गलत नहीं है। String.Compare का उपयोग कुछ "चालाक" हैक नहीं है, यह स्ट्रिंग्स की तुलना करने के लिए एक तरह से बनाया गया है जो ToUpper() को कॉल करने से भी अधिक कुशल है। यह इरादे में और भी स्पष्ट है, इसलिए मुझे नहीं लगता कि आप इस उदाहरण/ –

+0

में एक वैध 'समय-अनुकूलन' केस बना सकते हैं, मैं इसे लेता हूं जिसका मतलब है कि आप ट्रिम करें()। ToLower() –

+0

त्रुटि, हाँ :-) [15chars] –

2

क्या आप बस एक बार स्ट्रिंग (और संभवतः इसे लोअरकेस बना सकते हैं) प्रत्येक स्ट्रिंग को एक बार (इसे प्राप्त करते समय) नहीं कर सकते? समय से पहले अनुकूलन की तरह अधिक लगता है कर रहा ....

+0

कुछ मामलों में मैं ऐसा कर सकता हूं, यह देखने में दिलचस्पी है कि कोई यह – Homde

3

मैं कोड का प्रयोग करेंगे आप

String.Compare(txt1,txt2, StringComparison.OrdinalIgnoreCase) 

है और के रूप में आप उन्हें जरूरत के लिए किसी भी .Trim() कॉल जोड़ें। यह अपने प्रारंभिक विकल्प बचा लेगा 4 तार समय के सबसे अधिक (.ToLower().Trim(), और दो तार समय (.ToLower())।

आप इस के बाद प्रदर्शन की समस्याओं पीड़ित हैं, तो अपने "गंदा" विकल्प की संभावना सबसे अधिक उपयुक्त रहेगी के सभी ।

+0

करने के लिए अनुकूलित सामान्य उद्देश्य विधि के साथ आ सकता है। यह दिलचस्प है। Mattias: यदि आपके अधिकांश तारों की आवश्यकता नहीं है ट्रिम करें() कॉल करें, तो आप आम तौर पर इसे इस तरह से कर सकते हैं, और यदि तार मेल नहीं खाते हैं, तो फॉल-बैक करें और अगले ट्रिम() कॉल के साथ प्रयास करें, फिर "वास्तव में" वापस आएं कि वे मेल नहीं खाते हैं। –

+0

हम्म , उस मामले में मुझे लगता है कि मुझे यह देखने के लिए कुछ परीक्षण चलाना चाहिए कि क्या आवश्यक IsPrefix()/IsSuffix() (उनमें से चार) ट्रिम – Homde

+0

आह करने की तुलना में कम या कम प्रदर्शन लेता है! पहले तुलना करें, फिर तुलना करें ट्रिम तुलना करें (या गन्दा metehod) अगर 0 नहीं, अच्छा – Homde

0
  1. बात है, अगर यह किया जाना चाहिए यह किया जाना चाहिए है। मुझे नहीं लगता कि अपने विभिन्न समाधान के किसी भी सकारात्मक प्रभाव पड़ेगा है। प्रत्येक मामले में वहाँ एक संख्या होना चाहिए व्हाइटसाइट खोजने या इसे हटाने के लिए तुलना की तुलना।

    जाहिर है, व्हाइटस्पेस को हटाने का हिस्सा है समस्या है, तो आपको इसके बारे में चिंता नहीं करनी चाहिए।

  2. और तुलना करने से पहले एक स्ट्रिंग को कम करने, एक बग है यदि आप यूनिकोड वर्णों के साथ काम करते हैं और संभवतः स्ट्रिंग की प्रतिलिपि बनाने से धीमे होते हैं।

0

चेतावनी समय से पहले अनुकूलन के बारे में सही कर रहे हैं कर रहे हैं, लेकिन मैं मान लेंगे तो आप इस परीक्षण किया और पाया कि बहुत समय को कॉपी तार बर्बाद किया जा रहा है है।स्ट्रिंग (इस अपरीक्षित है, लेकिन सामान्य विचार देना चाहिए)

int startIndex1, length1, startIndex2, length2; 
FindStartAndLength(txt1, out startIndex1, out length1); 
FindStartAndLength(txt2, out startIndex2, out length2); 

int compareLength = Math.Max(length1, length2); 
int result = string.Compare(txt1, startIndex1, txt2, startIndex2, compareLength); 

FindStartAndLength एक समारोह है कि प्रारंभिक सूचकांक और "छंटनी" लंबाई पाता है: उस मामले में मैं निम्नलिखित की कोशिश करेगा

static void FindStartAndLength(string text, out int startIndex, out int length) 
{ 
    startIndex = 0; 
    while(char.IsWhiteSpace(text[startIndex]) && startIndex < text.Length) 
     startIndex++; 

    length = text.Length - startIndex; 
    while(char.IsWhiteSpace(text[startIndex + length - 1]) && length > 0) 
     length--; 
} 
0

आप अपनी खुद की StringComparer को लागू कर सकते हैं।

public class TrimmingStringComparer : StringComparer 
{ 
    private StringComparison _comparisonType; 

    public TrimmingStringComparer() 
     : this(StringComparison.CurrentCulture) 
    { 
    } 

    public TrimmingStringComparer(StringComparison comparisonType) 
    { 
     _comparisonType = comparisonType; 
    } 

    public override int Compare(string x, string y) 
    { 
     int indexX; 
     int indexY; 
     int lengthX = TrimString(x, out indexX); 
     int lengthY = TrimString(y, out indexY); 

     if (lengthX <= 0 && lengthY <= 0) 
      return 0; // both strings contain only white space 

     if (lengthX <= 0) 
      return -1; // x contains only white space, y doesn't 

     if (lengthY <= 0) 
      return 1; // y contains only white space, x doesn't 

     if (lengthX < lengthY) 
      return -1; // x is shorter than y 

     if (lengthY < lengthX) 
      return 1; // y is shorter than x 

     return String.Compare(x, indexX, y, indexY, lengthX, _comparisonType); 
    } 

    public override bool Equals(string x, string y) 
    { 
     return Compare(x, y) == 0; 
    } 

    public override int GetHashCode(string obj) 
    { 
     throw new NotImplementedException(); 
    } 

    private int TrimString(string s, out int index) 
    { 
     index = 0; 
     while (index < s.Length && Char.IsWhiteSpace(s, index)) index++; 
     int last = s.Length - 1; 
     while (last >= 0 && Char.IsWhiteSpace(s, last)) last--; 
     return last - index + 1; 
    } 
} 

टिप्पणी::

  • इसे बड़े पैमाने पर परीक्षण नहीं किया जाता है और शामिल हो सकता है कीड़े
  • प्रदर्शन अभी तक मूल्यांकन किया जाना है (वैसे भी लेकिन यह शायद Trim और ToLower बुला की तुलना में बेहतर है) यहाँ एक बुनियादी कार्यान्वयन है
  • GetHashCode विधि लागू नहीं की गई है, इसलिए इसे
012 में एक कुंजी के रूप में उपयोग न करें
0

मुझे लगता है कि आपके पहला सुझाव केवल बल्कि छँटाई से समानता के लिए तुलना करता है, कि कुछ और दक्षता बचत की अनुमति देता है पर ध्यान दें।

public static bool TrimmedOrdinalIgnoreCaseEquals(string x, string y) 
{ 
    //Always check for identity (same reference) first for 
    //any comparison (equality or otherwise) that could take some time. 
    //Identity always entails equality, and equality always entails 
    //equivalence. 
    if(ReferenceEquals(x, y)) 
     return true; 
    //We already know they aren't both null as ReferenceEquals(null, null) 
    //returns true. 
    if(x == null || y == null) 
     return false; 
    int startX = 0; 
    //note we keep this one further than the last char we care about. 
    int endX = x.Length; 
    int startY = 0; 
    //likewise, one further than we care about. 
    int endY = y.Length; 
    while(startX != endX && char.IsWhiteSpace(x[startX])) 
     ++startX; 
    while(startY != endY && char.IsWhiteSpace(y[startY])) 
     ++startY; 
    if(startX == endX)  //Empty when trimmed. 
     return startY == endY; 
    if(startY == endY) 
     return false; 
    //lack of bounds checking is safe as we would have returned 
    //already in cases where endX and endY can fall below zero. 
    while(char.IsWhiteSpace(x[endX - 1])) 
     --endX; 
    while(char.IsWhiteSpace(y[endY - 1])) 
     --endY; 
    //From this point on I am assuming you do not care about 
    //the complications of case-folding, based on your example 
    //referencing the ordinal version of string comparison 
    if(endX - startX != endY - startY) 
     return false; 
    while(startX != endX) 
    { 
     //trade-off: with some data a case-sensitive 
     //comparison first 
     //could be more efficient. 
     if(
      char.ToLowerInvariant(x[startX++]) 
      != char.ToLowerInvariant(y[startY++]) 
     ) 
      return false; 
    } 
    return true; 
} 
बेशक

, उससे मिलते-जुलते hashCode निर्माता के बिना एक समानता चेकर क्या है:

public static int TrimmedOrdinalIgnoreCaseHashCode(string str) 
{ 
    //Higher CMP_NUM (or get rid of it altogether) gives 
    //better hash, at cost of taking longer to compute. 
    const int CMP_NUM = 12; 
    if(str == null) 
     return 0; 
    int start = 0; 
    int end = str.Length; 
    while(start != end && char.IsWhiteSpace(str[start])) 
     ++start; 
    if(start != end) 
     while(char.IsWhiteSpace(str[end - 1])) 
      --end; 

    int skipOn = (end - start)/CMP_NUM + 1; 
    int ret = 757602046; // no harm matching native .NET with empty string. 
    while(start < end) 
    { 
      //prime numbers are our friends. 
     ret = unchecked(ret * 251 + (int)(char.ToLowerInvariant(str[start]))); 
     start += skipOn; 
    } 
    return ret; 
} 
संबंधित मुद्दे