【问题标题】:Java file equalsJava文件等于
【发布时间】:2023-04-03 15:05:01
【问题描述】:

我不了解你们,但至少我预计 f1 将等于 f2 在下面的代码中,但显然情况并非如此!您对此有何看法?看来我得自己写个equals方法来支持了,对吧?

import java.io.*;

public class FileEquals
{
    public static void main(String[] args)
    {
        File f1 = new File("./hello.txt");
        File f2 = new File("hello.txt");
        System.out.println("f1: " + f1.getName());
        System.out.println("f2: " + f2.getName());
        System.out.println("f1.equals(f2) returns " + f1.equals(f2));
        System.out.println("f1.compareTo(f2) returns " + f1.compareTo(f2));
    }
}

【问题讨论】:

  • Java 7 的 Path 类也是如此。但是存在 Path.normalize() 或 Files.isSameFile() 等方法
  • 您可以通过显示实际输出来保护这个问题的所有查看者一段时间。我期待equalscompareTo 的结果相互矛盾。情况并非如此,equals 返回 false,compareTo 返回 -58,在字典上表示“小于”。 @Luciano:请注意,Files.isSameFile 在这种情况下会尝试打开文件,因为路径不相等并且可能会因NoSuchFileException 而失败。

标签: java file compare equals


【解决方案1】:

