对包含数字字符串的文件名数组进行排序

Posted

技术标签:

【中文标题】对包含数字字符串的文件名数组进行排序【英文标题】:Sorting an array of filenames containing strings with numbers 【发布时间】:2016-07-02 15:02:18 【问题描述】:

对于我的项目,我需要从 FTP 服务器下载一个 zip 文件,该服务器每年大约发布 13 次新的 zip。我需要按照服务器的命名约定下载最新的文件:Prefix + release number (one or two digits) + year (two digits) + suffix + ".zip"

例如: ALFP1016F.zip

前缀将始终相同 (ALFP),后缀为 F 或 P(代表“完整”或“部分”;我只需要以后缀 F 结尾的文件)。最重要的是,我需要忽略目录中的其他几个文件,因为它们具有不同的前缀。然后,我需要按照这个优先级顺序获取数组中的最新文件:

    最近一年。当然,不应将 99 年视为最近的一年。 最新版本号

例如,如果我有这个文件名列表 (full server directory):

1stpage712.pdf
1stpage914.pdf
ALFP1015F.zip
ALFP1015P.zip
ALFP716F.zip
ALFP716P.zip
FSFP816F.zip
FSFP816P.zip

我的预期输出将是ALFP716F.zip 因为 16 是最近的一年,而 7 是那一年的最新发布编号

.

这是我到目前为止所做的:

//necessary imports
import org.apache.commons.net.ftp.FTP;
import org.apache.commons.net.ftp.FTPClient;
import org.apache.commons.net.ftp.FTPFile;

//initialize FTP client
ftpClient = new FTPClient();

try 
    //connect to server
    ftpClient.connect(server, port);
    ftpClient.login(username, password);
    ftpClient.enterLocalPassiveMode();
    ftpClient.setFileType(FTP.BINARY_FILE_TYPE);

    //list all names from server
    String[] filenames = ftpClient.listNames();

    //return expected file name
    String expectedFileName = returnMostRecent(filenames);
 catch (Exception e)  
    e.printStackTrace(); 
 finally 
    try 
        if (ftpClient.isConnected()) 
            ftpClient.logout();
            ftpClient.disconnect();
            System.out.println("Disconnected from server");
        
     catch (IOException ex)  ex.printStackTrace(); 

我在编写returnMostRecent(String[]) 方法时做了一次悲惨的尝试,但结果却是一团糟,不值得在这里发布。

如何对这个数组进行排序并按照我的优先顺序有效地返回最近的文件?

【问题讨论】:

您可以使用Regular Expressions with Capturing Groups 隔离文件名的重要部分,然后使用它们来查找最新版本(无需实际排序)。如果这太复杂了,那么使用多个substring() 来做同样的事情。 【参考方案1】:

如果你使用 Java8,你可以这样做:

String file = Arrays.stream(filenames)
                    .filter(s -> s.startsWith("ALFP") && s.endsWith("F.zip"))
                    .max(getReleaseComparator())                        
                    .orElse(null);

版本比较器基于从文件名中提取数字并进行比较

【讨论】:

这假设版本号是个位数,而 brso05 答案不是 @BalajiKrishnan 是的,需要自定义比较器才能正确排序数字。我将把它留给 OP,因为 brso05 已经给出了提示 Stream.max 会比链接sortedfindFirst 更有效并且可能更清晰。此外,如果您不打算提供一个有效的比较器,您可能应该写类似appropriateComparator 而不是Comparator.reverseOrder() 这样至少很明显Comparator.reverseOrder() 不起作用。【参考方案2】:

我没有对此进行测试,但我认为它应该可以工作。

private String returnMostRecent(String[] fileNames) 
   String file = null;
   double version = -1;
   for(String name : listNames)
   
      // skip files that don't match
      if (!name.matches("ALFP[0-9]*F.zip"))
          continue;
      // get digits only
      String digits = name.replaceAll("\\D+","");
      // format digits to <year>.<version>
      String vstr = digits.substring(digits.length-2,digits.length()) + ".";
      if (digits.length() < 4)
         vstr += "0";
      vstr = digits.substring(0, digits.length()-2);
      double v = Double.parseDouble(vstr);
      if (v > version)
      
          version = v;
          file = name;
      
   

   return file;

【讨论】:

这似乎可以解决问题,前提是版本号始终是一位数字 - 情况并非如此。 我添加了两行代码。这应该会照顾它。 我不得不修改你的代码,但最终让它工作。谢谢!【参考方案3】:

