如何在Java中获取文件的扩展名?

568

只是为了明确,我不是在寻找MIME类型。

假设我有以下输入:/path/to/file/foo.txt

我想要一种方法来分解这个输入,特别是将 .txt 作为扩展名。在Java中是否有任何内置的方式可以做到这一点?我想避免编写自己的解析器。


15
你永远不知道什么时候会出现一些将扩展名用逗号隔开的新平台,这意味着你需要编写特定于该平台的代码。Java框架应该更具前瞻性,并为获取扩展名提供API,他们编写与特定平台相关的代码,而你作为API的用户只需调用“获取扩展名”的函数即可。 - ArtOfWarfare
@ArtOfWarfare:哇塞。让我们创建一个有成千上万个类的100MB JRE,但请务必不要实现任何从“filename.txt”返回“txt”的方法,因为某些平台可能想要使用“filename,txt”。 - Eric Duminil
@EricDuminil “一定不要实现任何从“filename.txt”返回“txt”的方法”???尝试path.substring(path.lastIndexOf("."));.....是的...他们肯定不会为了什么事情而重复劳动... - VelocityPulse
@VelocityPulse 这正是我困扰的问题。由于没有标准的获取文件扩展名的方法,你会得到许多半正确的答案和稍微不同的实现。你的代码使用了2种方法(我希望只有一个明确的方法),它从“filename.txt”返回“.txt”,这可能不是期望的结果,最糟糕的是,如果没有扩展名,它会抛出StringIndexOutOfBoundsException而不是返回空字符串。 - Eric Duminil
最后,自Java 20起,JDK中提供了一个新的方法Path#getExtension:https://dev59.com/E3A65IYBdhLWcg3w5zHB#74315488 - Nikolas Charalambidis
33个回答

742

在这种情况下,使用Apache Commons IO中的FilenameUtils.getExtension方法。以下是如何使用它的示例(您可以指定完整路径或仅文件名):

import org.apache.commons.io.FilenameUtils;

// ...

String ext1 = FilenameUtils.getExtension("/path/to/file/foo.txt"); // returns "txt"
String ext2 = FilenameUtils.getExtension("bar.exe"); // returns "exe"

Maven依赖:

<dependency>
  <groupId>commons-io</groupId>
  <artifactId>commons-io</artifactId>
  <version>2.6</version>
</dependency>
Gradle Groovy DSL
implementation 'commons-io:commons-io:2.6'
Gradle Kotlin DSL
implementation("commons-io:commons-io:2.6")

其他 https://search.maven.org/artifact/commons-io/commons-io/2.6/jar


75
需要注意的是,对于名为archive.tar.gz的文件,它仅返回"gz"。 - Zitrax
120
这是因为"gz"是文件扩展名。 - BrainSlugs83
33
@zhelon ".gz" 代表gnu压缩文件,".tar" 代表(t)ape (ar)chive。因此,".tar.gz" 是一个放在 gnu 压缩文件中的 tar 文件,后缀名为 ".gz"。 - cirovladimir
2
@guru_001 不,当然不是,只是提一下你可以使用完整路径或者仅文件名来调用它。 - Scadge
1
@Zitrax 一个文件不能有多个扩展名或包含点的扩展名,所以在你的情况下,扩展名是 .gz。 - user25
显示剩余2条评论

371

你真的需要一个“解析器”来做这件事吗?

String extension = "";

int i = fileName.lastIndexOf('.');
if (i > 0) {
    extension = fileName.substring(i+1);
}

假设你正在处理类似于Windows的简单文件名,而不是像archive.tar.gz这样的名称。

顺便说一句,如果一个目录可能有一个'.',但文件名本身没有(例如/path/to.a/file),你可以这样做:

String extension = "";

int i = fileName.lastIndexOf('.');
int p = Math.max(fileName.lastIndexOf('/'), fileName.lastIndexOf('\\'));

if (i > p) {
    extension = fileName.substring(i+1);
}

4
谢谢!如果你想进行更多的操作而不仅仅是扩展名,那么你可能需要一个解析器/对象...比如说,如果你只想要路径、父目录、文件名(不包括扩展名)等内容。我来自C#和.Net,我们有这个:http://msdn.microsoft.com/en-us/library/system.io.fileinfo_members.aspx - longda
14
像你所说的,仅仅使用naive的lastIndexOf(".")并不足够,需要考虑许多其他因素。我猜测Apache Commons库中有相应的方法可以解决所有这些棘手的潜在问题。 - Tyler
13
我认为i > 0应该改为i >= 0i != -1。这可以处理像.htaccess这样的文件名。 - Pijusn
14
无论代码片段有多简单,仍然需要更新、维护、测试并将其作为方便的依赖项提供。如果已经有一个库来完成这些工作,那么就容易得多了。 - Don Cheadle
2
另一个需要注意的是,如果文件以点结尾。最好放在库中。 如果 (i > p && i < (fileName.length()-1)) { extension = fileName.substring(i+1); } - tgkprog
显示剩余6条评论

111
private String getFileExtension(File file) {
    String name = file.getName();
    int lastIndexOf = name.lastIndexOf(".");
    if (lastIndexOf == -1) {
        return ""; // empty extension
    }
    return name.substring(lastIndexOf);
}

