c# - .NET 中对以 1、10 和 2 开头的字符串进行排序并遵守数字顺序的最短方法是什么?

标签 c# .net string sorting lexicographic

我需要按如下方式对文件名进行排序:1.log、2.log、10.log

但是当我使用 OrderBy(fn => fn) 时,它会将它们排序为: 1.log, 10.log, 2.log

我显然知道这可以通过编写另一个比较器来完成,但是有没有一种更简单的方法可以将字典顺序更改为自然排序顺序?

编辑:目标是获得与在 Windows 资源管理器中选择“按名称排序”时相同的顺序。

最佳答案

您可以使用 Win32 CompareStringEx功能。在 Windows 7 上,它支持您需要的排序。 您将使用 P/Invoke:

static readonly Int32 NORM_IGNORECASE = 0x00000001;
static readonly Int32 NORM_IGNORENONSPACE = 0x00000002;
static readonly Int32 NORM_IGNORESYMBOLS = 0x00000004;
static readonly Int32 LINGUISTIC_IGNORECASE = 0x00000010;
static readonly Int32 LINGUISTIC_IGNOREDIACRITIC = 0x00000020;
static readonly Int32 NORM_IGNOREKANATYPE = 0x00010000;
static readonly Int32 NORM_IGNOREWIDTH = 0x00020000;
static readonly Int32 NORM_LINGUISTIC_CASING = 0x08000000;
static readonly Int32 SORT_STRINGSORT = 0x00001000;
static readonly Int32 SORT_DIGITSASNUMBERS = 0x00000008; 

static readonly String LOCALE_NAME_USER_DEFAULT = null;
static readonly String LOCALE_NAME_INVARIANT = String.Empty;
static readonly String LOCALE_NAME_SYSTEM_DEFAULT = "!sys-default-locale";

[DllImport("kernel32.dll", CharSet = CharSet.Unicode)]
static extern Int32 CompareStringEx(
  String localeName,
  Int32 flags,
  String str1,
  Int32 count1,
  String str2,
  Int32 count2,
  IntPtr versionInformation,
  IntPtr reserved,
  Int32 param
);

然后您可以创建一个使用 SORT_DIGITSASNUMBERS 标志的 IComparer:

class LexicographicalComparer : IComparer<String> {

  readonly String locale;

  public LexicographicalComparer() : this(CultureInfo.CurrentCulture) { }

  public LexicographicalComparer(CultureInfo cultureInfo) {
    if (cultureInfo.IsNeutralCulture)
      this.locale = LOCALE_NAME_INVARIANT;
    else
      this.locale = cultureInfo.Name;
  }

  public Int32 Compare(String x, String y) {
    // CompareStringEx return 1, 2, or 3. Subtract 2 to get the return value.
    return CompareStringEx( 
      this.locale, 
      SORT_DIGITSASNUMBERS, // Add other flags if required.
      x, 
      x.Length, 
      y, 
      y.Length, 
      IntPtr.Zero, 
      IntPtr.Zero, 
      0) - 2; 
  }

}

然后您可以在各种排序 API 中使用 IComparer:

var names = new [] { "2.log", "10.log", "1.log" };
var sortedNames = names.OrderBy(s => s, new LexicographicalComparer());

您还可以使用 StrCmpLogicalW这是 Windows 资源管理器使用的函数。它从 Windows XP 开始可用:

[DllImport("shlwapi.dll", CharSet = CharSet.Unicode)]
static extern Int32 StrCmpLogical(String x, String y);

class LexicographicalComparer : IComparer<String> {

  public Int32 Compare(String x, String y) {
    return StrCmpLogical(x, y);
  }

}

更简单,但您对比较的控制较少。

关于c# - .NET 中对以 1、10 和 2 开头的字符串进行排序并遵守数字顺序的最短方法是什么?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/7204663/

相关文章:

c# - 下拉值只选择第一个值

c# - RadAsyncUpload 抛出 HTTP 500,并在上传的文件名旁边显示一个红点

c++ - 当编译器知道长度时,为什么不优化字符串赋值?

c# - 类型 '...' 没有定义构造函数

c# - 是否可以从 Azure Key Vault key 中获取私钥?

c# - 在托管 C++ 中使用 C# DLL(命令行)

.net - SandcaSTLe 是否支持代码契约?

c# - 从 mvc Controller 使用 Web api Controller 操作

java - 如何操作一串 INSERT 数据并以正确的方式分隔列的值?

C++ 字符串和重载运算符