2016-02-18 13 views
6

C'è un modo per ottenere una sottostringa con corrispondenza da una stringa utilizzando un confronto di parità sensibile alla cultura? Ad esempio, sotto la cultura en-US, æ e ae sono considerati uguali. "Encyclopædia".IndexOf("aed") valuta 8, indicando una corrispondenza; tuttavia, esiste un modo per estrarre la sottostringa corrispondente, æd, che non coinvolga l'iterazione sulla stringa di origine? Si noti che le lunghezze delle sottostringhe ricercate e corrispondenti possono differire di diversi caratteri.Ottieni sottostringa da stringa utilizzando il confronto sensibile alla cultura

+0

Cosa succede ad usare un'espressione regolare come '(ae | æ) D'? – juharr

+0

@juharr: Regex sarebbe eccessivo (e introdurrà la propria serie di sfumature). Ho bisogno di questo per implementare funzionalità molto generali, come un metodo di estensione 'String.Replace' sensibile alla cultura. – Douglas

+0

Domande correlate: [Come posso eseguire un'operazione "start-in" sensibile alla cultura dal centro di una stringa?] (Http://stackoverflow.com/q/15980310/1149773) (di Jon Skeet), [Lunghezza della sottostringa corrispondente al metodo 'String.IndexOf' sensibile alla cultura] (http://stackoverflow.com/q/20480016/1149773). – Douglas

risposta

2

Ho risolto questo problema chiamando per la prima volta IndexOf per ottenere la posizione iniziale della partita, quindi cercando iterativamente di identificarne la lunghezza. Ho ottimizzato il percorso a caldo della partita avendo la stessa lunghezza della sottostringa specificata; in tal caso, viene eseguito solo un confronto singolo.

public static class StringExtensions 
{ 
    public static void Find(this string source, string substring, StringComparison comparisonType, out int matchIndex, out int matchLength) 
    { 
     Find(source, substring, 0, source.Length, comparisonType, out matchIndex, out matchLength); 
    } 

    public static void Find(this string source, string substring, int searchIndex, StringComparison comparisonType, out int matchIndex, out int matchLength) 
    { 
     Find(source, substring, searchIndex, source.Length - searchIndex, comparisonType, out matchIndex, out matchLength); 
    } 

    public static void Find(this string source, string substring, int searchIndex, int searchLength, StringComparison comparisonType, out int matchIndex, out int matchLength) 
    { 
     matchIndex = source.IndexOf(substring, searchIndex, searchLength, comparisonType); 
     if (matchIndex == -1) 
     { 
      matchLength = -1; 
      return; 
     } 

     matchLength = FindMatchLength(source, substring, searchIndex, searchLength, comparisonType, matchIndex); 

     // Defensive programming, but should never happen 
     if (matchLength == -1) 
      matchIndex = -1; 
    } 

    private static int FindMatchLength(string source, string substring, int searchIndex, int searchLength, StringComparison comparisonType, int matchIndex) 
    { 
     int matchLengthMaximum = searchLength - (matchIndex - searchIndex); 
     int matchLengthInitial = Math.Min(substring.Length, matchLengthMaximum); 

     // Hot path: match length is same as substring length. 
     if (Compare(source, matchIndex, matchLengthInitial, substring, 0, substring.Length, comparisonType) == 0) 
      return matchLengthInitial; 

     int matchLengthDecrementing = matchLengthInitial - 1; 
     int matchLengthIncrementing = matchLengthInitial + 1; 

     while (matchLengthDecrementing >= 0 || matchLengthIncrementing <= matchLengthMaximum) 
     { 
      if (matchLengthDecrementing >= 0) 
      { 
       if (Compare(source, matchIndex, matchLengthDecrementing, substring, 0, substring.Length, comparisonType) == 0) 
        return matchLengthDecrementing; 

       matchLengthDecrementing--; 
      } 

      if (matchLengthIncrementing <= matchLengthMaximum) 
      { 
       if (Compare(source, matchIndex, matchLengthIncrementing, substring, 0, substring.Length, comparisonType) == 0) 
        return matchLengthIncrementing; 

       matchLengthIncrementing++; 
      } 
     } 

     // Should never happen 
     return -1; 
    } 

    private static int Compare(string strA, int indexA, int lengthA, string strB, int indexB, int lengthB, StringComparison comparisonType) 
    { 
     switch (comparisonType) 
     { 
      case StringComparison.CurrentCulture: 
       return CultureInfo.CurrentCulture.CompareInfo.Compare(strA, indexA, lengthA, strB, indexB, lengthB, CompareOptions.None); 

      case StringComparison.CurrentCultureIgnoreCase: 
       return CultureInfo.CurrentCulture.CompareInfo.Compare(strA, indexA, lengthA, strB, indexB, lengthB, CompareOptions.IgnoreCase); 

      case StringComparison.InvariantCulture: 
       return CultureInfo.InvariantCulture.CompareInfo.Compare(strA, indexA, lengthA, strB, indexB, lengthB, CompareOptions.None); 

      case StringComparison.InvariantCultureIgnoreCase: 
       return CultureInfo.InvariantCulture.CompareInfo.Compare(strA, indexA, lengthA, strB, indexB, lengthB, CompareOptions.IgnoreCase); 

      case StringComparison.Ordinal: 
       return CultureInfo.InvariantCulture.CompareInfo.Compare(strA, indexA, lengthA, strB, indexB, lengthB, CompareOptions.Ordinal); 

      case StringComparison.OrdinalIgnoreCase: 
       return CultureInfo.InvariantCulture.CompareInfo.Compare(strA, indexA, lengthA, strB, indexB, lengthB, CompareOptions.OrdinalIgnoreCase); 

      default: 
       throw new ArgumentException("The string comparison type passed in is currently not supported.", nameof(comparisonType)); 
     } 
    } 
} 

uso Esempio:

int index, length; 
source.Find(remove, StringComparison.CurrentCulture, out index, out length); 
string clean = index < 0 ? source : source.Remove(index, length); 
Problemi correlati