【问题标题】:How to sort file names in ascending order?如何按升序对文件名进行排序?
【发布时间】:2013-05-29 16:19:16
【问题描述】:

我在一个文件夹中有一组文件,所有文件都以相似的名称开头,除了一个。这是一个例子:

Coordinate.txt
Spectrum_1.txt
Spectrum_2.txt
Spectrum_3.txt
.
.
.
Spectrum_11235

我可以列出指定文件夹中的所有文件,但列表不是按频谱编号升序排列的。示例:我在执行程序时得到如下结果:

Spectrum_999.txt
Spectrum_9990.txt
Spectrum_9991.txt
Spectrum_9992.txt
Spectrum_9993.txt
Spectrum_9994.txt
Spectrum_9995.txt
Spectrum_9996.txt
Spectrum_9997.txt
Spectrum_9998.txt
Spectrum_9999.txt

但是这个顺序是不正确的。 Spectrum_999.txt 后面应该有 Spectrum_1000.txt 文件。任何人都可以帮忙吗?代码如下:

import java.io.*;
import java.util.Arrays;
import java.util.Comparator;
import java.util.Scanner;

    public class FileInput {

        public void userInput()
        {
            Scanner scanner = new Scanner( System.in );
            System.out.println("Enter the file path: ");
            String dirPath = scanner.nextLine(); // Takes the directory path as the user input

            File folder = new File(dirPath);
            if(folder.isDirectory())
            {
                File[] fileList = folder.listFiles();

                Arrays.sort(fileList);

                System.out.println("\nTotal number of items present in the directory: " + fileList.length );


                // Lists only files since we have applied file filter
                for(File file:fileList)
                {
                    System.out.println(file.getName());
                }

                // Creating a filter to return only files.
                FileFilter fileFilter = new FileFilter()
                {
                    @Override
                    public boolean accept(File file) {
                        return !file.isDirectory();
                    }
                };

                fileList = folder.listFiles(fileFilter);

                // Sort files by name
                Arrays.sort(fileList, new Comparator()
                {
                    @Override
                    public int compare(Object f1, Object f2) {
                        return ((File) f1).getName().compareTo(((File) f2).getName());
                    }
                });

                //Prints the files in file name ascending order
                for(File file:fileList)
                {
                    System.out.println(file.getName());
                }

            }   
        }
    }

