Проще всего сделать P / Invoke встроенной функции в Windows и использовать ее как функцию сравнения в ваших IComparer
:
[DllImport("shlwapi.dll", CharSet = CharSet.Unicode)]
private static extern int StrCmpLogicalW(string psz1, string psz2);
У Майкла Каплана есть несколько примеров того, как работает эта функция , а также изменения, внесенные в Vista, чтобы сделать ее более интуитивно понятной. Положительной стороной этой функции является то, что она будет вести себя так же, как и версия Windows, в которой она работает, однако это означает, что она отличается в разных версиях Windows, поэтому вам нужно подумать, является ли это проблемой для вас.
Итак, полная реализация будет примерно такой:
[SuppressUnmanagedCodeSecurity]
internal static class SafeNativeMethods
{
[DllImport("shlwapi.dll", CharSet = CharSet.Unicode)]
public static extern int StrCmpLogicalW(string psz1, string psz2);
}
public sealed class NaturalStringComparer : IComparer<string>
{
public int Compare(string a, string b)
{
return SafeNativeMethods.StrCmpLogicalW(a, b);
}
}
public sealed class NaturalFileInfoNameComparer : IComparer<FileInfo>
{
public int Compare(FileInfo a, FileInfo b)
{
return SafeNativeMethods.StrCmpLogicalW(a.Name, b.Name);
}
}
Comparer<T>
вместо реализацииIComparer<T>
, вы получите встроенную реализациюIComparer
(неуниверсального) интерфейса, который вызывает ваш общий метод, для использования в API, которые его используют. Это тоже в принципе бесплатно: просто удалите «I» и изменитеpublic int Compare(...)
наpublic override int Compare(...)
. То же самое дляIEqualityComparer<T>
иEqualityComparer<T>
.Просто подумал, что добавлю к этому (с самым кратким решением, которое я смог найти):
Вышеуказанное дополняет любые числа в строке до максимальной длины всех чисел во всех строках и использует полученную строку для сортировки.
Приведение к (
int?
) позволяет.Max()
создавать коллекции строк без каких-либо чисел ( для пустого перечислимого выдает anInvalidOperationException
).источник
.DefaultIfEmpty().Max()
вместо приведения вint?
. Также стоит сделать,source.ToList()
чтобы избежать повторного перечисления перечисляемого.Ни одна из существующих реализаций не выглядела хорошо, поэтому я написал свою собственную. Результаты почти идентичны сортировке, используемой в современных версиях Windows Explorer (Windows 7/8). Единственные различия, которые я видел: 1) хотя раньше Windows (например, XP) обрабатывала числа любой длины, теперь она ограничена 19 цифрами - у меня неограниченно, 2) Windows дает несогласованные результаты с определенными наборами цифр Unicode - мои работы хорошо (хотя он не сравнивает цифры из суррогатных пар; как и Windows), и 3) мой не может различать разные типы непервичных весов сортировки, если они встречаются в разных разделах (например, "e-1é" vs " é1e- "- разделы до и после числа имеют разницу в диакритическом знаке и знаке препинания).
Подпись соответствует
Comparison<string>
делегату:Вот класс-оболочка для использования в качестве
IComparer<string>
:Пример:
Вот хороший набор имен файлов, которые я использую для тестирования:
источник
Чистое решение C # для linq orderby:
http://zootfroot.blogspot.com/2009/09/natural-sort-compare-with-linq-orderby.html
источник
Ответ Мэтьюса Хорсли - это самый быстрый метод, который не меняет поведения в зависимости от того, на какой версии Windows работает ваша программа. Однако это может быть еще быстрее, если создать регулярное выражение один раз и использовать RegexOptions.Compiled. Я также добавил возможность вставки средства сравнения строк, чтобы при необходимости можно было игнорировать регистр, и немного улучшил читаемость.
Использовать при
Это занимает 450 мс для сортировки 100 000 строк по сравнению с 300 мс для сравнения строк .net по умолчанию - довольно быстро!
источник
Мое решение:
Полученные результаты:
источник
Вам нужно быть осторожным - я смутно помню, что читал, что StrCmpLogicalW или что-то в этом роде не было строго транзитивным, и я наблюдал, что методы сортировки .NET иногда застревают в бесконечных циклах, если функция сравнения нарушает это правило.
Транзитивное сравнение всегда будет сообщать, что a <c, если a <b и b <c. Существует функция, которая выполняет естественное сравнение порядка сортировки, которое не всегда соответствует этому критерию, но я не могу вспомнить, является ли это StrCmpLogicalW или что-то еще.
источник
CultureInfo
имеет свойствоCompareInfo
, а возвращаемый объект может снабжать васSortKey
объектами. Их, в свою очередь, можно сравнивать и гарантировать транзитивность.Это мой код для сортировки строки, содержащей как буквы, так и цифры.
Во-первых, это метод расширения:
Затем просто используйте его в любом месте кода следующим образом:
Как это работает ? Заменив нулями:
Работает с кратными числами:
Надеюсь, это поможет.
источник
Добавляя к ответу Грега Бича (потому что я только что это искал), если вы хотите использовать это из Linq, вы можете использовать файл,
OrderBy
который принимает файлIComparer
. Например:источник
Вот относительно простой пример, который не использует P / Invoke и позволяет избежать выделения памяти во время выполнения.
Он не игнорирует начальные нули, поэтому
01
идет после2
.Соответствующий модульный тест:
источник
Я фактически реализовал его как метод расширения,
StringComparer
чтобы вы могли, например, сделать:StringComparer.CurrentCulture.WithNaturalSort()
илиStringComparer.OrdinalIgnoreCase.WithNaturalSort()
,Полученная
IComparer<string>
может использоваться во всех местах , какOrderBy
,OrderByDescending
,ThenBy
,ThenByDescending
,SortedSet<string>
и т.д. И вы все еще можете легко твик чувствительность к регистру, культуре и т.д.Реализация довольно тривиальна и должна хорошо работать даже с большими последовательностями.
Я также опубликовал его как крошечный пакет NuGet , так что вы можете просто сделать:
Код, включая комментарии к документации XML и набор тестов , доступен в репозитории NaturalSort.Extension на GitHub .
Вот весь код (если вы еще не можете использовать C # 7, просто установите пакет NuGet):
источник
Вот наивный однострочный способ LINQ без регулярных выражений (заимствованный из python):
источник
Dump()
. Спасибо, что указали.Расширяя пару предыдущих ответов и используя методы расширения, я придумал следующее, в котором нет предостережений в отношении потенциального множественного перечислимого перечисления или проблем с производительностью, связанных с использованием нескольких объектов регулярных выражений или ненужным вызовом регулярного выражения, что как говорится, он использует ToList (), что может свести на нет преимущества в больших коллекциях.
Селектор поддерживает универсальную типизацию, позволяющую назначать любого делегата, элементы в исходной коллекции изменяются селектором, а затем преобразуются в строки с помощью ToString ().
источник
Вдохновленный решением Майкла Паркера, вот
IComparer
реализация, которую вы можете применить к любому из методов упорядочивания linq:источник
Нам потребовалась естественная сортировка для работы с текстом по следующему шаблону:
По какой-то причине, когда я впервые посмотрел на SO, я не нашел этот пост и реализовал наш собственный. По сравнению с некоторыми из представленных здесь решений, хотя они схожи по концепции, они могут иметь то преимущество, что могут быть проще и понятнее. Однако, хотя я и попытался изучить узкие места в производительности, это все еще гораздо более медленная реализация, чем по умолчанию
OrderBy()
.Вот метод расширения, который я реализую:
Идея состоит в том, чтобы разбить исходные строки на блоки цифр и нецифровые (
"\d+|\D+"
). Поскольку это потенциально дорогостоящая задача, она выполняется только один раз для каждой записи. Затем мы используем средство сравнения сопоставимых объектов (извините, я не могу найти более точного способа сказать это). Он сравнивает каждый блок с соответствующим блоком в другой строке.Я хотел бы получить отзывы о том, как это можно улучшить и каковы основные недостатки. Обратите внимание, что ремонтопригодность важна для нас на данном этапе, и в настоящее время мы не используем ее в очень больших наборах данных.
источник
Версия, которую легче читать / поддерживать.
источник
Позвольте мне объяснить мою проблему и то, как я смог ее решить.
Проблема: - Сортировка файлов на основе FileName из объектов FileInfo, извлеченных из каталога.
Решение: - Я выбрал имена файлов из FileInfo и выделил часть имени файла «.png». Теперь просто выполните List.Sort (), который сортирует имена файлов в естественном порядке сортировки. Основываясь на моем тестировании, я обнаружил, что наличие .png нарушает порядок сортировки. Взгляните на приведенный ниже код
источник