【问题标题】:Sorting files 'numerically' instead of alphabetically in java在java中按“数字”而不是按字母顺序对文件进行排序
【发布时间】:2013-06-27 09:46:28
【问题描述】:

我有一个文件夹,其中包含以时间戳命名的文件。

当我尝试浏览每个文件时,它会按字母顺序对它们进行排序并给我这个顺序:

/home/user/buffereddata/1
/home/user/buffereddata/100
/home/user/buffereddata/1000
/home/user/buffereddata/200
/home/user/buffereddata/2000
/home/user/buffereddata/300

但我希望它们像这样排序:

/home/user/buffereddata/1
/home/user/buffereddata/100
/home/user/buffereddata/200
/home/user/buffereddata/300
/home/user/buffereddata/1000
/home/user/buffereddata/2000

这是我的代码:

File file = new File(System.getProperty("user.home") + "/buffereddata");

if(file.exists()) {
  File[] fileArray = file.listFiles();
  Arrays.sort(fileArray);
  for(File f : fileArray) {
    System.out.println(f);
  }
}

是否有一些(最好是简单的)方法以我想要循环的方式循环文件?

【问题讨论】:

  • 您可以将字符串解析为整数,然后将字符串和解析的整数放入两个相同索引的数组中,然后对整数数组进行排序,就像对字符串数组进行相同的交换一样。也许您需要自己编写排序方法。您也可以创建一个哈希表,一个 int 键和一个字符串。
  • 你应该写一个Comparator<File>,它基于文件名并按数字排序。
  • 您可以创建一个比较器,参见 (mkyong.com/java/…) 并进行数字比较(或用零填充名称以确保它们具有相同的长度并进行字符串比较)
  • Sort on a string that may contain a number 的可能重复项(不完全是,但足够接近以至于解决方案类似。)

标签: java


【解决方案1】:
Arrays.sort(fileArray, new Comparator<File>() {
    public int compare(File f1, File f2) {
        try {
            int i1 = Integer.parseInt(f1.getName());
            int i2 = Integer.parseInt(f2.getName());
            return i1 - i2;
        } catch(NumberFormatException e) {
            throw new AssertionError(e);
        }
    }
});

【讨论】:

  • 我试试看。我应该在比较器中还是在外部捕获我的 NumberFormatExceptions?
  • NumberFormatExceptions 呢?
  • 如果您预期是 NFE,则应检查 NFE。如果它不应该发生,您只需抛出 AssertionError 任何一种方式(并嵌入 NFE)。这就是为什么 NFE 是 RuntimeExcpetion 顺便说一句。
  • try { return ...; } catch (NFE e) { return f1.getName().compareTo(...); }(字符串比较的后备。)
  • 如果文件名有一个单独的两个整数可能是一个文件夹名称怎么办?
【解决方案2】:

您需要一个自定义比较器

    Arrays.sort(fileArray, new Comparator<File>() {
        public int compare(File f1, File f2) {
            int n1 = Integer.parseInt(f1.getName());
            int n2 = Integer.parseInt(f1.getName());
            return Integer.compare(n1, n2);
        }});

【讨论】:

    【解决方案3】:

    虽然其他答案在您的特定情况中是正确的(给定目录中的所有文件名都是数字),但这里有一个可以比较混合数字/非数字文件名的解决方案,例如version-1.10.3.txt 以一种直观的方式,类似于 Windows 资源管理器的方式:

    这个想法(which I have blogged about here.The idea was inspired by this answer here.)是将文件名拆分为数字/非数字段,然后比较两个文件名中的每个单独段,无论是数字(如果都是数字)还是字母- 其他数字:

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

    然后您可以像这样使用比较器:

    Arrays.sort(fileArray, Comparators.comparing(File::getName, new FilenameComparator()));
    

    【讨论】:

      【解决方案4】:

      基于this answer

      您可以使用比较函数定义自己的比较器:

      public class FileSizeComparator implements Comparator<File> {
          public int compare( File a, File b ) {
              String aName = a.getName();
              String bName = b.getName();
              // make both strings equal size by padding 0s to the smaller one
              // then compare the strings
              return aName.compareTo(bName); // dictionary order!
          }
      }
      

      compareTo 是一个 String 类方法,在这里可以为您带来好处。

      "0100".compareTo("1000"); // < 0
      "0100".compareTo("0200"); // < 0
      "0200".compareTo("1000"); // < 0
      

      所以如果你有 100、200、1000,你得到的是 100、200、1000,而不是 100、1000、200!

      应该有效,未经测试!

      【讨论】:

      • 不,这是错误的。 "200".compareTo("1000") 返回 1,而不是您建议的 -1。 String.compareTo() 进行字母比较,而不是数字比较。
      • 另外,FileSizeComparator 是比较器的误导类名称。
      • 天哪!你说的对!我错了。我对字符串 compareTo 函数有完全不同的概念:(我已经很久没有使用 java 了。
      • 是的。你应该编辑你的答案,或者删除它(已经有两个正确的答案);否则我将不得不投票。 :)
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2018-05-20
      • 2011-08-29
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2017-11-19
      相关资源
      最近更新 更多