Производительность String.comparison (с обрезкой)

Никто еще не упомянул его, таким образом позвольте мне просто упомянуть DejaVu Sans Mono , который является ветвлением Vera Sans Mono и включен в большую часть Linux distribs. Это поддерживает большую часть Unicode.

10
задан Homde 7 December 2009 в 20:15
поделиться

8 ответов

Что-то вроде этого должно сработать:

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

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

5
ответ дан 4 December 2019 в 00:25
поделиться

Вы можете реализовать свой собственный 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 не реализован, поэтому не используйте его в качестве ключа в словаре
0
ответ дан 4 December 2019 в 00:25
поделиться

Я бы использовал имеющийся у вас код

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

и добавлял любые вызовы .Trim () по мере необходимости. В большинстве случаев это позволит сохранить исходные 4 строки параметра ( .ToLower (). Trim () и две строки все время ( .ToLower () ).

Если после этого у вас возникнут проблемы с производительностью, то, вероятно, лучше всего подойдет вариант «беспорядок».

3
ответ дан 4 December 2019 в 00:25
поделиться

Сначала make уверен, что вам действительно нужно оптимизировать этот код. Возможно, создание копий строк не окажет заметного влияния на вашу программу.

Если вам действительно нужно оптимизировать, вы можете попробовать обработать строки при их первом сохранении, а не при их сравнении (при условии, что это происходит на разных этапах программа). Например, сохраняйте обрезанные и строчные версии строк, чтобы при их сравнении можно было просто проверить эквивалентность.

Если вам действительно нужна оптимизация, вы можете попытаться обработать строки при их первом сохранении, а не при их сравнении (при условии, что это происходит на разных этапах программы). Например, храните обрезанные и строчные версии строк, чтобы при их сравнении можно было просто проверить эквивалентность.

Если вам действительно нужна оптимизация, вы можете попробовать обработать строки при их первом сохранении, а не при их сравнении (при условии, что это происходит на разных этапах программы). Например, храните обрезанные и строчные версии строк, чтобы при их сравнении можно было просто проверить эквивалентность.

2
ответ дан 4 December 2019 в 00:25
поделиться

Не можете ли вы просто обрезать (и, возможно, сделать ее строчной) каждую строку ровно один раз (при ее получении)? Больше похоже на преждевременную оптимизацию ....

2
ответ дан 4 December 2019 в 00:25
поделиться
  1. Дело в том, что если это нужно сделать, то это нужно сделать. Я не думаю, что какое-либо из ваших решений будет иметь значение. В каждом случае необходимо несколько сравнений, чтобы найти пробелы или удалить их.

    Очевидно, удаление пробелов является частью проблемы, поэтому вам не стоит об этом беспокоиться.

  2. И уменьшение строки перед сравнением , является ошибкой, если вы работаете с символами Юникода и, возможно, медленнее, чем копирование строки.

0
ответ дан 4 December 2019 в 00:25
поделиться

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

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
ответ дан 4 December 2019 в 00:25
поделиться

Я заметил, что ваше первое предложение сравнивает только на равенство, а не на сортировку, что позволяет дополнительно сэкономить на эффективности.

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;
}

Конечно, что такое средство проверки равенства без соответствующего производителя хэш-кода:

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;
}
0
ответ дан 4 December 2019 в 00:25
поделиться
Другие вопросы по тегам:

Похожие вопросы: