Merge pull request #3620 from YMNNs/v5-dev

[新特性] 添加 Windows 资源管理器风格字符串比较器
This commit is contained in:
Golden Looly 2024-06-18 15:55:21 +08:00 committed by GitHub
commit bd164db9b4
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 151 additions and 0 deletions

View File

@ -0,0 +1,80 @@
package cn.hutool.core.comparator;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.Iterator;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
* Windows 资源管理器风格字符串比较器
*
* <p>此比较器模拟了 Windows 资源管理器的文件名排序方式可得到与其相同的排序结果</p>
*
* <p>假设有一个数组包含若干个文件名 {@code {"abc2.doc", "abc1.doc", "abc12.doc"}}</p>
* <p> Windows 资源管理器中以名称排序时得到 {@code {"abc1.doc", "abc2.doc", "abc12.doc" }}</p>
* <p>调用 {@code Arrays.sort(filenames);} 得到 {@code {"abc1.doc", "abc12.doc", "abc2.doc" }}</p>
* <p>调用 {@code Arrays.sort(filenames, new WindowsExplorerStringComparator());} 得到 {@code {"abc1.doc", "abc2.doc",
* "abc12.doc" }}这与在资源管理器中看到的相同</p>
*
* @author YMNNs
* @see
* <a href="https://stackoverflow.com/questions/23205020/java-sort-strings-like-windows-explorer">Java - Sort Strings like Windows Explorer</a>
*/
public class WindowsExplorerStringComparator 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()) {
return -1;
}
//first has more parts than i2 -> comes after
if (!i2.hasNext()) {
return 1;
}
String data1 = i1.next();
String data2 = i2.next();
int result;
try {
//If both data are numbers, then compare numbers
result = Long.compare(Long.parseLong(data1), Long.parseLong(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<>();
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;
}
}

View File

@ -0,0 +1,71 @@
package cn.hutool.core.comparator;
import cn.hutool.core.lang.Assert;
import org.junit.Test;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
/**
* {@link WindowsExplorerStringComparator} 单元测试类
*
* @author YMNNs
*/
public class WindowsExplorerStringComparatorTest {
List<String> answer1 = new ArrayList<String>() {{
add("filename");
add("filename 00");
add("filename 0");
add("filename 01");
add("filename.jpg");
add("filename.txt");
add("filename00.jpg");
add("filename00a.jpg");
add("filename00a.txt");
add("filename0");
add("filename0.jpg");
add("filename0a.txt");
add("filename0b.jpg");
add("filename0b1.jpg");
add("filename0b02.jpg");
add("filename0c.jpg");
add("filename01.0hjh45-test.txt");
add("filename01.0hjh46");
add("filename01.1hjh45.txt");
add("filename01.hjh45.txt");
add("Filename01.jpg");
add("Filename1.jpg");
add("filename2.hjh45.txt");
add("filename2.jpg");
add("filename03.jpg");
add("filename3.jpg");
}};
List<String> answer2 = new ArrayList<String>() {{
add("abc1.doc");
add("abc2.doc");
add("abc12.doc");
}};
@Test
public void testCompare1() {
List<String> toSort = new ArrayList<>(answer1);
while (toSort.equals(answer1)) {
Collections.shuffle(toSort);
}
toSort.sort(new WindowsExplorerStringComparator());
Assert.equals(toSort, answer1);
}
@Test
public void testCompare2() {
List<String> toSort = new ArrayList<>(answer2);
while (toSort.equals(answer2)) {
Collections.shuffle(toSort);
}
toSort.sort(new WindowsExplorerStringComparator());
Assert.equals(toSort, answer2);
}
}