Как получить расширение файла в Java?


484

Просто чтобы прояснить, я не ищу тип MIME.

Допустим, у меня есть следующий вход: /path/to/file/foo.txt

Я хотел бы способ разбить этот вход, в частности, .txtдля расширения. Есть ли способ сделать это в Java? Я хотел бы избежать написания моего собственного парсера.


12
Вы никогда не знаете, когда появится какая-то новая платформа, которая определяет расширения как разделенные запятой. Теперь вам нужно написать зависимый от платформы код. Фреймворки Java должны быть более дальновидными и иметь API для получения расширений, где они пишут зависимый от платформы код, а вы, как пользователь API, просто скажете, получите расширение.
ArtOfWarfare

@ArtOfWarfare: OMG. Давайте создадим 100MB JRE со многими тысячами классов , но , пожалуйста , убедитесь , что не осуществлять какой - либо метод , который возвращается "txt"из , "filename.txt"потому что некоторые платформы где - то , возможно , захотите использовать "filename,txt".
Эрик

@EricDuminil "Убедитесь, что не реализован какой-либо метод, который возвращает" txt "из" filename.txt "" ??? Попробуй path.substring(path.lastIndexOf("."));..... И да .. Они точно не будут ничего дублировать ...
VelocityPulse

@VelocityPulse Это именно то , что беспокоит меня. Поскольку не существует стандартного способа получить расширение файла, вы получите десятки полу неправильных ответов и несколько разных реализаций. Ваш код использует 2 метод (я хотел бы иметь один единственный, явный метод), она возвращает ".txt"из "filename.txt", который не может быть желаемым результатом, и хуже всего, он терпит неудачу с StringIndexOutOfBoundsExceptionвместо того , чтобы возвратить пустую строку , если нет расширения.
Эрик Думинил

Ответы:


649

В этом случае используйте FilenameUtils.getExtension от Apache Commons IO

Вот пример того, как его использовать (вы можете указать либо полный путь, либо просто имя файла):

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


70
Следует отметить, что он возвращает только «gz» для файла с именем archive.tar.gz.
Цитракс

106
@Zitrax потому что "gz" - это расширение файла.
BrainSlugs83

6
@ BrainSlugs83 Так что же означает «смола»?
TuGordoBello

31
@zhelon .gz расшифровывается как сжатый файл gnu, а .tar обозначает (t) ape (ar) зубок чеснока. Итак, .tar.gz - это tar-файл внутри zn-файла gnu с расширением .gz.
cirovladimir

5
Для этой простой задачи нет причин использовать другую библиотеку.
masterwok

311

Вам действительно нужен "парсер" для этого?

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
Спасибо! Конечно, вам может понадобиться парсер / объект для этого, если вы хотите сделать больше манипуляций, чем просто расширение ... скажем, если вам нужен только путь, родительский каталог, имя файла (без расширения) и т. Д. m приходит из C # и .Net, где мы имеем это: msdn.microsoft.com/en-us/library/…
longda

10
Как вы говорите, есть несколько вещей, о которых нужно подумать, помимо простого использования наивного lastIndexOf ("."). Я предполагаю, что у Apache Commons есть метод для этого, который учитывает все маленькие хитрые потенциальные проблемы.
MatrixFrog

12
Я думаю, что i > 0следует изменить на i >= 0или i != -1. Это заботится о именах файлов, как .htaccess.
Пиюсн

8
независимо от того, насколько прост любой фрагмент кода ... вам все равно нужно обновить его / поддерживать его / протестировать / сделать его доступным в качестве удобной зависимости ... гораздо проще, если бы все это уже выполнялось библиотекой
Don Cheadle

2
Еще одна проблема, если файл заканчивается точкой. Лучше в библиотеке. if (i> p && i <(fileName.length () - 1)) {extension = fileName.substring (i + 1);
tgkprog

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

13
Следует отметить, что это возвращает «.» Кроме того, ваше расширение файла будет «.txt», а не «txt» в некоторых других ответах
NickEntin

2
Лучший ответ и @NickEntin Лучший комментарий. Удалить период "." из расширения файла, может быть закодирован как int lastIndexOf = name.lastIndexOf (".") + 1;
Ханзалла Афган

11
этот подход может не сработать в некоторых случаях, например /usr/bin/foo.bar/httpconf
Иман Акбари

8
@ lukasz1985 1. сотни пакетов linux создают каталоги с такими именами, как «init.d», более того, полагаться на путь, не имея каталогов с точками, небезопасно, так как это недопустимо 2. Я программировал для Android, поэтому использовал какой-то SDK метод, который я не помню, но я предполагаю, что stackoverflow.com/a/3571239/2546146 не имеет этого недостатка
Иман Акбари

6
@Iman Akbari: getName () возвращает только само имя файла, которое в вашем примере будет «httpconf».
Президент Dreamspace

85

Если вы используете библиотеку 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 .
Жан Вальжан,

К сожалению, не все люди могут решить, какие библиотеки использовать. По крайней мере, у нас есть Apache Commons, хотя и старый.
Луис Мартинес

1
если вы видите исходный код на getFileExtensionсамом деле, это не int dotIndex = fileName.lastIndexOf('.'); return (dotIndex == -1) ? "" : fileName.substring(dotIndex + 1)так уж сложно. также обратите внимание, что Filesпомечен как «нестабильный» по некоторым причинам.
Аль-Мотафар

@ Аль-Мотафар, многие классы помечены как нестабильные (см. Многокартовые компоновщики), я также не понимаю, почему: выпущено несколько выпущенных версий, но там ничего не изменилось.
Жан Вальжан

27

Если на Android, вы можете использовать это:

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

Обратите внимание, что это не будет работать, если строка не закодирована (например, содержит пробел или китайский символ), см. Stackoverflow.com/a/14321470/1074998
Fruit

14

Чтобы учесть имена файлов без символов перед точкой, вы должны использовать это небольшое изменение принятого ответа:

String extension = "";

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

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

вероятно, стоит защищаться от "фу". вход.
chrisinmtown

14

Это проверенный метод

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"));
}

10

Мой грязный и может крошечный, используя String.replaceAll :

.replaceAll("^.*\\.(.*)$", "$1")

Обратите внимание, что сначала *он жадный, поэтому он будет захватывать максимально возможное количество символов, а затем останется только последняя точка и расширение файла.


Это не удастся, если файл не имеет расширения.
Зэк

Да, к сожалению, все же, тем не менее, его можно использовать для простых сценариев, таких как быстрое определение типа файла, например, наличие неправильного расширения не сильно отличается от его отсутствия, или можно поставить условие if, когда результат замены равен входному.
Эбрагим Бягови,

1
Или даже короче.replaceAll(".*\\.", "")
Эбрагим Бягови

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

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

вернуть ".txt"

если вы хотите только "TXT", сделайте path.lastIndexOf(".") + 1


9

Как видно из всех других ответов, нет адекватной «встроенной» функции. Это безопасный и простой метод.

String getFileExtension(File file) {
    if (file == null) {
        return "";
    }
    String name = file.getName();
    int i = name.lastIndexOf('.');
    String ext = i > 0 ? name.substring(i + 1) : "";
    return ext;
}

7

Как насчет (с использованием Java 1.5 RegEx):

    String[] split = fullFileName.split("\\.");
    String ext = split[split.length - 1];

6

Если вы планируете использовать Apache commons-io и просто хотите проверить расширение файла, а затем выполнить какую-либо операцию, вы можете использовать это , вот фрагмент:

if(FilenameUtils.isExtension(file.getName(),"java")) {
    someoperation();
}

Обратите внимание, что эта проверка чувствительна к регистру в соответствии с документами.
Бабкен Варданян

6

Вот еще один вкладыш для Java 8.

String ext = Arrays.stream(fileName.split("\\.")).reduce((a,b) -> b).orElse(null)

Это работает следующим образом:

  1. Разбейте строку на массив строк, используя «.»
  2. Конвертировать массив в поток
  3. Используйте Reduce, чтобы получить последний элемент потока, то есть расширение файла.

4

Как насчет JFileChooser? Это не просто, так как вам нужно будет проанализировать его окончательный результат ...

JFileChooser filechooser = new JFileChooser();
File file = new File("your.txt");
System.out.println("the extension type:"+filechooser.getTypeDescription(file));

который является типом MIME ...

Хорошо ... я забыл, что вы не хотите знать его MIME-тип.

Интересный код по следующей ссылке: http://download.oracle.com/javase/tutorial/uiswing/components/filechooser.html

