Java: tách tên tệp thành cơ sở và phần mở rộng


83

Có cách nào tốt hơn để lấy tên cơ sở và phần mở rộng của tệp hơn là những thứ như

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

7
Hãy xem commons-io FilenameUtils . Nó có các phương pháp getBaseName(..)getExtension(..).
Bozho

Đối với chỉ phần mở rộng, xem stackoverflow.com/questions/3571223/... .
Andy Thomas

Câu trả lời:


168

Tôi biết những người khác đã đề cập đến String.split, nhưng đây là một biến thể chỉ mang lại hai mã thông báo (cơ sở và phần mở rộng):

String[] tokens = fileName.split("\\.(?=[^\\.]+$)");

Ví dụ:

"test.cool.awesome.txt".split("\\.(?=[^\\.]+$)");

Sản lượng:

["test.cool.awesome", "txt"]

Biểu thức chính quy yêu cầu Java phân chia theo bất kỳ khoảng thời gian nào theo sau bởi bất kỳ số lượng không phải dấu chấm nào, theo sau là phần cuối của đầu vào. Chỉ có một thời kỳ phù hợp với định nghĩa này (cụ thể là thời kỳ cuối cùng ).

Về mặt kỹ thuật Nói một cách độc đáo, kỹ thuật này được gọi là cái nhìn tích cực không độ rộng .


BTW, nếu bạn muốn tách đường dẫn và lấy tên tệp đầy đủ bao gồm nhưng không giới hạn phần mở rộng dấu chấm, sử dụng đường dẫn có dấu gạch chéo về phía trước,

    String[] tokens = dir.split(".+?/(?=[^/]+$)");

Ví dụ:

    String dir = "/foo/bar/bam/boozled"; 
    String[] tokens = dir.split(".+?/(?=[^/]+$)");
    // [ "/foo/bar/bam/" "boozled" ] 

2
Tôi không hiểu tại sao mọi người lại sợ sự phụ thuộc ;-)
Bozho

3
@Bozho: Tôi đồng ý rằng thư viện là giải pháp tốt hơn cho loại vấn đề này. Nó cho phép người khác thực hiện việc duy trì và suy nghĩ cho bạn (đó là lý do tại sao tôi bình chọn câu trả lời của bạn!). Điều này nghe có vẻ tầm thường, nhưng có một phần trong tôi luôn do dự khi cân nhắc việc đưa vào thư viện Apache vì tôi đã phải chịu đựng "địa ngục JAR" trong quá khứ với một số thứ của họ (tôi biết, nó tầm thường).
Adam Paynter

4
@Bozho: Adam đúng 100%. Vấn đề này sẽ không đủ để đảm bảo tôi sử dụng một thư viện khác - nhưng nếu tôi đã sử dụng commons-io vì những lý do khác, thì tôi sẽ sử dụng Filenameutils.
Jason S

1
@Jason: Biểu thức chính quy: món quà không ngừng trao tặng. :)
Adam Paynter

3
@Bozho - Cực khoái? Câu hỏi thực sự là tại sao java đi kèm với vô số lớp thừa, gần như giúp bạn dễ dàng thực hiện những gì bạn thực sự muốn, nhưng sau đó thật bực bội là không bao giờ thực sự làm được. Không có gì tương đương với Apache-Commons trong Python vì Python đơn giản là đã có sẵn tất cả những thứ hữu ích mà bạn muốn. C # dường như là một ví dụ khác về ngôn ngữ mà bạn có thể tập trung vào vấn đề duy nhất của mình thay vì phải tìm cách phát minh lại bánh xe hoặc đi lấy bánh xe do người khác phát minh ra.
ArtOfWarfare

84

Câu hỏi cũ nhưng tôi thường sử dụng giải pháp này:

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" !)

Không hoạt động nếu làm việc trong windows và Chuỗi "fileName" là "D: \ resources \ ftp_upload.csv" Bạn có thể giúp đỡ không?
NIKHIL CHAURASIA

3
@NIKHILCHAURASIA bạn cần phải thoát khỏi các dấu gạch chéo ngược, bằng cách nhân đôi chúng. Như: "D: \\ resources \\ ftp_upload.csv".
Ricket

8

Nguồn: http://www.java2s.com/Code/Java/File-Input-Output/Getextensionpathandfilename.htm

một lớp tiện ích như vậy:

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

sử dụng:

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()sẽ là một cái tên hay hơn thay vìfilename()
nimcap

trong trường hợp không có phần mở rộng (ví dụ: tên tệp như "/ etc / hosts"), điều này sẽ trả về "hosts" là phần mở rộng (thay vì ""). các lớp tiện ích cấp thư viện nên quan tâm đến các trường hợp góc.
Zach-M

6

http://docs.oracle.com/javase/6/docs/api/java/io/File.html#getName ()

Từ http://www.xinotes.org/notes/note/774/ :

Java có các hàm tích hợp để lấy tên cơ sở và tên dirname cho một đường dẫn tệp nhất định, nhưng tên hàm không rõ ràng như vậy.

