From 486341833397cfd48e41f9b87b5c898aa2a51f04 Mon Sep 17 00:00:00 2001 From: Mengbo Yang Date: Sun, 16 Jun 2024 00:48:53 +0800 Subject: [PATCH] =?UTF-8?q?feat(core):=20=E6=B7=BB=E5=8A=A0=20Windows=20?= =?UTF-8?q?=E8=B5=84=E6=BA=90=E7=AE=A1=E7=90=86=E5=99=A8=E9=A3=8E=E6=A0=BC?= =?UTF-8?q?=E5=AD=97=E7=AC=A6=E4=B8=B2=E6=AF=94=E8=BE=83=E5=99=A8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../WindowsExplorerStringComparator.java | 80 +++++++++++++++++++ .../WindowsExplorerStringComparatorTest.java | 71 ++++++++++++++++ 2 files changed, 151 insertions(+) create mode 100644 hutool-core/src/main/java/cn/hutool/core/comparator/WindowsExplorerStringComparator.java create mode 100644 hutool-core/src/test/java/cn/hutool/core/comparator/WindowsExplorerStringComparatorTest.java diff --git a/hutool-core/src/main/java/cn/hutool/core/comparator/WindowsExplorerStringComparator.java b/hutool-core/src/main/java/cn/hutool/core/comparator/WindowsExplorerStringComparator.java new file mode 100644 index 000000000..1b3d36eba --- /dev/null +++ b/hutool-core/src/main/java/cn/hutool/core/comparator/WindowsExplorerStringComparator.java @@ -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 资源管理器风格字符串比较器 + * + *

此比较器模拟了 Windows 资源管理器的文件名排序方式,可得到与其相同的排序结果。

+ * + *

假设有一个数组,包含若干个文件名 {@code {"abc2.doc", "abc1.doc", "abc12.doc"}}

+ *

在 Windows 资源管理器中以名称排序时,得到 {@code {"abc1.doc", "abc2.doc", "abc12.doc" }}

+ *

调用 {@code Arrays.sort(filenames);} 时,得到 {@code {"abc1.doc", "abc12.doc", "abc2.doc" }}

+ *

调用 {@code Arrays.sort(filenames, new WindowsExplorerStringComparator());} 时,得到 {@code {"abc1.doc", "abc2.doc", + * "abc12.doc" }},这与在资源管理器中看到的相同

+ * + * @author YMNNs + * @see + * Java - Sort Strings like Windows Explorer + */ +public class WindowsExplorerStringComparator implements Comparator { + + private static final Pattern splitPattern = Pattern.compile("\\d+|\\.|\\s"); + + @Override + public int compare(String str1, String str2) { + Iterator i1 = splitStringPreserveDelimiter(str1).iterator(); + Iterator 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 splitStringPreserveDelimiter(String str) { + Matcher matcher = splitPattern.matcher(str); + List 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; + } +} diff --git a/hutool-core/src/test/java/cn/hutool/core/comparator/WindowsExplorerStringComparatorTest.java b/hutool-core/src/test/java/cn/hutool/core/comparator/WindowsExplorerStringComparatorTest.java new file mode 100644 index 000000000..1dc9beffb --- /dev/null +++ b/hutool-core/src/test/java/cn/hutool/core/comparator/WindowsExplorerStringComparatorTest.java @@ -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 answer1 = new ArrayList() {{ + 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 answer2 = new ArrayList() {{ + add("abc1.doc"); + add("abc2.doc"); + add("abc12.doc"); + }}; + + @Test + public void testCompare1() { + List toSort = new ArrayList<>(answer1); + while (toSort.equals(answer1)) { + Collections.shuffle(toSort); + } + toSort.sort(new WindowsExplorerStringComparator()); + Assert.equals(toSort, answer1); + } + + @Test + public void testCompare2() { + List toSort = new ArrayList<>(answer2); + while (toSort.equals(answer2)) { + Collections.shuffle(toSort); + } + toSort.sort(new WindowsExplorerStringComparator()); + Assert.equals(toSort, answer2); + } +}