/*
 * Get the extension of a file.
 */  
public static String getExtension(File f) {
    String ext = null;
    String s = f.getName();
    int i = s.lastIndexOf('.');

    if (i > 0 &&  i < s.length() - 1) {
        ext = s.substring(i+1).toLowerCase();
    }
    return ext;
}

Связанный вопрос: Как мне урезать расширение файла из строки в Java?


4

Вот метод, который обрабатывает .tar.gzправильно, даже в пути с точками в именах каталогов:

private static final String getExtension(final String filename) {
  if (filename == null) return null;
  final String afterLastSlash = filename.substring(filename.lastIndexOf('/') + 1);
  final int afterLastBackslash = afterLastSlash.lastIndexOf('\\') + 1;
  final int dotIndex = afterLastSlash.indexOf('.', afterLastBackslash);
  return (dotIndex == -1) ? "" : afterLastSlash.substring(dotIndex + 1);
}

afterLastSlashСоздан для afterLastBackslashускорения поиска, так как ему не придется искать всю строку, если в ней есть какие-то косые черты.

char[]В оригинале Stringиспользуется повторно, не добавляя мусор там, и виртуальная машина, вероятно , заметили , что afterLastSlashсразу же мусор для того , чтобы поместить его в стек вместо кучи .


этот метод скопирован из исходного кода Guava, вы должны упомянуть об этом.
сумасшедший

1
Я не копировал это. Если это в исходном коде Guava, они скопировали его отсюда. Возможно уведомить их.
Олат

извините за то, что это не идентично между прочим, так что, может, у вас и разработчика Guava просто одна и та же идея.
сумасшедший

2
Действительно "gz" - правильное расширение для возврата. Если вызывающий код также может обрабатывать «tar», он должен дополнительно проверить внешнюю по отношению к getExtensionфункции. Если имя файла пользователя, "my zip. don't touch.tar.gz"то этот метод вернет неправильное расширение.
Intrepidis

2
// Modified from EboMike's answer

String extension = "/path/to/file/foo.txt".substring("/path/to/file/foo.txt".lastIndexOf('.'));

расширение должно иметь ".txt" в нем при запуске.


13
Сбой, если имя не имеет расширения.
EboMike

2

Вот версия с Optional в качестве возвращаемого значения (потому что вы не можете быть уверены, что файл имеет расширение) ... также проверяет работоспособность ...

import java.io.File;
import java.util.Optional;

public class GetFileExtensionTool {

    public static Optional<String> getFileExtension(File file) {
        if (file == null) {
            throw new NullPointerException("file argument was null");
        }
        if (!file.isFile()) {
            throw new IllegalArgumentException("getFileExtension(File file)"
                    + " called on File object that wasn't an actual file"
                    + " (perhaps a directory or device?). file had path: "
                    + file.getAbsolutePath());
        }
        String fileName = file.getName();
        int i = fileName.lastIndexOf('.');
        if (i > 0) {
            return Optional.of(fileName.substring(i + 1));
        } else {
            return Optional.empty();
        }
    }
}

2

Как насчет версии REGEX :

static final Pattern PATTERN = Pattern.compile("(.*)\\.(.*)");

Matcher m = PATTERN.matcher(path);
if (m.find()) {
    System.out.println("File path/name: " + m.group(1));
    System.out.println("Extention: " + m.group(2));
}

или с поддерживаемым нулевым расширением:

static final Pattern PATTERN =
    Pattern.compile("((.*\\" + File.separator + ")?(.*)(\\.(.*)))|(.*\\" + File.separator + ")?(.*)");

class Separated {
    String path, name, ext;
}

Separated parsePath(String path) {
    Separated res = new Separated();
    Matcher m = PATTERN.matcher(path);
    if (m.find()) {
        if (m.group(1) != null) {
            res.path = m.group(2);
            res.name = m.group(3);
            res.ext = m.group(5);
        } else {
            res.path = m.group(6);
            res.name = m.group(7);
        }
    }
    return res;
}


Separated sp = parsePath("/root/docs/readme.txt");
System.out.println("path: " + sp.path);
System.out.println("name: " + sp.name);
System.out.println("Extention: " + sp.ext);

результат для * nix:
путь: / root / docs /
name: readme
Расширение: txt