【问题讨论】:

    标签: java file sorting arraylist


    【解决方案1】:

    您可以使用Collections.sort(fileList); 对arraylist 进行排序。

    然后使用

     for(File file:fileList)                
             System.out.println(file.getName());
    

    Collections.sort()

    【讨论】:

    • 我试过这个,但是对于Collections.sort(fileList);,会显示以下错误:The method sort(List<T>) in the type Collections is not applicable for the arguments (File[])
    • 这是什么问题
    • 我删除了代码Arrays.sort(fileList, new Comparator() { @Override public int compare(Object f1, Object f2) { return ((File) f1).getName().compareTo(((File) f2).getName()); } });并尝试Collections.sort(fileList);但显示以下错误:The method sort(List<T>) in the type Collections is not applicable for the arguments (File[])如何继续?
    • 是的。但问题仍然没有解决。我希望文件名中的数字应该按升序排列,而不是上面显示的列表。
    • 然后使用子串并将它们放入一个列表中
    【解决方案2】:

    您要求的是数字排序。您需要实现 Comparator 并将其传递给 Arrays#sort 方法。在比较方法中,您需要从每个文件名中提取数字,然后比较数字。

    你得到你现在得到的输出的原因是排序发生在alphanumerically

    这是一个非常基本的方法。此代码使用简单的String-操作来提取数字。如果您知道文件名的格式(在您的情况下为Spectrum_<number>.txt),则此方法有效。更好的提取方法是使用regular expression

    public class FileNameNumericSort {
    
        private final static File[] files = {
            new File("Spectrum_1.txt"),
            new File("Spectrum_14.txt"),
            new File("Spectrum_2.txt"),
            new File("Spectrum_7.txt"),     
            new File("Spectrum_1000.txt"), 
            new File("Spectrum_999.txt"), 
            new File("Spectrum_9990.txt"), 
            new File("Spectrum_9991.txt"), 
        };
    
        @Test
        public void sortByNumber() {
            Arrays.sort(files, new Comparator<File>() {
                @Override
                public int compare(File o1, File o2) {
                    int n1 = extractNumber(o1.getName());
                    int n2 = extractNumber(o2.getName());
                    return n1 - n2;
                }
    
                private int extractNumber(String name) {
                    int i = 0;
                    try {
                        int s = name.indexOf('_')+1;
                        int e = name.lastIndexOf('.');
                        String number = name.substring(s, e);
                        i = Integer.parseInt(number);
                    } catch(Exception e) {
                        i = 0; // if filename does not match the format
                               // then default to 0
                    }
                    return i;
                }
            });
    
            for(File f : files) {
                System.out.println(f.getName());
            }
        }
    }
    

    输出

    Spectrum_1.txt
    Spectrum_2.txt
    Spectrum_7.txt
    Spectrum_14.txt
    Spectrum_999.txt
    Spectrum_1000.txt
    Spectrum_9990.txt
    Spectrum_9991.txt
    

    【讨论】:

    • @LukasEder Here a is a very basic way of doing it. This code uses simple String-operation to extract the numbers. This works if you know the format of the filename, in your case Spectrum_&lt;number&gt;.txt. A better way of doing the extraction is to use regular expression. 此处的答案并非旨在提供完全可工作的生产质量代码,而是提供有关如何解决问题的提示以及最终的非常基本的代码示例。希望我能回答你的问题。
    【解决方案3】:
    Arrays.sort(fileList, new Comparator()
    {
        @Override
        public int compare(Object f1, Object f2) {
            String fileName1 = ((File) f1).getName();
            String fileName2 = ((File) f1).getName();
    
            int fileId1 = Integer.parseInt(fileName1.split("_")[1]);
            int fileId2 = Integer.parseInt(fileName2.split("_")[1]);
    
            return fileId1 - fileId2;
        }
    });
    

    确保处理名称中没有 _ 的文件

    【讨论】:

      【解决方案4】:

      Commons IO 库中提供的NameFileComparator 类具有按名称、上次修改日期、大小等对文件数组进行排序的功能。文件可以按升序和降序排序,区分大小写或不区分大小写。

      进口:

      org.apache.commons.io.comparator.NameFileComparator

      代码:

      File directory = new File(".");
      File[] files = directory.listFiles();
      Arrays.sort(files, NameFileComparator.NAME_COMPARATOR)
      

      【讨论】:

      • 这并没有按照 OP 想要的方式进行排序。它忽略了数字。
      【解决方案5】:

      您可以在上面的评论中找到问题的解决方案,但考虑到仅发布了链接,我提供了该站点的代码。效果很好。

      1. 您需要创建自己的 AlphanumericalComparator。

         import java.io.File;
         import java.util.Comparator;
        
        public class AlphanumFileComparator implements Comparator
        {
        
           private final boolean isDigit(char ch)
           {
            return ch >= 48 && ch <= 57;
           }
        
        
        private final String getChunk(String s, int slength, int marker)
        {
            StringBuilder chunk = new StringBuilder();
            char c = s.charAt(marker);
            chunk.append(c);
            marker++;
            if (isDigit(c))
            {
                while (marker < slength)
                {
                    c = s.charAt(marker);
                    if (!isDigit(c))
                        break;
                    chunk.append(c);
                    marker++;
                }
            } else
            {
                while (marker < slength)
                {
                    c = s.charAt(marker);
                    if (isDigit(c))
                        break;
                    chunk.append(c);
                    marker++;
                }
            }
            return chunk.toString();
        }
        
        public int compare(Object o1, Object o2)
        {
            if (!(o1 instanceof File) || !(o2 instanceof File))
            {
                return 0;
            }
            File f1 = (File)o1;
            File f2 = (File)o2;
            String s1 = f1.getName();
            String s2 = f2.getName();
        
            int thisMarker = 0;
            int thatMarker = 0;
            int s1Length = s1.length();
            int s2Length = s2.length();
        
            while (thisMarker < s1Length && thatMarker < s2Length)
            {
                String thisChunk = getChunk(s1, s1Length, thisMarker);
                thisMarker += thisChunk.length();
        
                String thatChunk = getChunk(s2, s2Length, thatMarker);
                thatMarker += thatChunk.length();
        
                /** If both chunks contain numeric characters, sort them numerically **/
        
                int result = 0;
                if (isDigit(thisChunk.charAt(0)) && isDigit(thatChunk.charAt(0)))
                {
                    // Simple chunk comparison by length.
                    int thisChunkLength = thisChunk.length();
                    result = thisChunkLength - thatChunk.length();
                    // If equal, the first different number counts
                    if (result == 0)
                    {
                        for (int i = 0; i < thisChunkLength; i++)
                        {
                            result = thisChunk.charAt(i) - thatChunk.charAt(i);
                            if (result != 0)
                            {
                                return result;
                            }
                        }
                    }
                } else
                {
                    result = thisChunk.compareTo(thatChunk);
                }
        
                if (result != 0)
                    return result;
            }
        
            return s1Length - s2Length;
        }
        }
        

      2。根据此类对文件进行排序。

           File[] listOfFiles = rootFolder.listFiles();
           Arrays.sort(listOfFiles, new AlphanumFileComparator() );
           ...to sth with your files.
      

      希望对您有所帮助。它对我有用,就像一个魅力。

      解决方案来自:http://www.davekoelle.com/files/AlphanumComparator.javahere

      【讨论】:

        【解决方案6】:

        只是另一种方法,但使用 java8 的强大功能

        List<Path> x = Files.list(Paths.get("C:\\myPath\\Tools"))
                    .filter(p -> Files.exists(p))
                    .map(s -> s.getFileName())
                    .sorted()
                    .collect(Collectors.toList());
        
        x.forEach(System.out::println);
        

        【讨论】:

        • 如果您使用映射,排序后的所有内容都将使用文件名(= 字符串对象)而不是实际路径或文件对象。在收集结果时,toList 实际上会生成一个List&lt;String&gt; 而不是List&lt;Path&gt; 对象。我已经修改了您的代码以满足我的需求:File[] sortedFiles = Arrays.stream(files).filter(f -&gt; Files.exists(f.toPath())).sorted(Comparator.comparing(File::getName)).toArray(File[]::new);
        • sorted() 使用自然排序,它将“a_10”放在“a_9”之前。这不是问题所要求的。
        【解决方案7】:

        currently accepted answer 仅对始终称为相同名称的文件的数字后缀执行此操作(即忽略前缀)。

        A much more generic solution, which I blogged about here,适用于任何文件名,将名称拆分为段并按数字(如果两个段都是数字)或字典顺序对段进行排序,否则。 Idea inspired from this answer:

        public final class FilenameComparator implements Comparator<String> {
            private static final Pattern NUMBERS = 
                Pattern.compile("(?<=\\D)(?=\\d)|(?<=\\d)(?=\\D)");
            @Override public final int compare(String o1, String o2) {
                // Optional "NULLS LAST" semantics:
                if (o1 == null || o2 == null)
                    return o1 == null ? o2 == null ? 0 : -1 : 1;
        
                // Splitting both input strings by the above patterns
                String[] split1 = NUMBERS.split(o1);
                String[] split2 = NUMBERS.split(o2);
                for (int i = 0; i < Math.min(split1.length, split2.length); i++) {
                    char c1 = split1[i].charAt(0);
                    char c2 = split2[i].charAt(0);
                    int cmp = 0;
        
                    // If both segments start with a digit, sort them numerically using 
                    // BigInteger to stay safe
                    if (c1 >= '0' && c1 <= '9' && c2 >= '0' && c2 <= '9')
                        cmp = new BigInteger(split1[i]).compareTo(new BigInteger(split2[i]));
        
                    // If we haven't sorted numerically before, or if numeric sorting yielded 
                    // equality (e.g 007 and 7) then sort lexicographically
                    if (cmp == 0)
                        cmp = split1[i].compareTo(split2[i]);
        
                    // Abort once some prefix has unequal ordering
                    if (cmp != 0)
                        return cmp;
                }
        
                // If we reach this, then both strings have equally ordered prefixes, but 
                // maybe one string is longer than the other (i.e. has more segments)
                return split1.length - split2.length;
            }
        }
        

        这也可以处理带有颠覆的版本,例如类似version-1.2.3.txt

        【讨论】:

        • 我看到你在使用正则表达式,我想知道你从哪里得到灵感...我想你应该得到一个 cookie ;-)
        • @A4L:你说得对,我已经记下了我的消息来源,我应该记下的!
        【解决方案8】:

        只需使用:

        1. 对于升序:Collections.sort(List)

        2. 对于降序:Collections.sort(List,Collections.reverseOrder())

        【讨论】:

        • 必须是 List 类型
        • Collections.sort 使用自然排序,将“a_10”放在“a_9”之前。这不是问题所要求的。
        【解决方案9】:

        Ivan Gerasimiv 发现了智能 AlphaDecimal 比较器的最佳实现 here。它被提议作为标准 JDK 比较器 here 的扩展,并在线程 here 中进行了讨论。
        不幸的是,这种变化仍然没有进入 JDK(据我所知)。但是您可以使用第一个链接中的代码作为解决方案。对我来说,它就像一种魅力。

        【讨论】:

          【解决方案10】:

          我的 kotlin 版本算法。该算法用于按名称对文件和目录进行排序。

          import kotlin.Comparator
          
          const val DIFF_OF_CASES = 'a' - 'A'
          
          /** File name comparator. */
          class FileNameComparator(private val caseSensitive: Boolean = false) : Comparator<String> {
          
              override fun compare(left: String, right: String): Int {
                  val csLeft = left.toCharArray()
                  val csRight = right.toCharArray()
                  var isNumberRegion = false
                  var diff = 0; var i = 0; var j = 0
                  val lenLeft = csLeft.size; val lenRight = csRight.size
                  while (i < lenLeft && j < lenRight) {
                      val cLeft = getCharByCaseSensitive(csLeft[i])
                      val cRight = getCharByCaseSensitive(csRight[j])
                      val isNumericLeft = cLeft in '0'..'9'
                      val isNumericRight = cRight in '0'..'9'
                      if (isNumericLeft && isNumericRight) {
                          // Number start!
                          if (!isNumberRegion) {
                              isNumberRegion = true
                              // Remove prefix '0'
                              while (i < lenLeft && cLeft == '0') i++
                              while (j < lenRight && cRight == '0') j++
                              if (i == lenLeft || j == lenRight) break
                          }
                          // Diff start: calculate the diff value.
                          if (cLeft != cRight && diff == 0) diff = cLeft - cRight
                      } else {
                          if (isNumericLeft != isNumericRight) {
                              // One numeric and one char: the longer one is backwards.
                              return if (isNumberRegion) { if (isNumericLeft) 1 else -1 } else cLeft - cRight
                          } else {
                              // Two chars: if (number) diff don't equal 0, return it (two number have same length).
                              if (diff != 0) return diff
                              // Calculate chars diff.
                              diff = cLeft - cRight
                              if (diff != 0) return diff
                              // Reset!
                              isNumberRegion = false
                              diff = 0
                          }
                      }
                      i++; j++
                  }
                  if (i == lenLeft) i--
                  if (j == lenRight) j--
                  return csLeft[i] - csRight[j]
              }
          
              private fun getCharByCaseSensitive(c: Char): Char
                      = if (caseSensitive) c else if (c in 'A'..'Z') (c + DIFF_OF_CASES) else c
          }
          
          

          例如,对于输入,

                    "A__01__02",
                      "A__2__02",
                      "A__1__23",
                      "A__11__23",
                      "A__3++++",
                      "B__1__02",
                      "B__22_13",
                      "1_22_2222",
                      "12_222_222",
                      "2222222222",
                      "1.sadasdsadsa",
                      "11.asdasdasdasdasd",
                      "2.sadsadasdsad",
                      "22.sadasdasdsadsa",
                      "3.asdasdsadsadsa",
                      "adsadsadsasd1",
                      "adsadsadsasd10",
                      "adsadsadsasd3",
                      "adsadsadsasd02"
          

          它会将它们排序为,

          1.sadasdsadsa 1_22_2222 2.sadsadasdsad 3.asdasdsadsadsa 11.asdasdasdasdasd 12_222_222 22.sadasdasdsadsa 2222222222 A__01__02 A__1__23 A__2__02 A__3++++ A__11__23 adsadsadsasd02 adsadsadsasd1 adsadsadsasd3 adsadsadsasd10 B__1__02 B__22_13 
          

          【讨论】:

            【解决方案11】:

            实现此目的的最简单方法是使用 NameFileComparator.NAME_COMPARATOR。以下是代码格式:

            File[] fileNamesArr = filePath.listFiles();
            if(fileNamesArr != null && fileNamesArr.length > 0) {
                Arrays.sort(fileNamesArr, NameFileComparator.NAME_COMPARATOR);
            }
            

            为了实现 NameFileComparator,您需要添加以下 maven 库:

            实现'commons-io:commons-io:2.6'

            同时导入:

            导入 org.apache.commons.io.comparator.NameFileComparator;

            就是这样……

            【讨论】:

              猜你喜欢
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 2019-01-04
              • 2016-01-14
              • 1970-01-01
              相关资源
              最近更新 更多