14
需要注意的是,这个方法也会返回文件名中的“.”符号,因此你的文件扩展名将会是“.txt”,而不是其他回答中的“txt”。 - NickEntin
16
这种方法可能在某些情况下不起作用,例如 /usr/bin/foo.bar/httpconf。 - Iman Akbari
8
  1. 数以百计的Linux软件包会创建名为“init.d”等名称的目录,此外依赖路径中没有带有点的目录并不安全,因为这是不违法的。
  2. 我曾为Android编写代码,所以我使用了一些SDK方法,但我记不清楚了。不过我猜https://dev59.com/E3A65IYBdhLWcg3w5zHB#3571239没有这个缺陷。
- Iman Akbari
6
getName() 只返回文件名本身,你的例子中应该是 "httpconf"。 - Dreamspace President
2
你不应该依赖异常来防范粗糙的编码。这里不需要异常处理。 - intrepidis
显示剩余3条评论

88
如果您使用 Guava 库,您可以使用 Files 实用类。它有一个特定的方法,getFileExtension()。例如:
String path = "c:/path/to/file/foo.txt";
String ext = Files.getFileExtension(path);
System.out.println(ext); //prints txt

此外,您还可以使用类似的函数 getNameWithoutExtension() 来获取文件名(不包括扩展名):

String filename = Files.getNameWithoutExtension(path);
System.out.println(filename); //prints foo

4
真的吗?这是一家很棒的图书馆,里面包含了许多实用工具。其中大部分将成为Java8的一部分,例如伟大的Guava Function - JeanValjean
不是所有人都能决定使用哪些库,不幸的是。至少我们有Apache Commons,尽管它有点老了。 - Lluis Martinez
1
如果您查看getFileExtension的源代码,实际上只是int dotIndex = fileName.lastIndexOf('.'); return (dotIndex == -1) ? "" : fileName.substring(dotIndex + 1),所以没什么大不了的。此外,请注意Files由于某些原因被标记为“不稳定”。 - Al-Mothafar
1
@Al-Mothafar 许多类被标记为不稳定(请参见multimap builders),我也不明白为什么:已经发布了几个版本,但在那里没有任何更改。 - JeanValjean

27

如果使用Android,您可以使用以下方式:

String ext = android.webkit.MimeTypeMap.getFileExtensionFromUrl(file.getName());

请注意,如果字符串未编码(例如包含空格或中文字符),则此方法将无法正常工作,请参阅:https://dev59.com/DWYq5IYBdhLWcg3wpiKk#14321470。 - 林果皞
它除了英语之外没有获得扩展。 - Ahmad

19

这是一种经过测试的方法

public static String getExtension(String fileName) {
    char ch;
    int len;
    if(fileName==null || 
            (len = fileName.length())==0 || 
            (ch = fileName.charAt(len-1))=='/' || ch=='\\' || //in the case of a directory
             ch=='.' ) //in the case of . or ..
        return "";
    int dotInd = fileName.lastIndexOf('.'),
        sepInd = Math.max(fileName.lastIndexOf('/'), fileName.lastIndexOf('\\'));
    if( dotInd<=sepInd )
        return "";
    else
        return fileName.substring(dotInd+1).toLowerCase();
}

测试用例:

@Test
public void testGetExtension() {
    assertEquals("", getExtension("C"));
    assertEquals("ext", getExtension("C.ext"));
    assertEquals("ext", getExtension("A/B/C.ext"));
    assertEquals("", getExtension("A/B/C.ext/"));
    assertEquals("", getExtension("A/B/C.ext/.."));
    assertEquals("bin", getExtension("A/B/C.bin"));
    assertEquals("hidden", getExtension(".hidden"));
    assertEquals("dsstore", getExtension("/user/home/.dsstore"));
    assertEquals("", getExtension(".strange."));
    assertEquals("3", getExtension("1.2.3"));
    assertEquals("exe", getExtension("C:\\Program Files (x86)\\java\\bin\\javaw.exe"));
}

18
如果您在项目中使用Spring框架,那么您可以使用StringUtils
import org.springframework.util.StringUtils;

StringUtils.getFilenameExtension("YourFileName")

16
String path = "/Users/test/test.txt";
String extension = "";

if (path.contains("."))
     extension = path.substring(path.lastIndexOf("."));

返回 ".txt"

如果您想要只获取 "txt",则将 path.lastIndexOf(".") + 1


1
这是为数不多的几个答案之一,消除了对返回内容的困惑。 - riddle_me_this

16
为了考虑到点号之前没有字符的文件名,你需要使用已接受答案的微小变化版本:

String extension = "";

int i = fileName.lastIndexOf('.');
if (i >= 0) {
    extension = fileName.substring(i+1);
}

"file.doc" => "doc"
"file.doc.gz" => "gz"
".doc" => "doc"

可能应该防范自己免受“foo”输入的攻击。 - chrisinmtown

12

使用String.replaceAll的时候,我的代码变得又脏又短:

.replaceAll("^.*\\.(.*)$", "$1")
注意第一个*是贪婪的,因此它会尽可能多地获取字符,然后只剩下最后的句点和文件扩展名。

如果文件没有扩展名,这将失败。 - Zack
是的,不幸的是,仍然可以用于简单的场景,比如快速文件类型检测等,因为拥有错误的扩展名与没有扩展名并没有太大的区别,或者可以在替换结果等于输入的情况下放置一个if条件。 - Ebrahim Byagowi
2
甚至更短的写法是.replaceAll(".*\\.", "") - Ebrahim Byagowi

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