Java Список файлов того же порядка, что и Windows Explorer - PullRequest
2 голосов
/ 06 февраля 2020

Я использую приведенный ниже код для получения порядка списка файлов: (например, проводник окон)

    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;
            }
        } 
    }

НО , вывод при запуске программы:

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

Ожидаемый вывод (как в проводнике):

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

in window explorer it showing like this(sort by name)

Что нужно сделать, чтобы получить список файлов, подобный этому?

ОБНОВЛЕНО

Реализовано решение, предоставленное @ 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

Ожидается

enter image description here

1 Ответ

4 голосов
/ 06 февраля 2020

Сортировка по имени в Windows сложна и намного сложнее, чем ваша реализация. Он также настраивается и зависит от версии.

ПРИМЕЧАНИЕ. Я создал демонстрационную версию, описанную в этом посте. Проверьте это на GitHub .

Сортировка имен файлов с использованием StrCmpLogicalWComparator function

Согласно некоторым (например, здесь ) Windows использует StrCmpLogicalW для сортировки файлов по имени.

Вы можете попробовать реализовать свой компаратор, вызвав эту системную функцию с помощью 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 Explorer настраивается. Вы можете изменить способ обработки чисел в именах файлов и включить так называемую «числовую сортировку». Вы можете прочитать, как настроить здесь . Числовая сортировка, как описано в документации:

При сортировке обрабатывать цифры как числа, например, сортировать «2» перед «10».

- https://docs.microsoft.com/en-us/windows/win32/api/stringapiset/nf-stringapiset-comparestringex#SORT_DIGITSASNUMBERS

При включенной числовой сортировке результат будет:

file names sorted with numerical sorting enabled

, тогда как при отключенной числовой сортировке это выглядит так :

file names sorted with numerical sorting disabled

Это заставляет меня думать, что Windows Explorer фактически использует функцию CompareStringEx для сортировки, которая может быть параметризованный для включения этой функции.

Сортировка имен файлов с использованием 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);
    }
}

Numeri c sort компаратор:

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;
    }
}

без нумерации c сортировщик сравнения:

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;
    }
}

ссылки

...