Java文件列表与Window Explorer一样的顺序 [英] Java File list same order like Window explorer
问题描述
我正在使用下面的代码来获取文件列表的顺序:(例如窗口浏览器)
I am using the code below to get file list ordering: (like window explorer)
package com.codnix.quickpdfgenerator.testing;
import java.io.File;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Comparator;
import java.util.Iterator;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class FileListOrder {
public static void main(String args[]) {
//huge test data set ;)
File folder = new File("C:\\Users\\Codnix\\Desktop\\Test Sequence");
File[] listOfFiles = folder.listFiles();
List<File> filenames = Arrays.asList(listOfFiles);
//adaptor for comparing files
Collections.sort(filenames, new Comparator<File>() {
private final Comparator<String> NATURAL_SORT = new WindowsExplorerComparator();
@Override
public int compare(File o1, File o2) {;
return NATURAL_SORT.compare(o1.getName(), o2.getName());
}
});
for (File f : filenames) {
System.out.println(f);
}
}
public static class WindowsExplorerComparator implements Comparator<String> {
private static final Pattern splitPattern = Pattern.compile("\\d+|\\.|\\s");
@Override
public int compare(String str1, String str2) {
Iterator<String> i1 = splitStringPreserveDelimiter(str1).iterator();
Iterator<String> i2 = splitStringPreserveDelimiter(str2).iterator();
while (true) {
//Til here all is equal.
if (!i1.hasNext() && !i2.hasNext()) {
return 0;
}
//first has no more parts -> comes first
if (!i1.hasNext() && i2.hasNext()) {
return -1;
}
//first has more parts than i2 -> comes after
if (i1.hasNext() && !i2.hasNext()) {
return 1;
}
String data1 = i1.next();
String data2 = i2.next();
int result;
try {
//If both datas are numbers, then compare numbers
result = Long.compare(Long.valueOf(data1), Long.valueOf(data2));
//If numbers are equal than longer comes first
if (result == 0) {
result = -Integer.compare(data1.length(), data2.length());
}
} catch (NumberFormatException ex) {
//compare text case insensitive
result = data1.compareToIgnoreCase(data2);
}
if (result != 0) {
return result;
}
}
}
private List<String> splitStringPreserveDelimiter(String str) {
Matcher matcher = splitPattern.matcher(str);
List<String> list = new ArrayList<String>();
int pos = 0;
while (matcher.find()) {
list.add(str.substring(pos, matcher.start()));
list.add(matcher.group());
pos = matcher.end();
}
list.add(str.substring(pos));
return list;
}
}
}
但,当我运行程序时输出:
BUT, output when I run the program:
C:\Users\Codnix\Desktop\Test Sequence\1 test -12.jpg
C:\Users\Codnix\Desktop\Test Sequence\1 test --11.jpg
C:\Users\Codnix\Desktop\Test Sequence\1 test ---10.jpg
预期输出(如窗口浏览器):
Expected Output (Like window explorer):
C:\Users\Codnix\Desktop\Test Sequence\1 test ---10.jpg
C:\Users\Codnix\Desktop\Test Sequence\1 test --11.jpg
C:\Users\Codnix\Desktop\Test Sequence\1 test -12.jpg
如何获取这样的文件列表?
What to do to get file list like this?
已更新
@jannis提供的已实现的解决方案
And here its output
before
1 test ---10.jpg
1 test --11.jpg
1 test -12.jpg
1.jpg
10.jpg
2.jpg
After (output)
1.jpg
1 test ---10.jpg
1 test --11.jpg
1 test -12.jpg
2.jpg
10.jpg
期望
推荐答案
在Windows中按名称排序很棘手,而且比您的实现复杂得多.它也是可配置的,并取决于版本.
Sorting by name in Windows is tricky and far more complicated than your implementation. It's also configurable and version dependent.
注意:我为这篇文章后面的内容创建了一个演示. 在GitHub上查看.
NOTE: I created a demo for what follows in this post. Check it out on GitHub.
根据某些情况(例如 StrCmpLogicalW 用于按名称对文件进行排序.
According to some (e.g. here) Windows uses StrCmpLogicalW for sorting files by name.
您可以尝试通过使用JNA调用此系统函数来实现比较器(不要忘记包含项目中的JNA库):
You could try to implement your comparator by calling this system function using JNA (don't forget to include JNA library in your project):
比较器:
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资源管理器对文件进行排序的方式是可配置的.您可以更改文件名中数字的处理方式,并切换所谓的数字排序".您可以阅读如何配置此
Handling file names that contain digits
I mentioned earlier that the way that Windows Explorer sorts files is configurable. You can change how numbers in file names are handled and toggle so-called "numerical sorting". You can read how to configure this here. Numerical sorting as explained in the docs:
在排序过程中将数字视为数字,例如,在"10"之前将"2"排序.
Treat digits as numbers during sorting, for example, sort "2" before "10".
启用数字排序后,结果为:
With numerical sorting enabled the result is:
在禁用数字排序的情况下,它看起来像这样:
whereas with numerical sorting disabled it looks like this:
这使我认为Windows资源管理器实际上使用了 CompareStringEx函数用于排序,可以对其进行参数化以启用此功能.
This makes me think that Windows Explorer in fact uses CompareStringEx function for sorting which can be parameterized to enable this feature.
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;
}
}
参考
- 马丁·利弗塞奇(Martin Liversage)的 回答在.NET中对以1、10和2开头的字符串进行排序并遵守数字顺序的最短方法是什么?
- hmuelner's 答案为"Windows资源管理器使用的排序顺序中的第一个字符是什么?"
- Martin Liversage's answer to "What is the shortest way in .NET to sort strings starting with 1, 10 and 2 and respect the number ordering?
- hmuelner's answer to "What is the first character in the sort order used by Windows Explorer?"
References
这篇关于Java文件列表与Window Explorer一样的顺序的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!