import java.io.File;

public class JavaFileDirNameBaseName {
    public static void main(String[] args) {
    File theFile = new File("../foo/bar/baz.txt");
    System.out.println("Dirname: " + theFile.getParent());
    System.out.println("Basename: " + theFile.getName());
    }
}

4
java.io.File.getName () trả về tên có phần mở rộng.
Bram

2
Tôi thích nghĩ rằng không có thứ như "phần mở rộng" :-)

3
nhưng câu hỏi là về việc mở rộng ...
user85421

4

Phần mở rộng tệp là một khái niệm bị hỏng

Và không tồn tại chức năng đáng tin cậy cho nó. Hãy xem xét ví dụ tên tệp này:

archive.tar.gz

Có gì phần mở rộng? Người dùng DOS sẽ thích cái tên này hơn archive.tgz. Đôi khi bạn thấy các ứng dụng Windows ngu ngốc đầu tiên giải nén tệp (tạo ra một .tartệp), sau đó bạn phải mở lại để xem nội dung lưu trữ.

Trong trường hợp này, một khái niệm hợp lý hơn về phần mở rộng tệp sẽ là .tar.gz. Ngoài ra còn có .tar.bz2, .tar.xz, .tar.lz.tar.lzmatập tin "mở rộng" được sử dụng. Nhưng bạn sẽ quyết định như thế nào, nên tách ở dấu chấm cuối cùng hay từ dấu chấm thứ hai đến dấu chấm cuối cùng?

Thay vào đó, hãy sử dụng các loại kịch câm.

Hàm Files.probeContentType trong Java 7 có thể sẽ đáng tin cậy hơn nhiều để phát hiện các loại tệp hơn là tin tưởng vào phần mở rộng tệp. Gần như tất cả thế giới Unix / Linux cũng như Webbrowser và Điện thoại thông minh của bạn đã làm theo cách này.


6
Làm thế nào để trả lời câu hỏi này? Cũng Filekhông Pathđể tôi tách phần mở rộng.
Andreas Abel

@ andreas.abel hãy để tôi nhắc lại điều này: Phần mở rộng tệp là một khái niệm bị hỏng. Chúng không đáng tin cậy, cũng không được xác định rõ ràng ngoại trừ tên tệp DOS 8 + 3 (xem xét .tar.gzso với .tgztất cả quá phổ biến trên unix). Sử dụng các loại kịch câm để thay thế.
Có QUIT - Anony-Mousse

1
@ Anony-Mousse Vâng, tôi đồng ý về nguyên tắc nhưng 99.999% của tất cả các hệ thống tôi tương tác với việc sử dụng một tên tập tin, không phải là một loại mime
Christian Sauer

Vấn đề nằm ở đâu trong việc sử dụng Files.probeContentTypethay vì dựa vào tên tệp để có phần mở rộng phù hợp?
Có QUIT - Anony-Mousse

3
Điều này không trả lời câu hỏi. Tôi có một use-case trong đó tên tệp, phim, là tên + phần mở rộng. Làm cách nào để trích xuất tên bằng cách sử dụng kiểu kịch câm?
Niek

1

Có gì sai với mã của bạn? Được bao bọc trong một phương pháp tiện ích gọn gàng, nó tốt.

Điều quan trọng hơn là những gì được sử dụng làm dấu phân cách - dấu chấm đầu tiên hoặc cuối cùng. Đầu tiên là không tốt cho các tên tệp như "setup-2.5.1.exe", cuối cùng là không tốt cho các tên tệp có nhiều phần mở rộng như "mybundle.tar.gz".



-3

Có thể bạn có thể sử dụng String # split

Để trả lời bình luận của bạn:

Tôi không chắc liệu có thể có nhiều hơn một. trong một tên tệp, nhưng bất cứ điều gì, ngay cả khi có nhiều dấu chấm hơn, bạn có thể sử dụng phần tách. Hãy xem xét ví dụ:

String input = "boo.and.foo";

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

Điều này sẽ trả về một mảng chứa:

{ "boo", "and", "foo" }

Vì vậy, bạn sẽ biết rằng chỉ mục cuối cùng trong mảng là phần mở rộng và tất cả các chỉ số khác là cơ sở.


tốt, vâng, nhưng tôi sẽ phải tìm ra một regex cho người cuối cùng .trong một chuỗi
Jason S

1
Hmm, tôi không chắc, nhưng bạn không thể sử dụng "." Được không? Hoặc ar nhiều hơn 1 dấu chấm trong tên tệp?

2
Tôi nghĩ rằng điều này sẽ làm việc:fileName.split("\\.(?=[^\\.]+$)")
Adam Paynter

1
Bạn không thể cho rằng chỉ có một dấu chấm. Adam: cảm ơn, tôi sẽ thử.
Jason S

4
Câu trả lời này không chính xác. Vì dấu chấm không được thoát nên nó sẽ trả về một mảng trống.
aled
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.