按数字顺序排序文件

时间:2014-03-14 16:59:25

标签: java sorting

我制作了一个程序,将文件夹中的所有文件组合在一起。

这是我的代码的一部分:

File folder = new File("c:/some directory");
File[] listOfFiles = folder.listFiles();
for (File file : listOfFiles){

if (file.isFile()){
    System.out.println(file.getName());
    File f = new File("c:/some directory"+file.getName());

但是,我希望我的文件可以按顺序排列: job1.script,job2.script,.....

但我明白了: job1.script,job10.script,job11.script,那个10,11,12 ...都在2的前面。

我希望我能够获得可以避免此问题的高效代码。

4 个答案:

答案 0 :(得分:6)

时间摆脱所有丛生代码,并使用Java 8!这个答案还包含Path类,它已经是Java 7的一部分,但在Java 8中似乎有了很大的改进。

代码:

private void init() throws IOException {
    Path directory = Paths.get("C:\\Users\\Frank\\Downloads\\testjob");
    Files.list(directory)
            .filter(path -> Files.isRegularFile(path))
            .filter(path -> path.getFileName().toString().startsWith("job"))
            .filter(path -> path.getFileName().toString().endsWith(".script"))
            .sorted(Comparator.comparingInt(this::pathToInt))
            .map(path -> path.getFileName())
            .forEach(System.out::println);
}

private int pathToInt(final Path path) {
    return Integer.parseInt(path.getFileName()
            .toString()
            .replace("job", "")
            .replace(".script", "")
    );
}

pathToInt的解释:

  1. 从给定的Path获取文件的字符串表示。
  2. 删除"job"".script"
  3. 尝试将String解析为整数。
  4. init的解释,主要方法:

    1. 获取文件所在目录的Path
    2. 在目录中获取一个由Path添加的懒惰填充列表,请注意:这些Path仍然完全合格!
    3. 保留常规文件。
    4. 保留Path的最后部分的文件,因此文件名(例如job1.script)以"job"开头。 请注意您需要首先获取Path的字符串表示形式,然后才能检查它,否则您将检查整个Path是否以名为{的目录开头{1}}。
    5. 对以"job"结尾的文件执行相同操作。
    6. 现在来了一个有趣的观点。在这里,我们根据".script"对文件列表进行排序,该Comparator通过调用pathToInt上的Path来比较我们获得的整数。这里我使用方法引用,方法comparingInt(ToIntFunction<? super T> keyExtractor期望一个函数将T(在本例中为Path)映射到int。这正是pathToInt的作用,因此它可以用作方法参考。
    7. 然后我将每个Path映射到仅由文件名组成的Path
    8. 最后,对于Stream<Path>的每个元素,我都会调用System.out.println(Path.toString())
    9. 看起来这段代码可能更容易编写,但是我故意写得更详细。我的设计是始终保持完整Path完整,forEach中代码的最后一部分实际上违反了该原则,因为它只被映射到文件名,因此您以后无法处理完整的Path

      此代码也设计为快速失败,因此期望文件以job(\D+).script的形式存在,如果不是这样,则会抛出NumberFormatException

      示例输出:

      job1.script
      job2.script
      job10.script
      job11.script
      

      一个可以说是更好的替代方案具有正则表达式的强大功能:

      private void init() throws IOException {
          Path directory = Paths.get("C:\\Users\\Frank\\Downloads\\testjob");
          Files.list(directory)
                  .filter(path -> Files.isRegularFile(path))
                  .filter(path -> path.getFileName().toString().matches("job\\d+.script"))
                  .sorted(Comparator.comparingInt(this::pathToInt))
                  .map(path -> path.getFileName())
                  .forEach(System.out::println);
      }
      
      private int pathToInt(final Path path) {
          return Integer.parseInt(path.getFileName()
                  .toString()
                  .replaceAll("job(\\d+).script", "$1")
          );
      }
      

      这里我使用正则表达式"job\\d+.script",它匹配以"job"开头的字符串,后跟一个或多个数字,后跟&#34; .script&#34;。
      我对pathToInt方法使用几乎相同的表达式,但是我使用捕获组,括号和$1来使用该捕获组。

      我还将提供一个简洁的方法来阅读一个大文件中的文件内容,正如您在问题中提到的那样:

      private void init() throws IOException {
          Path directory = Paths.get("C:\\Users\\Frank\\Downloads\\testjob");
          try (BufferedWriter writer = Files.newBufferedWriter(directory.resolve("masterjob.script"))) {
              Files.list(directory)
                      .filter(path -> Files.isRegularFile(path))
                      .filter(path -> path.getFileName().toString().matches("job\\d+.script"))
                      .sorted(Comparator.comparingInt(this::pathToInt))
                      .flatMap(this::wrappedLines)
                      .forEach(string -> wrappedWrite(writer, string));
          }
      }
      
      private int pathToInt(final Path path) {
          return Integer.parseInt(path.getFileName()
                  .toString()
                  .replaceAll("job(\\d+).script", "$1")
          );
      }
      
      private Stream<String> wrappedLines(final Path path) {
          try {
              return Files.lines(path);
          } catch (IOException ex) {
              //swallow
              return null;
          }
      }
      
      private void wrappedWrite(final BufferedWriter writer, final String string) {
          try {
              writer.write(string);
              writer.newLine();
          } catch (IOException ex) {
              //swallow
          }
      }
      

      请注意,lambdas不能抛出/捕获已检查的Exception,因此在代码周围编写包装器是必要的,它决定了如何处理错误。吞下异常很少是一个好主意,我只是在这里使用它来代码简单。

      这里真正的重大变化是,不是打印出名称,而是将每个文件映射到其内容并将其写入文件。

答案 1 :(得分:2)

如果您的文件名称始终为jobNumber.script,则可以对提供自定义比较器的array进行排序:

Arrays.sort(listOfFiles, new Comparator<File>(){
        @Override
        public int compare(File f1, File f2) {
            String s1 = f1.getName().substring(3, f1.getName().indexOf("."));
            String s2 = f2.getName().substring(3, f2.getName().indexOf("."));
            return Integer.valueOf(s1).compareTo(Integer.valueOf(s2));  
        }
});

public static void main(String[] args) throws Exception{
        File folder = new File(".");
        File[] listOfFiles = folder.listFiles(new FilenameFilter() {            
            @Override
            public boolean accept(File arg0, String arg1) {
                return arg1.endsWith(".script");
            }
        });
        System.out.println(Arrays.toString(listOfFiles));
        Arrays.sort(listOfFiles, new Comparator<File>(){
            @Override
            public int compare(File f1, File f2) {
                String s1 = f1.getName().substring(3, f1.getName().indexOf("."));
                String s2 = f2.getName().substring(3, f2.getName().indexOf("."));
                return Integer.valueOf(s1).compareTo(Integer.valueOf(s2));  
            }
        });
        System.out.println(Arrays.toString(listOfFiles));
    }

打印:

[.\job1.script, .\job1444.script, .\job4.script, .\job452.script, .\job77.script]
[.\job1.script, .\job4.script, .\job77.script, .\job452.script, .\job1444.script]

答案 2 :(得分:1)

最简单的解决方案是将所有低于10的数字填零。如

job01.script

而不是

job1.script

这假设不超过100个文件。使用更多,只需添加更多零。

否则,您需要分析并细分每个文件名,然后按数字对其进行排序。目前,它是由字符订购的。

答案 3 :(得分:0)

解决此问题的最简单方法是在名称前添加0 s。这就是我遇到同样问题时的所作所为。所以基本上你选择你拥有的最大数字(例如433234)并在所有数字前加biggestLength - currentNumLength个零。

一个例子:

最大号码为12345job12345.script

这样第一份工作就变成了job00001.script