我会建议这种方法:

final String[] filesArr =  "1stpage712.txt", "1stpage712.pdf", "1stpage914.pdf", "ALFP1015F.zip", "ALFP1015P.zip", "ALFP716F.zip",
            "ALFP716P.zip", "FSFP816F.zip", "FSFP816P.zip" ;

    // turn the array into a list.
    final List<String> filesList = new ArrayList<String>();
    // add to the list only the good candidates
    for (int i = 0; i < filesArr.length; i++) 
        if (filesArr[i].matches("ALFP\\d+F.zip")) 
            System.out.println("candidate");
            filesList.add(filesArr[i]);
        
    
    System.out.println(filesList);
    Collections.sort(filesList, new Comparator<String>() 

        @Override
        public int compare(String o1, String o2) 
            final SimpleDateFormat df = new SimpleDateFormat("mmyy");
            // get the date of the file
            final String dat1 = o1.substring(o1.indexOf("ALFP"), o1.indexOf("ALFP") + 3);
            final String dat2 = o2.substring(o2.indexOf("ALFP"), o2.indexOf("ALFP") + 3);
            Date date1;
            Date date2;
            try 
                date1 = df.parse(dat1);
                date2 = df.parse(dat2);

                return date1.compareTo(date2);
             catch (final ParseException e) 
                System.out.println("Error parsing date..");
                return 0;
            
        
    );

    // since the sort is made by date chronologically, the 1st element is the oldest and the last element is
    // the newest
    System.out.println("The file is: " + filesList.get(filesList.size() - 1));


【讨论】:

【参考方案4】:

我会建议这个解决方案:

private static String returnMostRecent(String[] fileNames)
    
       int lastTwoDigits = Calendar.getInstance().get(Calendar.YEAR) % 100;
       int fullFileRel = 0;
       int partialFileRel = 0;
       for(String myStr : fileNames)
       

          if(myStr.startsWith("ALFP"))
          
              System.out.println(myStr);
             if(myStr.endsWith(""+lastTwoDigits+"F.zip"))
             
              String temp = myStr.substring(4,myStr.length()-7);
                 System.out.println("temp : "+temp);
                 int releaseNum = Integer.parseInt(temp);
                 System.out.println("releaseNum : "+releaseNum);
                 if(releaseNum > fullFileRel)
                     fullFileRel = releaseNum;            
             

             if(myStr.endsWith(""+lastTwoDigits+"P.zip"))
             
                String temp = myStr.substring(4,myStr.length()-7);
                 System.out.println("temp : "+temp);
                 int releaseNum = Integer.parseInt(temp);
                 System.out.println("releaseNum : "+releaseNum);
                 if(releaseNum > fullFileRel)
                     partialFileRel = releaseNum;
                       
          
       

        System.out.println("full Rel :"+fullFileRel);
        System.out.println("partial Rel :"+partialFileRel);

       if(fullFileRel > partialFileRel)
           return "ALFP"+fullFileRel+""+lastTwoDigits+"F.zip";
       else
           return "ALFP"+partialFileRel+""+lastTwoDigits+"P.zip";
    

【讨论】:

【参考方案5】:

您可以使用正则表达式并执行以下操作来解析年份和版本:

public static void main(String[] args)

    int year = -1;
    int version = -1;
    String test = "ALFP716F.zip";
    if(test.matches("ALFP\\d+F.zip"))
    
        Pattern pattern = Pattern.compile("\\d+");
        Matcher matcher = pattern.matcher(test);
        matcher.find();
        String tempString = matcher.group(0);
        year = Integer.parseInt(tempString.substring((tempString.length() - 2)));
        version = Integer.parseInt(tempString.substring(0, (tempString.length() - 2)));
    
    System.out.println("Year: " + year + "    Version: " + version);


【讨论】:

【参考方案6】:

这是兼容 Java 1.5 的解决方案,AlphaNumComparator

【讨论】:

以上是关于对包含数字字符串的文件名数组进行排序的主要内容,如果未能解决你的问题,请参考以下文章

如何在iOS中对包含数字和名称的字符串数组进行排序

您可以按值对包含数字的字符串数组进行排序吗? [复制]

如何对包含数字的字符串列表进行数字排序?

Java:以数字方式对字符串数组进行排序

如何对表示数字(正数和负数以及小数部分)的字符串数组进行排序?

如何对R中元素包含字母和数字的字符向量进行排序?