Перейти к основному содержимому

Получить имя файла без расширения в Java

· 6 мин. чтения

1. Обзор

Когда мы работаем с файлами в Java, нам часто приходится обрабатывать имена файлов. Например, иногда мы хотим получить имя без расширения из заданного имени файла. Другими словами, мы хотим удалить расширение имени файла.

В этом уроке мы обсудим общий способ удаления расширения из имени файла.

2. Сценарии удаления расширения из имени файла

При первом взгляде на это может показаться, что удаление расширения из имени файла — довольно простая задача.

Однако, если мы внимательно посмотрим на проблему, она может быть более сложной, чем мы думали.

Прежде всего, давайте посмотрим, какими могут быть имена файлов:

  • Без какого-либо расширения, например, « foreach»
  • С одним расширением это самый обычный случай, например " foreach.txt "
  • С несколькими расширениями, такими как « foreach.tar.gz »
  • Dotfile без расширения, например « .foreach »
  • Dotfile с одним расширением, например, « .foreach.conf » .
  • Dotfile с несколькими расширениями, например, « .foreach.conf.bak »

Далее мы перечислим ожидаемые результаты приведенных выше примеров после удаления расширений:

  • « foreach »: Имя файла не имеет расширения. Поэтому имя файла менять не следует, и у нас должно получиться « foreach » .
  • « foreach.txt »: это простой случай. Правильный результат: « foreach » .
  • « foreach.tar.gz »: это имя файла содержит два расширения. Если мы хотим удалить только одно расширение, результатом должно быть « foreach.tar ». Но если мы хотим удалить все расширения из имени файла, правильным результатом будет « foreach ».
  • « .foreach »: Поскольку это имя файла также не имеет расширения, имя файла также не следует изменять. Таким образом, мы ожидаем увидеть в результате « .foreach ».
  • « .foreach.conf »: Результат должен быть « .foreach » .
  • « .foreach.conf.bak »: Результат должен быть «.foreach.conf», если мы хотим удалить только одно расширение. В противном случае « .foreach » — это ожидаемый результат, если мы удалим все расширения .

В этом руководстве мы проверим, могут ли служебные методы, предоставляемые Guava и Apache Commons IO, обрабатывать все случаи, перечисленные выше.

Далее мы также обсудим общий способ решения проблемы удаления расширения (или расширений) из заданного имени файла.

3. Тестирование библиотеки гуавы

Начиная с версии 14.0, в Guava появился метод Files.getNameWithoutExtension() . Это позволяет нам легко удалить расширение из данного имени файла.

Чтобы использовать служебный метод, нам нужно добавить библиотеку Guava в наш путь к классам. Например, если мы используем Maven в качестве инструмента сборки, мы можем добавить зависимость Guava в наш файл pom.xml :

<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
<version>31.0.1-jre</version>
</dependency>

Во-первых, давайте посмотрим на реализацию этого метода:

public static String getNameWithoutExtension(String file) {
...
int dotIndex = fileName.lastIndexOf('.');
return (dotIndex == -1) ? fileName : fileName.substring(0, dotIndex);
}

Реализация довольно проста. Если имя файла содержит точки, метод сокращается от последней точки до конца имени файла. В противном случае, если имя файла не содержит точки, исходное имя файла будет возвращено без каких-либо изменений.

Поэтому метод getNameWithoutExtension() в Guava не будет работать для точечных файлов без расширения. `` Давайте напишем тест, чтобы доказать, что:

@Test
public void givenDotFileWithoutExt_whenCallGuavaMethod_thenCannotGetDesiredResult() {
//negative assertion
assertNotEquals(".foreach", Files.getNameWithoutExtension(".foreach"));
}

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

@Test
public void givenFileWithoutMultipleExt_whenCallGuavaMethod_thenCannotRemoveAllExtensions() {
//negative assertion
assertNotEquals("foreach", Files.getNameWithoutExtension("foreach.tar.gz"));
}

4. Тестирование библиотеки ввода-вывода Apache Commons

Подобно библиотеке Guava, популярная библиотека ввода-вывода Apache Commons предоставляет метод removeExtension() в классе FilenameUtils для быстрого удаления расширения имени файла.

Прежде чем мы рассмотрим этот метод, давайте добавим зависимость ввода-вывода Apache Commons в наш pom.xml :

<dependency>
<groupId>commons-io</groupId>
<artifactId>commons-io</artifactId>
<version>2.8.0</version>
</dependency>

Реализация аналогична методу getNameWithoutExtension() в Guava :

