【问题标题】:Transparent and OS-agnostic path handling in JavaJava 中的透明和与操作系统无关的路径处理
【发布时间】:2012-06-06 02:50:19
【问题描述】:

我正在为我们的应用程序开发一个图形安装程序。由于没有可用的安装程序生成器满足要求和限制,我正在从头开始构建它。

安装程序应该在多个操作系统上运行,因此路径处理需要与操作系统无关。为此,我编写了以下小实用程序:

public class Path {
  private Path() {
  }

  public static String join(String... pathElements) {
    return ListEnhancer.wrap(Arrays.asList(pathElements)).
      mkString(File.separator);
  }

  public static String concatOsSpecific(String path, String element) {
    return path + File.separator + element;
  }

  public static String concatOsAgnostic(String path, String element) {
    return path + "/" + element;
  }

  public static String makeOsAgnostic(String path) {
    return path.replace(File.separator, "/");
  }

  public static String makeOsSpecific(String path) {
    return new File(path).getAbsolutePath();
  }

  public static String fileName(String path) {
    return new File(path).getName();
  }
}

现在我的代码在很多地方都充斥着Path.*AgnosticPath.*Specific 调用。很明显,这很容易出错,而且根本不透明。

我应该采取什么方法来使路径处理透明且不易出错?是否存在已经解决此问题的任何实用程序/库?任何帮助将不胜感激。

编辑:

为了举例说明我的意思,这是我不久前写的一些代码。 (题外话:原谅冗长的方法。代码处于初始阶段,很快将进行一些重度重构。)

一些上下文:ApplicationContext 是一个存储安装数据的对象。这包括多个路径,例如installationRootDirectoryinstallationDirectory 等。这些路径的默认值是在创建安装程序时指定的,因此始终以与操作系统无关的格式存储。

@Override
protected void initializeComponents() {
  super.initializeComponents();
  choosePathLabel = new JLabel("Please select the installation path:");
  final ApplicationContext c = installer.getAppContext();
  pathTextField = new JTextField(
    Path.makeOsSpecific(c.getInstallationDirectory()));
  browseButton = new JButton("Browse", 
    new ImageIcon("resources/images/browse.png"));
  browseButton.addActionListener(new ActionListener() {
    @Override
    public void actionPerformed(ActionEvent e) {
      JFileChooser fileChooser = new JFileChooser();
      fileChooser.setFileSelectionMode(JFileChooser.DIRECTORIES_ONLY);
      fileChooser.setAcceptAllFileFilterUsed(false);
      int choice = fileChooser.showOpenDialog(installer);
      String selectedInstallationRootDir = fileChooser.getSelectedFile().
        getPath();
      if (choice == JFileChooser.APPROVE_OPTION) {
        c.setInstallationRootDirectory(
          Path.makeOsAgnostic(selectedInstallationRootDir));
        pathTextField.setText(Path.makeOsSpecific(c.getInstallationDirectory()));
      }
    }
  });
}

【问题讨论】:

  • / 分隔路径在Windows 上运行得非常好。除此之外,您为什么还需要不可知论版本?
  • @Mat,我知道。以下是一些情况: 1. File#getAbsolutePath 返回一个带有操作系统特定分隔符的字符串。 2. System.getProperty(<something that returns a path>) 返回一个带有操作系统特定分隔符的字符串。 3. 我需要读取和写入某些安装面板中的文本框,并且在呈现给用户时,路径需要采用特定于操作系统的格式。
  • 当然。那么 *Agnostic 的用途是什么?
  • @Mat,让我在我的问题中添加一个代码示例。
  • 是否需要显示相同的分隔符? (例如,对所有操作系统使用 ``)。如果没有,只需使用一个小实用程序来确定不支持的分隔符,并在写入 FS 之前将其替换。这样你就不会强迫用户做任何“特定于操作系统”的事情

标签: java cross-platform file-handling


【解决方案1】:

或者您可以引入 2 个新课程:

class OsSpecificPath implements FilePathInterface
{
      String path;

      OsAgnosticPath toAgnosticPath();

      OsSpecificPath concat( OsSpecificPath otherPath );

      // from IFilePath
      getFile();

     ... etc
}

class OsAgnosticPath implements FilePathInterface
{
      String path;

      OsSpecificPath toOsSpecificPath();

      OsAgnosticPath concat( OsAgnosticPath otherPath );

      // from IFilePath
      getFile();

     ... etc
}

每个人都根据需要包装一条路径。

然后,每个方法都可以具有转换为其他类型路径的方法,但不是所有内容都是字符串并且可能被滥用的“字符串类型”解决方案,而是有 2 个强类型类,不能被错误地传递。

任何不关心路径类型的东西都会使用FilePathInterface,任何需要对特定类型的路径进行操作的东西都会专门使用这些类型。如果真的有必要,FilePathInterface 可以假设在界面中同时包含 toAgnosticPathtoOsSpecificPath...

