在 Windows 中按名称排序非常棘手,并且比您的实现复杂得多。它也是可配置的并且依赖于版本。
注意:我为本文中的后续内容创建了一个演示。在 GitHub 上查看 https://github.com/jannis-baratheon/stackoverflow--sorting-with-winapi.
使用排序文件名StrCmpLogicalW 比较器函数 https://learn.microsoft.com/pl-pl/windows/win32/api/shlwapi/nf-shlwapi-strcmplogicalw
根据一些人的说法(例如here https://stackoverflow.com/questions/4290541/what-is-the-first-character-in-the-sort-order-used-by-windows-explorer/4314191#4314191) Windows 使用StrCmp逻辑W https://learn.microsoft.com/pl-pl/windows/win32/api/shlwapi/nf-shlwapi-strcmplogicalw用于按名称对文件进行排序。
您可以尝试使用 JNA 调用此系统函数来实现比较器(不要忘记包括JNA图书馆 https://mvnrepository.com/artifact/net.java.dev.jna/jna在你的项目中):
比较器:
public class StrCmpLogicalWComparator implements Comparator<String> {
@Override
public int compare(String o1, String o2) {
return Shlwapi.INSTANCE.StrCmpLogicalW(
new WString(o1), new WString(o2));
}
}
JNA部分:
import com.sun.jna.WString;
import com.sun.jna.win32.StdCallLibrary;
public interface Shlwapi extends StdCallLibrary {
Shlwapi INSTANCE = Native.load("Shlwapi", Shlwapi.class);
int StrCmpLogicalW(WString psz1, WString psz2);
}
处理包含数字的文件名
我之前提到过 Windows 资源管理器对文件进行排序的方式是可配置的。您可以更改文件名中数字的处理方式并切换所谓的“数字排序”。您可以阅读如何配置它here https://www.tenforums.com/tutorials/91417-enable-disable-numerical-sorting-file-explorer-windows-10-a.html。文档中解释的数字排序:
排序时将数字视为数字,例如将“2”排在“10”之前。
-- https://learn.microsoft.com/en-us/windows/win32/api/stringapiset/nf-stringapiset-comparestringex#SORT_DIGITSASNUMBERS https://learn.microsoft.com/en-us/windows/win32/api/stringapiset/nf-stringapiset-comparestringex#SORT_DIGITSASNUMBERS
启用数字排序后,结果为:
而禁用数字排序后,它看起来像这样:
这让我认为 Windows 资源管理器实际上使用比较字符串Ex函数 https://learn.microsoft.com/en-us/windows/win32/api/stringapiset/nf-stringapiset-comparestringex用于排序,可以对其进行参数化以启用此功能。
使用排序文件名比较字符串Ex函数 https://learn.microsoft.com/en-us/windows/win32/api/stringapiset/nf-stringapiset-comparestringex
JNA部分:
import com.sun.jna.Pointer;
import com.sun.jna.WString;
import com.sun.jna.win32.StdCallLibrary;
public interface Kernel32 extends StdCallLibrary {
Kernel32 INSTANCE = Native.load("Kernel32", Kernel32.class);
WString INVARIANT_LOCALE = new WString("");
int CompareStringEx(WString lpLocaleName,
int dwCmpFlags,
WString lpString1,
int cchCount1,
WString lpString2,
int cchCount2,
Pointer lpVersionInformation,
Pointer lpReserved,
int lParam);
default int CompareStringEx(int dwCmpFlags,
String str1,
String str2) {
return Kernel32.INSTANCE
.CompareStringEx(
INVARIANT_LOCALE,
dwCmpFlags,
new WString(str1),
str1.length(),
new WString(str2),
str2.length(),
Pointer.NULL,
Pointer.NULL,
0);
}
}
数字排序比较器:
public class CompareStringExNumericComparator implements Comparator<String> {
private static int SORT_DIGITSASNUMBERS = 0x00000008;
@Override
public int compare(String o1, String o2) {
int compareStringExComparisonResult =
Kernel32.INSTANCE.CompareStringEx(SORT_DIGITSASNUMBERS, o1, o2);
// CompareStringEx returns 1, 2, 3 respectively instead of -1, 0, 1
return compareStringExComparisonResult - 2;
}
}
非数字排序比较器:
public class CompareStringExNonNumericComparator implements Comparator<String> {
private static String INVARIANT_LOCALE = "";
private static int NO_OPTIONS = 0;
@Override
public int compare(String o1, String o2) {
int compareStringExComparisonResult =
Kernel32.INSTANCE.CompareStringEx(NO_OPTIONS, o1, o2);
// CompareStringEx returns 1, 2, 3 respectively instead of -1, 0, 1
return compareStringExComparisonResult - 2;
}
}
参考
-
马丁·利弗西奇 https://stackoverflow.com/users/98607/martin-liversage 回答“.NET 中对以 1、10 和 2 开头的字符串进行排序并遵循数字顺序的最短方法是什么? https://stackoverflow.com/a/7205788/4494577
-
赫穆尔纳的 https://stackoverflow.com/users/324065/hmuelner 回答“Windows 资源管理器使用的排序顺序中的第一个字符是什么?” https://stackoverflow.com/a/4314191/4494577