Java: 将文件名拆分为基本名称和扩展名

97

有没有比这种方式更好的方法来获取文件的基本名称和扩展名:

File f = ...
String name = f.getName();
int dot = name.lastIndexOf('.');
String base = (dot == -1) ? name : name.substring(0, dot);
String extension = (dot == -1) ? "" : name.substring(dot+1);

8
请查看commons-io FilenameUtils。它具有getBaseName(..)getExtension(..)方法。 - Bozho
仅获取文件扩展名,请参阅 https://dev59.com/E3A65IYBdhLWcg3w5zHB。 - Andy Thomas
Bozho的评论中的页面已经移动,现在位于:https://commons.apache.org/proper/commons-io/apidocs/org/apache/commons/io/FilenameUtils.html - undefined
8个回答

179

我知道其他人已经提到了String.split,但这里有一个变体,仅产生两个标记(基础和扩展名):

String[] tokens = fileName.split("\\.(?=[^\\.]+$)");
例如:
"test.cool.awesome.txt".split("\\.(?=[^\\.]+$)");

产生:

["test.cool.awesome", "txt"]
正则表达式告诉Java在任何后面跟着任意数量的非句点字符,以及输入结尾的所有句点上进行拆分。只有一个符合此定义的句点(即最后一个句点)。 从技术上讲正则表达式方面而言,这种技巧被称为零宽度正向先行断言
顺便说一下,如果您想要拆分路径并获取包括但不限于扩展名在内的完整文件名,可以使用带有正斜杠的路径。
    String[] tokens = dir.split(".+?/(?=[^/]+$)");
例如:
    String dir = "/foo/bar/bam/boozled"; 
    String[] tokens = dir.split(".+?/(?=[^/]+$)");
    // [ "/foo/bar/bam/" "boozled" ] 

3
我不知道为什么人们害怕依赖关系;-) - Bozho
3
@Bozho: 我同意图书馆是解决这种问题的更好方案。这让其他人为您进行维护和思考(这就是我点赞你的答案的原因!)。这听起来可能很琐碎,但当我考虑包含Apache图书馆时,总有一部分我会犹豫不决,因为我过去曾经历过“JAR地狱”(我知道,这很琐碎)。 - Adam Paynter
4
@Bozho:Adam说得百分之百正确。这个问题并不足以让我另外再使用一个库,但如果我已经因其他原因在使用commons-io库,那么我会使用FilenameUtils。 - Jason S
1
失败,archive.tar.gz的正确扩展名应为.tar.gz - Has QUIT--Anony-Mousse
6
@Bozho - 讽刺?真正的问题是为什么Java自带无数堆看似可以轻松完成你想做的事情的冗余类,但实际上却总是让人感到沮丧,永远无法真正做到。Python没有类似于Apache-Commons的东西,因为Python已经内置了所有你需要的有用功能。C#似乎是另一个例子,你可以专注于解决你独特的问题,而不必费心去重新发明轮子或去获取别人发明的轮子。 - ArtOfWarfare
显示剩余8条评论

98

这是一个老问题,但我通常使用这个解决方案:

import org.apache.commons.io.FilenameUtils;

String fileName = "/abc/defg/file.txt";

String basename = FilenameUtils.getBaseName(fileName);
String extension = FilenameUtils.getExtension(fileName);
System.out.println(basename); // file
System.out.println(extension); // txt (NOT ".txt" !)

如果在Windows上工作且字符串“fileName”为“D:\ resources \ ftp_upload.csv”,则无法正常工作。你能帮忙吗? - NIKHIL CHAURASIA
3
@NIKHILCHAURASIA,您需要转义反斜杠,方法是将它们加倍。例如:"D:\resources\ftp_upload.csv"。 - Ricket

8

来源:http://www.java2s.com/Code/Java/File-Input-Output/Getextensionpathandfilename.htm

这是一个实用类:

class Filename {
  private String fullPath;
  private char pathSeparator, extensionSeparator;

  public Filename(String str, char sep, char ext) {
    fullPath = str;
    pathSeparator = sep;
    extensionSeparator = ext;
  }

  public String extension() {
    int dot = fullPath.lastIndexOf(extensionSeparator);
    return fullPath.substring(dot + 1);
  }