【讨论】:

  • +1 表示不易出错。然而,转换仍然是手动的,代码也会乱七八糟。
  • 不过,它们可能不需要是手动的。两个类都可以实现一个接口,比如IFilePath 之类的,然后每个类都知道如何根据自己的路径样式创建文件对象。然后,您只需在各处传递 IFilePath 对象,并将所有这些转换隐藏在类本身中。是的,在某些时候,比如在文本框中显示它们,您可能需要进行一些手动转换,但在其他任何地方您不应该...
  • 是的,这是有道理的。谢谢!
  • 将您的答案标记为正确。旁注:这是一个 Java 问题,因此请遵循 Java 方法和类型名称的约定。 (方法名应该小写驼峰式,Java 接口不遵循I- 约定。)
  • 再次感谢您的回答。我按照您建议的想法重新设计了我的 API,并在我的代码中发现了一些我可能会错过的错误。
【解决方案2】:

不确定这是否是您想要的,但通常当我需要在独立于操作系统的 Java 程序中执行与路径相关的操作时,我总是使用字符串而不是文件来传递路径,而且我总是这样做以下两件事:

每当我构建字符串路径时,我总是使用/ 作为文件分隔符

每当我使用字符串路径创建文件或将其保存为文本时,我总是在使用路径之前进行以下调用:

String fSep = System.getProperty("file.separator);
String path = ... //might be built from scratch, might be passed in from somewhere
path = path.replace("/",fSep).replace("\\",fSep);

无论路径是在本地机器上构建,还是从网络上具有不同操作系统的不同机器传入,这似乎都能正常工作,前提是我打算使用本地机器上的路径。如果您打算通过网络在不同操作系统之间传递路径,请注意您自己的代码是一致的。

编辑

哇...不知怎的,我的回答被弄乱了,代码格式没有按最初的预期工作...

【讨论】:

  • 我也替换了,就在写入文件系统之前。
【解决方案3】:

您永远不需要将 back 转换为 os-agnostic。以下是特定于操作系统的转换:

public class Path {
  private Path() {
  }

  public static String concat(String path, String element) {
    return new File(path, element).getPath();
  }

  public static String makeOsSpecific(String path) {
    return new File(path).getAbsolutePath();
  }

  public static String fileName(String path) {
    return new File(path).getName();
  }
}

您的样本:

@Override
protected void initializeComponents() {
  super.initializeComponents();
  choosePathLabel = new JLabel("Please select the installation path:");
  final ApplicationContext c = installer.getAppContext();
  pathTextField = new JTextField(
    Path.makeOsSpecific(c.getInstallationDirectory()));
  browseButton = new JButton("Browse", 
    new ImageIcon("resources/images/browse.png"));
  browseButton.addActionListener(new ActionListener() {
    @Override
    public void actionPerformed(ActionEvent e) {
      JFileChooser fileChooser = new JFileChooser();
      fileChooser.setFileSelectionMode(JFileChooser.DIRECTORIES_ONLY);
      fileChooser.setAcceptAllFileFilterUsed(false);
      int choice = fileChooser.showOpenDialog(installer);
      String selectedInstallationRootDir = fileChooser.getSelectedFile().
        getPath();
      if (choice == JFileChooser.APPROVE_OPTION) {
        c.setInstallationRootDirectory(selectedInstallationRootDir);
        pathTextField.setText(Path.makeOsSpecific(c.getInstallationDirectory()));
      }
    }
  });
}

【讨论】:

  • 您的示例中有错误。 File#getPath 方法返回特定于操作系统的路径字符串,而我需要将路径以与操作系统无关的形式存储在 ApplicationContext 中。所以c.setInstallationDirectory(selectedInstallationRootDir) 是错误的。
  • @missingfaktor - 为什么您需要以与操作系统无关的格式存储它们?这是我一直在问的问题,你还没有回答。
  • 我已经在这个线程的一个 cmets 中回答了这个问题。不管怎样,我们又来了:Installer 对象是使用我正在设计的这个 API 创建的。这些对象在安装过程中需要指定多个路径,路径以字符串形式存储。 Installer 对象将只被写入一次,并且无论操作系统如何都需要正常工作。现在,如果我以特定于某些操作系统的格式存储这些路径,那会产生问题,不是吗?
  • @missingfaktor - 我对您所包含的 ui 代码的理解是,它是运行安装程序时向用户显示的安装程序的一部分,这不正确吗?在这种情况下,您永远不需要与操作系统无关的路径,只需要当前操作系统的特定于操作系统的路径。我错过了什么?
  • @missingfaktor - 解释的问题在于您有很多上下文,这使得您提供的少量信息对您有意义。不幸的是,这个小信息窗口对外人来说没有意义。很多时候人们试图解决错误的问题。我认为这里就是这种情况,不幸的是,我似乎无法找到根本问题,所以我无法帮助您解决它。根本问题是“为什么需要与操作系统无关的路径”(安装程序的初始状态除外)。开始安装过程后,您应该只需要特定于操作系统的路径。
【解决方案4】:

我会创建我自己的 MyFile 对象来扩展或包装 java.util.File。然后确保所有代码都使用这个对象而不是 java.io.File。在这里,您将进行操作系统检查并调用方法来清理文件名。您的其余代码将是“干净的”。

【讨论】:

  • 我假设他的意思是 java.io.File。
猜你喜欢
  • 2018-10-29
  • 1970-01-01
  • 2020-04-10
  • 1970-01-01
  • 2013-08-22
  • 1970-01-01
  • 1970-01-01
  • 2014-10-23
  • 2015-07-16
相关资源
最近更新 更多