不,事实并非如此。因为 equals 正在比较绝对路径的相等性(在您上面的情况下,它类似于:

some-project\.\hello.txt
some-project\hello.txt

所以它们自然是不同的。

看来我必须编写自己的equals方法来支持它, 对吧?

可能是的。但首先,您必须知道要比较什么?只有路径名?如果是,请以这种方式比较其规范路径:

f1.getCanonicalPath().equals(f2.getCanonicalPath())

但如果您想比较两个不同文件的内容,那么是的,您应该编写自己的方法 - 或者干脆从互联网上的某个地方复制。

【讨论】:

  • 我其实想做类似“fileList.contains(file)”的东西,这个方法调用equals方法。
  • 这个答案让我感到困惑。查看 jdk 中 UnixFileSystem.java 中的源代码: public int compare(File f1, File f2) { return f1.getPath().compareTo(f2.getPath()); }@G.Demecki 我不同意:equals 是比较绝对路径的相等性
【解决方案2】:

要正确测试equals,您必须调用getCanonicalFile()。例如

public static void main(String[] args) throws IOException
   {
       File f1 = new File("./hello.txt").getCanonicalFile();
       File f2 = new File("hello.txt").getCanonicalFile();
       System.out.println("f1: " + f1.getAbsolutePath());
       System.out.println("f2: " + f2.getAbsolutePath());
       System.out.println("f1.equals(f2) returns " + f1.equals(f2));
       System.out.println("f1.compareTo(f2) returns " + f1.compareTo(f2));
   }

将返回 true 等于。请注意,getCanonicalFile 可能会抛出 IOException,因此我将其添加到方法签名中。

【讨论】:

    【解决方案3】:

    如果你只想比较每个文件的内容,你可以像这样将内容读入一个字节数组:

    byte[] f1 = Files.readAllBytes(file1);
    byte[] f2 = Files.readAllBytes(file2);
    

    然后从那里准确比较你想要的。

    请注意,此方法调用仅存在于 Java 7 中。对于旧版本,Guava 和 Apache 有类似的方法,但名称和细节不同。

    编辑:或者更好的选择(尤其是在比较大文件时)可能是简单地逐字节比较,而不是将整个文件加载到内存中,如下所示:

    FileInputStream f1 = new FileInputStream(file1);
    DataInputStream d1 = new DataInputStream(f1);
    FileInputStream f2 = new FileInputStream(file2);
    DataInputStream d2 = new DataInputStream(f2);
    
    byte b1 = d1.readByte();
    byte b2 = d2.readByte();
    

    然后从那里比较。

    【讨论】:

    • +1 好帖子 - 我今天学到了一些东西(我还没有使用 Java 7,很高兴看到他们添加了文件实用程序)
    • 如果有的话,我会先比较文件的大小。
    • 比较这样的文件是一个非常糟糕的主意
    • @unbeli 请详细说明。我在很多单元测试中使用过类似的代码,其中一个文件包含正确的结果,一个文件包含程序/算法生成的结果。这不是 OP 想要做的(正如他后来阐述的那样),但 Brian 说 CONTENTS,他甚至将其大写。
    • @user949300 感谢您对我的帖子的补充
    【解决方案4】:

    我发现比较两个文件的更快方法如下。

    这只是解决它的建议。

    不确定性能(如果每个文件有 10 GB 怎么办?)

        File file = new File("/tmp/file.txt");
        File secondFile = new File("/tmp/secondFile.txt");
    
        // Bytes diff
        byte[] b1 = Files.readAllBytes(file.toPath());
        byte[] b2 = Files.readAllBytes(secondFile.toPath());
    
        boolean equals = Arrays.equals(b1, b2);
    
        System.out.println("the same? " + equals);
    
        // List Diff
        List<String> c1 = Files.readAllLines(file.toPath());
        List<String> c2 = Files.readAllLines(secondFile.toPath());
    
        boolean containsAll = c1.containsAll(c2);
        System.out.println("the same? " + containsAll);                
    }
    

    编辑

    但是,unix 系统上的 diff 实用程序会更快更详细。 取决于你需要比较什么。

    【讨论】:

      【解决方案5】:

      以下是这两种方法的实现:

      /**
       * Tests this abstract pathname for equality with the given object.
       * Returns <code>true</code> if and only if the argument is not
       * <code>null</code> and is an abstract pathname that denotes the same file
       * or directory as this abstract pathname.  Whether or not two abstract
       * pathnames are equal depends upon the underlying system.  On UNIX
       * systems, alphabetic case is significant in comparing pathnames; on Microsoft Windows
       * systems it is not.
       *
       * @param   obj   The object to be compared with this abstract pathname
       *
       * @return  <code>true</code> if and only if the objects are the same;
       *          <code>false</code> otherwise
       */
      public boolean equals(Object obj) {
          if ((obj != null) && (obj instanceof File)) {
              return compareTo((File)obj) == 0;
          }
          return false;
      }
      
      /**
       * Compares two abstract pathnames lexicographically.  The ordering
       * defined by this method depends upon the underlying system.  On UNIX
       * systems, alphabetic case is significant in comparing pathnames; on Microsoft Windows
       * systems it is not.
       *
       * @param   pathname  The abstract pathname to be compared to this abstract
       *                    pathname
       *
       * @return  Zero if the argument is equal to this abstract pathname, a
       *          value less than zero if this abstract pathname is
       *          lexicographically less than the argument, or a value greater
       *          than zero if this abstract pathname is lexicographically
       *          greater than the argument
       *
       * @since   1.2
       */
      public int compareTo(File pathname) {
          return fs.compare(this, pathname);
      }
      

      【讨论】:

        【解决方案6】:

        如果您只想根据路径检查文件是否相同,请使用

        java.nio.file.Files#isSameFile

        例如

        Assert.assertTrue(Files.isSameFile(
             new File("some-project\.\hello.txt").toPath(),
             new File("some-project\hello.txt").toPath()
        ));
        

        【讨论】:

          【解决方案7】:

          如果您使用的是 Windows,请参阅课程 Win32FileSystem

          比较方法如下,所以你的文件对象不同是很正常的。

              public int compare(File f1, File f2) {
                return f1.getPath().compareToIgnoreCase(f2.getPath());
              }
          

          也将这些行添加到您的代码中

                  System.out.println(f1.getPath());
                  System.out.println(f2.getPath());
          

          它会打印出来

          .\hello.txt
          hello.txt
          

          因此它们不相等,因为使用 File 对象的路径属性进行比较

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 2014-03-27
            • 2020-11-25
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2012-02-02
            • 2018-12-16
            • 2012-11-24
            相关资源
            最近更新 更多