【问题标题】:Proper way to sort a File array in ascending order by last number按最后一个数字升序对文件数组进行排序的正确方法
【发布时间】:2018-07-29 07:34:44
【问题描述】:

我的内存里有各种文件,每个人都用一个数字和他的格式来完成他的名字,例如:

/storage/emulated/0/packets
  • 文件 1.x
  • 文件 2.x
  • ...
  • 文件 11.x

我做了一个代码来检索文件并按升序排序,但是当文件超过10个时,排序失败。

我的代码:

ArrayList<String> list;

public static boolean SetFileList(String directory){
    list = new ArrayList<>();

    if(DirectoryExist(directory)) { //DirectoryExist() verify if the parameter directory it's a true directory
        File[] files = new File(directory).listFiles();

        if (files == null) {
            return false;
        }

        else if (files.length == 0) {
            return false;
        }

        else {
            Arrays.sort(files); //<------this method is supposed to sort

            for (File file : files) {
                list.add(file.getName());
            }

            return true;
        }
    }

    else{
        return false;
    }
}

当我使用以下代码显示文件时:

for(int i = 0; i < list.size(); i++){
    Log.i("File", list.get(i));
}

抛出类似:

File: File 1.x
File: File 10.x
File: File 11.x
File: File 12.x
File: File 2.x
File: File 3.x
File: ...

但我想展示:

File: File 1.x
File: File 2.x
File: File 3.x
File: ...
File: File 10.x
File: File 11.x
File: File 12.x

为什么会这样?

【问题讨论】:

  • 检查你的 int 是否小于 10
  • 你必须实现一个只比较数字的特殊比较器

标签: java android arrays file sorting


【解决方案1】:

我已经添加了一种方法,并且还使用了比较方法来对文件名进行排序。

 public  boolean SetFileList(String directory){
    list = new ArrayList<>();

    if(DirectoryExist(directory)) { //DirectoryExist() verify if the parameter directory it's a true directory

        File[] files = new File(Environment.getExternalStorageDirectory() +"/"+directory).listFiles();

        if (files == null) {
            return false;
        }

        else if (files.length == 0) {
            return false;
        }

        else {
            Arrays.sort(files, new Comparator<File>() {
                @Override
                public int compare(File file, File t1) {
                    int n1 = extractNumber(file.getName());
                    int n2 = extractNumber(t1.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;
                }
            }); //<------this method is supposed to sort

            for (File file : files) {
                list.add(file.getName());
                System.out.println(file.getName());
            }

            return true;
        }
    }

    else{
        return false;
    }
}

【讨论】:

    【解决方案2】:

    使用Arrays.sort,您按自然顺序(字母数字)排序,因此 10 在 3 之前,因为 1 在 3 之前。

    这就是为什么需要一个新的比较器。也许您想要的是某种 Windows 资源管理器排序,其中数字部分被视为数字。

    这是一个实现:Java - Sort Strings like Windows Explorer。它比其他答案更复杂,因为它不仅仅对扩展前的最后一个数字进行排序。看看那里的例子。它比较两个字符串的对应字母和数字部分,并能够处理前导零。

    它输出以下内容

    file1.txt
    file3.txt
    file05.txt
    file7.txt
    file10.txt
    

    这是实现本身:

    public class WindowsSortOrderTest {
        public static void main(String args[]) throws UnsupportedEncodingException {
            List<String> files = Arrays.asList("file1.txt", "file10.txt", "file3.txt", "file7.txt");
    
            files.sort(new WindowsExplorerComparator());
    
            for (String file : files) {
                System.out.println(file);
            }
        }
    
    
        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;
            }
        }
    }
    

    【讨论】:

      【解决方案3】:

      这样怎么样? 从每个文件名中提取数字并进行比较。

      File[] files = new File(directory).listFiles();
      
      if (files == null || files.length == 0) {
          return false;
      }
      
      List<File> fileList = Arrays.asList(files);
      
      Collections.sort(fileList, (o1, o2) -> {
          // find the dots.
          int pointIndex1 = o1.getName().lastIndexOf(".");
          int pointIndex2 = o2.getName().lastIndexOf(".");
      
          // filename -> integer value.
          int val1 = Integer.valueOf(o1.getName().substring(0, pointIndex1));
          int val2 = Integer.valueOf(o2.getName().substring(0, pointIndex2));
      
          return Integer.compare(val1, val2);
      });
      
      for (File file : fileList) {
          list.add(file.getName());
      }
      
      return true;
      

      -- 编辑

      你可以用数组做到这一点!

      File[] files = new File(directory).listFiles();
      
      Arrays.sort(files, (o1, o2) -> {
          // find the dots.
          int pointIndex1 = o1.getName().lastIndexOf(".");
          int pointIndex2 = o2.getName().lastIndexOf(".");
      
          // filename -> integer value.
          int val1 = Integer.valueOf(o1.getName().substring(0, pointIndex1));
          int val2 = Integer.valueOf(o2.getName().substring(0, pointIndex2));
      
          return Integer.compare(val1, val2);
      });
      
      for (File file : fileList) {
          list.add(file.getName());
      }
      
      return true;
      

      【讨论】:

        【解决方案4】:

        以下方法返回按编号排序的文件名列表。

        它在扩展之前提取一个或多个数字并解析一个整数值。因此,即使文件名在不同的地方包含数字,它也可以工作。

        扩展名前没有数字的文件不会被比较并转到列表末尾。

        如果具有给定名称的文件不存在或不是目录,则该方法返回一个空列表。

        public static List<String> sortFiles(String directory) {
            File dir = new File(directory);
            if (dir.exists() && dir.isDirectory()) {
                File[] files = dir.listFiles();
                if (files != null) {
                    // this will match the number before the extension
                    Pattern p = Pattern.compile("[\\d]+(?=\\.[a-zA-Z]+$)");
                    return Arrays.stream(files)
                            .map(File::getName)
                            .sorted(Comparator.comparing(n -> {
                                Matcher m = p.matcher(n);
                                if (m.find()) return Integer.parseInt(m.group());
                                else return Integer.MAX_VALUE;
                            }))
                            .collect(Collectors.toList());
                }
            }
            return Collections.emptyList();
        }
        

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2021-06-10
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多