для Windows: parsePath ("c: \ windows \ readme.txt"):
путь: c: \ windows \
name: readme
Расширение: txt



1

Здесь я сделал небольшой метод (однако не настолько безопасный и не проверяющий много ошибок), но если только вы программируете обычную java-программу, этого более чем достаточно, чтобы найти тип файла. Это не работает для сложных типов файлов, но они обычно используются не так часто.

    public static String getFileType(String path){
       String fileType = null;
       fileType = path.substring(path.indexOf('.',path.lastIndexOf('/'))+1).toUpperCase();
       return fileType;
}

ОП ищет встроенный метод
Panther

(1) Вы должны использовать lastIndexOfтак, чтобы имена файлов вроде john.smith.report.docобрабатывались правильно. (2) Вы должны правильно обрабатывать случаи, когда нет расширения. Этот метод возвращает ABC/XYZпуть вроде abc/xyz, который не имеет никакого смысла. Было бы больше смысла вернуться ""или null. (3) Разделитель файлов не всегда /.
Radiodef

1

Получение расширения файла из имени файла

/**
 * The extension separator character.
 */
private static final char EXTENSION_SEPARATOR = '.';

/**
 * The Unix separator character.
 */
private static final char UNIX_SEPARATOR = '/';

/**
 * The Windows separator character.
 */
private static final char WINDOWS_SEPARATOR = '\\';

/**
 * The system separator character.
 */
private static final char SYSTEM_SEPARATOR = File.separatorChar;

/**
 * Gets the extension of a filename.
 * <p>
 * This method returns the textual part of the filename after the last dot.
 * There must be no directory separator after the dot.
 * <pre>
 * foo.txt      --> "txt"
 * a/b/c.jpg    --> "jpg"
 * a/b.txt/c    --> ""
 * a/b/c        --> ""
 * </pre>
 * <p>
 * The output will be the same irrespective of the machine that the code is running on.
 *
 * @param filename the filename to retrieve the extension of.
 * @return the extension of the file or an empty string if none exists.
 */
public static String getExtension(String filename) {
    if (filename == null) {
        return null;
    }
    int index = indexOfExtension(filename);
    if (index == -1) {
        return "";
    } else {
        return filename.substring(index + 1);
    }
}

/**
 * Returns the index of the last extension separator character, which is a dot.
 * <p>
 * This method also checks that there is no directory separator after the last dot.
 * To do this it uses {@link #indexOfLastSeparator(String)} which will
 * handle a file in either Unix or Windows format.
 * <p>
 * The output will be the same irrespective of the machine that the code is running on.
 *
 * @param filename  the filename to find the last path separator in, null returns -1
 * @return the index of the last separator character, or -1 if there
 * is no such character
 */
public static int indexOfExtension(String filename) {
    if (filename == null) {
        return -1;
    }
    int extensionPos = filename.lastIndexOf(EXTENSION_SEPARATOR);
    int lastSeparator = indexOfLastSeparator(filename);
    return (lastSeparator > extensionPos ? -1 : extensionPos);
}

/**
 * Returns the index of the last directory separator character.
 * <p>
 * This method will handle a file in either Unix or Windows format.
 * The position of the last forward or backslash is returned.
 * <p>
 * The output will be the same irrespective of the machine that the code is running on.
 *
 * @param filename  the filename to find the last path separator in, null returns -1
 * @return the index of the last separator character, or -1 if there
 * is no such character
 */
public static int indexOfLastSeparator(String filename) {
    if (filename == null) {
        return -1;
    }
    int lastUnixPos = filename.lastIndexOf(UNIX_SEPARATOR);
    int lastWindowsPos = filename.lastIndexOf(WINDOWS_SEPARATOR);
    return Math.max(lastUnixPos, lastWindowsPos);
}

кредиты

  1. Скопировано из класса Apache FileNameUtils - http://grepcode.com/file/repo1.maven.org/maven2/commons-io/commons-io/1.3.2/org/apache/commons/io/FilenameUtils.java#FilenameUtils. getExtension% 28java.lang.String% 29

1

Без использования какой-либо библиотеки вы можете использовать метод String split следующим образом:

        String[] splits = fileNames.get(i).split("\\.");

        String extension = "";

        if(splits.length >= 2)
        {
            extension = splits[splits.length-1];
        }