public static String removeExtension(final String filename) {
...
final int index = indexOfExtension(filename); //used the String.lastIndexOf() method
if (index == NOT_FOUND) {
return filename;
} else {
return filename.substring(0, index);
}
}

Поэтому метод Apache Commons IO также не будет работать с файлами точек :

@Test
public void givenDotFileWithoutExt_whenCallApacheCommonsMethod_thenCannotGetDesiredResult() {
//negative assertion
assertNotEquals(".foreach", FilenameUtils.removeExtension(".foreach"));
}

Если имя файла имеет несколько расширений, метод removeExtension() не может удалить все расширения:

@Test
public void givenFileWithoutMultipleExt_whenCallApacheCommonsMethod_thenCannotRemoveAllExtensions() {
//negative assertion
assertNotEquals("foreach", FilenameUtils.removeExtension("foreach.tar.gz"));
}

5. Удаление расширения (расширений) из имени файла

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

Однако, с другой стороны, у них есть некоторые недостатки:

  • Они не будут работать для точечных файлов, например, « .foreach » .
  • Когда имя файла имеет несколько расширений, они не предоставляют возможность удалить только последнее расширение или все расширения.

Далее создадим метод, охватывающий все случаи:

public static String removeFileExtension(String filename, boolean removeAllExtensions) {
if (filename == null || filename.isEmpty()) {
return filename;
}

String extPattern = "(?<!^)[.]" + (removeAllExtensions ? ".*" : "[^.]*$");
return filename.replaceAll(extPattern, "");
}

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

Основной частью этого метода является шаблон регулярного выражения . Итак, давайте разберемся, что делает этот шаблон регулярного выражения :

  • «(?<!^)[.]» — в этом регулярном выражении используется отрицательный просмотр назад . Соответствует точке « . ” это не в начале имени файла
  • « (?<!^)[.].* » — если установлена опция removeAllExtensions , это будет соответствовать первой совпадающей точке до конца имени файла.
  • « (?<!^)[.][^.]*$ » — этот шаблон соответствует только последнему расширению

Наконец, давайте напишем несколько тестовых методов, чтобы проверить, работает ли наш метод для всех различных случаев:

@Test
public void givenFilenameNoExt_whenCallFilenameUtilMethod_thenGetExpectedFilename() {
assertEquals("foreach", MyFilenameUtil.removeFileExtension("foreach", true));
assertEquals("foreach", MyFilenameUtil.removeFileExtension("foreach", false));
}

@Test
public void givenSingleExt_whenCallFilenameUtilMethod_thenGetExpectedFilename() {
assertEquals("foreach", MyFilenameUtil.removeFileExtension("foreach.txt", true));
assertEquals("foreach", MyFilenameUtil.removeFileExtension("foreach.txt", false));
}

@Test
public void givenDotFile_whenCallFilenameUtilMethod_thenGetExpectedFilename() {
assertEquals(".foreach", MyFilenameUtil.removeFileExtension(".foreach", true));
assertEquals(".foreach", MyFilenameUtil.removeFileExtension(".foreach", false));
}

@Test
public void givenDotFileWithExt_whenCallFilenameUtilMethod_thenGetExpectedFilename() {
assertEquals(".foreach", MyFilenameUtil.removeFileExtension(".foreach.conf", true));
assertEquals(".foreach", MyFilenameUtil.removeFileExtension(".foreach.conf", false));
}

@Test
public void givenDoubleExt_whenCallFilenameUtilMethod_thenGetExpectedFilename() {
assertEquals("foreach", MyFilenameUtil.removeFileExtension("foreach.tar.gz", true));
assertEquals("foreach.tar", MyFilenameUtil.removeFileExtension("foreach.tar.gz", false));
}

@Test
public void givenDotFileWithDoubleExt_whenCallFilenameUtilMethod_thenGetExpectedFilename() {
assertEquals(".foreach", MyFilenameUtil.removeFileExtension(".foreach.conf.bak", true));
assertEquals(".foreach.conf", MyFilenameUtil.removeFileExtension(".foreach.conf.bak", false));
}

6. Заключение

В этой статье мы говорили о том, как удалить расширения из данного имени файла.

Во-первых, мы обсудили различные сценарии удаления расширений.

Далее мы представили методы, предоставляемые двумя широко используемыми библиотеками: Guava и Apache Commons IO. Они очень удобны и работают в обычных случаях, но не могут работать с точечными файлами. Кроме того, они не предоставляют возможность удалить одно расширение или все расширения.

Наконец, мы создали метод, удовлетворяющий всем требованиям.

Как всегда, полный исходный код статьи доступен на GitHub .