  public String filename() { // gets filename without extension
    int dot = fullPath.lastIndexOf(extensionSeparator);
    int sep = fullPath.lastIndexOf(pathSeparator);
    return fullPath.substring(sep + 1, dot);
  }

  public String path() {
    int sep = fullPath.lastIndexOf(pathSeparator);
    return fullPath.substring(0, sep);
  }
}

使用方法:

public class FilenameDemo {
  public static void main(String[] args) {
    final String FPATH = "/home/mem/index.html";
    Filename myHomePage = new Filename(FPATH, '/', '.');
    System.out.println("Extension = " + myHomePage.extension());
    System.out.println("Filename = " + myHomePage.filename());
    System.out.println("Path = " + myHomePage.path());
  }
}

4
"basename()"比"filename()"更合适作为名称。 - nimcap
如果没有扩展名(例如类似于“/etc/hosts”的文件名),则将返回“hosts”作为扩展名(而不是“”)。库级别的实用程序类应处理角落情况。 - Zach-M

8

7
Java.io.File.getName()方法返回带有文件扩展名的文件名。 - Bram
2
我倾向于认为没有所谓的“扩展”这样的东西 :-) - user933161

2

文件扩展名是一个破碎的概念

而且不存在可靠的函数。例如,考虑以下文件名:

archive.tar.gz

“扩展名”是什么?DOS用户可能更喜欢名称为“archive.tgz”的文件。有时你会看到一些愚蠢的Windows应用程序,它们首先解压文件(得到一个“.tar”文件),然后你必须再次打开才能查看存档内容。
在这种情况下,“.tar.gz”可能是一个更合理的文件扩展名。还有一些其他的文件扩展名,如“.tar.bz2”、“.tar.xz”、“.tar.lz”和“.tar.lzma”。但是,“你如何决定是否在最后一个点或倒数第二个点处拆分呢?”
“使用mime类型代替。”
Java 7函数Files.probeContentType比信任文件扩展名更可靠地检测文件类型。几乎所有Unix/Linux世界以及您的Web浏览器和智能手机都是这样做的。

13
这个回答如何解决问题?无论是File还是Path都不能让我分离出扩展名。 - Andreas Abel
1
@andreas.abel 让我再重复一遍:文件扩展名是一个有缺陷的概念。它们不可靠,也没有明确定义,除了在DOS 8+3文件名上(考虑到在Unix上.tar.gz.tgz非常常见)。使用MIME类型代替。 - Has QUIT--Anony-Mousse
4
好的,我会尽力进行翻译。内容如下:@Anony-Mousse 嗯,我原则上同意,但我与之交互的99.999%的系统使用文件名而不是MIME类型。 - Christian Sauer
1
在使用Files.probeContentType而不是依赖于文件名具有正确扩展名的情况下,问题出在哪里? - Has QUIT--Anony-Mousse
8
这并没有回答问题。我有一个使用情景,文件名是电影的名称+扩展名。如何使用 MIME 类型来提取名称? - Niek
显示剩余2条评论

2
你的代码有什么问题?如果包装在一个整洁的实用方法中,它就没问题了。
更重要的是使用什么作为分隔符 - 第一个或最后一个点。第一个对于文件名如“setup-2.5.1.exe”不好,最后一个对于具有多个扩展名的文件名如“mybundle.tar.gz”也不好。

-1

-4

也许你可以使用String#split

回答你的评论:

我不确定文件名中是否可以有多个“.”,但无论如何,即使有更多的点,你也可以使用split。例如考虑以下情况:

String input = "boo.and.foo";

String[] result = input.split(".");

这将返回一个包含以下内容的数组:
{ "boo", "and", "foo" }

所以你会知道数组中的最后一个索引是扩展名,而其他所有索引都是基础。


好的,但我需要找出字符串中最后一个 . 的正则表达式。 - Jason S
1
嗯,我不确定,但是你不能只使用“。”吗?或者文件名中有多个点吗? - anon
2
我认为这会起作用:fileName.split("\\.(?=[^\\.]+$)") - Adam Paynter
1
你不能假设只有一个点。Adam: 谢谢,我会尝试的。 - Jason S
4
这个答案是不正确的。因为点号没有被转义,它会返回一个空数组。 - aled
显示剩余2条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接