0

Просто альтернатива на основе регулярных выражений. Не так быстро, не так хорошо.

Pattern pattern = Pattern.compile("\\.([^.]*)$");
Matcher matcher = pattern.matcher(fileName);

if (matcher.find()) {
    String ext = matcher.group(1);
}

0

Я нашел лучший способ найти расширение, смешав все ответы выше

public static String getFileExtension(String fileLink) {

        String extension;
        Uri uri = Uri.parse(fileLink);
        String scheme = uri.getScheme();
        if (scheme != null && scheme.equals(ContentResolver.SCHEME_CONTENT)) {
            MimeTypeMap mime = MimeTypeMap.getSingleton();
            extension = mime.getExtensionFromMimeType(CoreApp.getInstance().getContentResolver().getType(uri));
        } else {
            extension = MimeTypeMap.getFileExtensionFromUrl(fileLink);
        }

        return extension;
    }

public static String getMimeType(String fileLink) {
        String type = CoreApp.getInstance().getContentResolver().getType(Uri.parse(fileLink));
        if (!TextUtils.isEmpty(type)) return type;
        MimeTypeMap mime = MimeTypeMap.getSingleton();
        return mime.getMimeTypeFromExtension(FileChooserUtil.getFileExtension(fileLink));
    }

0

Мне нравится простота ответа Spectre , и в одном из его комментариев есть ссылка на другой ответ, который фиксирует точки в путях к файлам, на другой вопрос, заданный EboMike .

Без реализации какого-либо стороннего API, я предлагаю:

private String getFileExtension(File file) {

    String name = file.getName().substring(Math.max(file.getName().lastIndexOf('/'),
            file.getName().lastIndexOf('\\')) < 0 ? 0 : Math.max(file.getName().lastIndexOf('/'),
            file.getName().lastIndexOf('\\')));
    int lastIndexOf = name.lastIndexOf(".");
    if (lastIndexOf == -1) {
        return ""; // empty extension
    }
    return name.substring(lastIndexOf + 1); // doesn't return "." with extension
}

Нечто подобное может быть полезно, скажем, в любом из writeметодов ImageIO , где необходимо передать формат файла.

Зачем использовать весь сторонний API, когда вы можете сделать DIY?


0

Этот конкретный вопрос доставил мне много хлопот, тогда я нашел очень простое решение этой проблемы, которое я публикую здесь.

file.getName().toLowerCase().endsWith(".txt");

Вот и все.


3
ОП Нужен способ извлечь расширение, а не тестировать его.
Предраг Манойлович

на самом деле, что бы вы ни разрабатывали в большинстве случаев, вам нужно иметь дело только с определенным типом файлов ... поэтому, если ваша проблема возникает в этой области, это поможет вам.
Викрам Бхардвадж

4
Который не соответствует его требованиям
Предраг Манойлович

2
Это не ответ на вопрос, но это действительно было то, что я искал.
Эдиолот

-1

попробуй это.

String[] extension = "adadad.adad.adnandad.jpg".split("\\.(?=[^\\.]+$)"); // ['adadad.adad.adnandad','jpg']
extension[1] // jpg

-1
  @Test
    public void getFileExtension(String fileName){
      String extension = null;
      List<String> list = new ArrayList<>();
      do{
          extension =  FilenameUtils.getExtension(fileName);
          if(extension==null){
              break;
          }
          if(!extension.isEmpty()){
              list.add("."+extension);
          }
          fileName = FilenameUtils.getBaseName(fileName);
      }while (!extension.isEmpty());
      Collections.reverse(list);
      System.out.println(list.toString());
    }

-4

Java имеет встроенный способ решения этой проблемы в классе java.nio.file.Files , который может работать для ваших нужд:

File f = new File("/path/to/file/foo.txt");
String ext = Files.probeContentType(f.toPath());
if(ext.equalsIgnoreCase("txt")) do whatever;

Обратите внимание, что этот статический метод использует найденные здесь спецификации для получения «типа контента», который может варьироваться.


28
Это неверно Тип возврата для probeContentType - это тип содержимого Mime, а не расширение файла. Обычно это не будет соответствовать расширению. Это также будет довольно медленно в файловом браузере, так как он фактически открывает файл для определения типа.
Чарльз
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.