2010-05-08 61 views
19

Khi tôi chạy một ứng dụng Java cần đọc từ một tệp trong Eclipse, tôi nhận được một java.io.FileNotFoundException, mặc dù tệp nằm trong đúng thư mục. Tôi có thể biên dịch và chạy ứng dụng từ dòng lệnh tốt; vấn đề chỉ xảy ra trong Eclipse, với nhiều hơn một dự án và ứng dụng. Có cài đặt nào tôi cần phải thay đổi trong cấu hình chạy hoặc tạo đường dẫn để làm cho nó có thể tìm thấy tệp chính xác không?Java không thể tìm thấy tệp khi chạy qua Eclipse

+1

mà không nhìn thấy mã của bạn, chúng tôi không thể cho bạn biết điều gì có thể xảy ra –

Trả lời

28

Vấn đề là khả năng nhất mà ứng dụng của bạn đang sử dụng một tên đường dẫn tương đối. Như @BalusC nói, tên đường dẫn tương đối có thể có vấn đề. Nhưng IMO, anh ta đi quá xa khi anh ấy nói "[y] ou nên không bao giờ sử dụng đường dẫn tương đối trong nội dung java.io".

Khi ứng dụng mở tệp bằng cách sử dụng (ví dụ) hàm tạo FileInputStream(File), tên đường dẫn tương đối được giải quyết tương ứng với "thư mục hiện tại" trong quá trình được mô tả như sau trong javadoc cho File.getAbsolutePath().

[...] Nếu không, tên đường dẫn này được giải quyết theo cách phụ thuộc vào hệ thống. Trên các hệ thống UNIX, một tên đường dẫn tương đối được thực hiện tuyệt đối bằng cách giải quyết nó chống lại thư mục người dùng hiện tại. Trên các hệ thống Microsoft Windows, một tên đường dẫn tương đối được thực hiện tuyệt đối bằng cách giải quyết nó dựa vào thư mục hiện tại của ổ đĩa được đặt tên bởi tên đường dẫn, nếu có; nếu không, nó được giải quyết dựa vào thư mục người dùng hiện tại.

Vì vậy, ngay lập tức, chúng ta thấy khái niệm "thư mục hiện tại" có các sắc thái khác nhau trên nền tảng Windows và UNIX. Vấn đề thứ hai là trong Java tinh khiết, bạn không thể tìm ra thư mục hiện tại là gì, và chắc chắn bạn không thể thay đổi nó cho JVM hiện tại bằng cách sử dụng Java thuần túy. (Khi JVM bắt đầu, thuộc tính hệ thống "user.dir" được đặt thành thư mục hiện tại, nhưng không có gì ngăn ứng dụng thay đổi thuộc tính để bạn không thể hoàn toàn dựa vào nó. Hơn nữa, thay đổi "user.dir" chỉ thay đổi cách đường dẫn trống được giải quyết, không phải đường dẫn tương đối nói chung.)

Vì vậy, bạn nên làm gì về điều này?

  • Một tùy chọn là sử dụng tên đường dẫn tuyệt đối để tham chiếu đến tệp. Điều này là đáng tin cậy trong (gần như) tất cả các trường hợp, nhưng sử dụng tên đường dẫn tuyệt đối có thể có vấn đề nếu người dùng phải nhập tên đường dẫn hoặc nếu bạn cần tránh các tên đường dẫn tuyệt đối có dây (hoặc được định cấu hình).

  • Tùy chọn thứ hai là sử dụng tên đường dẫn tương đối classpath và định vị tệp tương ứng với thư mục cài đặt của ứng dụng. Điều này làm việc nếu đó là những gì bạn cần làm, nhưng trình bày một vấn đề nếu bạn cần phải vượt qua một File đến một số phương pháp thư viện. Nó cũng không giúp ích nếu bạn đang cố gắng tìm các tùy chọn ứng dụng của người dùng. (Nói chung, việc đặt sở thích của người dùng vào thư mục cài đặt là sai lầm ...)

  • Tùy chọn thứ ba là đặt tên tệp tương đối với một số thư mục tuyệt đối mà bạn nhận được từ nơi khác; ví dụ. new File(System.getProperty("home.dir"), "foo/bar");.

  • Tùy chọn cuối cùng là sử dụng tên đường dẫn tương đối và giả sử rằng người dùng biết thư mục hiện tại. Đối với nhiều ứng dụng mà người dùng chạy từ dòng lệnh, đây là giải pháp đúng.

Trong trường hợp cụ thể của Eclipse, có một giải pháp đơn giản. Đi tới "cấu hình chạy" mà bạn đang sử dụng để khởi chạy ứng dụng của mình, mở tab "Đối số" và nhấp vào nút radio "Khác". Sau đó nhập tên đường dẫn tuyệt đối làm thư mục làm việc cho ứng dụng được khởi chạy. Khi JVM con được khởi chạy, nó sẽ có thư mục làm việc được chỉ định làm thư mục hiện tại của nó.

+0

Cảm ơn sự giúp đỡ của bạn! – derekerdmann

+0

Đã xảy ra sự cố với ứng dụng của tôi được khởi chạy từ nhật thực không thể đọc được một số tệp thuộc tính nằm trong thư mục gốc của dự án của tôi.Sau khi tôi đã thử thiết lập thư mục làm việc trong cấu hình chạy đến $ {workspace_loc}/myproject/bin nó đã hoạt động !. – Krishnaraj

+0

@Krishnaraj - Đó là những gì tôi đã nói trong đoạn cuối của câu trả lời của tôi :-) –

6

Khi bạn tạo một ứng dụng Java mặc định trong Eclipse, bạn sẽ có được cấu trúc thư mục này:

./ProjectName/ - thư mục gốc
./ProjectName/bin/ - thư mục đầu ra, chứa .class file
./ProjectName/src/ - thư mục nguồn, chứa file java

Nếu yêu cầu ứng dụng của bạn "./data.txt" nó sẽ tìm kiếm nó tương đối so với thư mục gốc. Đây là "thư mục làm việc" và có thể được cấu hình trong tab đối số theo câu trả lời của Martin ở trên.

Bạn nói nó hoạt động từ dòng lệnh? Điều này có thể là do bạn đang ở trong thư mục bin hoặc src khi bạn chạy tệp nhị phân java. Thư mục làm việc trong trường hợp này là bất kỳ thư mục nào mà dấu nhắc lệnh hiện đang ở bên trong. Nếu, ví dụ, bạn đi vào thư mục/src /, giả sử javac *.java sau đó chạy các tệp từ đó, nó sẽ tìm kiếm "./data.txt" trong thư mục/src /. Nếu bạn vào trong thư mục/bin/và chạy ứng dụng của bạn từ đó, nó sẽ tìm tệp tương đối với thư mục/bin /.

+0

Cảm ơn sự giúp đỡ của bạn. Các tệp văn bản tôi cần tồn tại trong thư mục/bin /, do đó, điều đó dường như không phải là vấn đề. Tôi sẽ chơi xung quanh với những con đường tương đối và xem những gì tôi có thể nhận được. – derekerdmann

2

Bạn nên không bao giờ sử dụng đường dẫn tương đối trong các công cụ java.io. Đường dẫn sẽ trở nên phụ thuộc vào thư mục làm việc hiện tại, phụ thuộc vào cách bạn khởi động ứng dụng và do đó không phải như nhau trên tất cả các môi trường. Điều này không thể kiểm soát được từ bên trong ứng dụng Java. Sự cố về tính di động! Luôn luôn sử dụng đường dẫn tuyệt đối. Vì vậy, ví dụ: c:/path/to/file.ext hoặc /path/to/file.ext (có dấu gạch chéo hàng đầu) cho UNIX và liên kết (hoặc thậm chí Windows khi thư đĩa không liên quan).

Bất cứ khi nào bạn muốn gửi một số tệp cùng với ứng dụng của mình, thực tiễn phổ biến là đặt chúng trong đường dẫn lớp classpath. Bằng cách này bạn chỉ có thể sử dụng ClassLoader#getResource() để có được vị trí của nó. Nó trả về một URL. Bạn có thể sử dụng URL#toURI() hoặc URL#getPath() và chuyển nó tới hàm tạo của java.io.File và sau đó sử dụng nó theo cách thông thường.

Trong dự án Eclipse của bạn, thư mục src (nơi nguồn Java của bạn đi) về cơ bản là gốc của đường dẫn lớp. Hơn nữa nó tất nhiên cũng bao gồm tất cả các dự án khác và (bên ngoài) thư mục được thực hiện trong dự án của Xây dựng đường dẫn.

Giả sử bạn đã đặt các tập tin đặc biệt trong gốc của classpath:

ClassLoader classLoader = Thread.currentThread().getContextClassLoader(); 
URL url = classLoader.getResource("file.ext"); 
File file = new File(url.getPath()); 
FileInputStream input = new FileInputStream(file); 
// ... 

Bạn thậm chí có thể sử dụng ClassLoader#getResourceAsStream() để trực tiếp nhận được một InputStream:

ClassLoader classLoader = Thread.currentThread().getContextClassLoader(); 
InputStream input = classLoader.getResourceAsStream("file.ext"); 
// ... 

Nếu nó được đặt bên trong một gói, sau đó bạn chỉ có thể sử dụng tên đường dẫn thông thường:

URL url = classLoader.getResource("com/example/file.ext"); 
// ... 

hoặc

InputStream input = classLoader.getResourceAsStream("com/example/file.ext"); 
// ... 
+0

một ứng dụng dòng lệnh không GUI có thể được thiết kế hợp lý để sử dụng tên đường dẫn tương đối. Thông báo trước là người dùng cần phải hiểu khái niệm "thư mục hiện tại" và lập trình viên cần phải hiểu cách bản đồ này hoạt động như thế nào đối với các thư viện Java IO. –

+0

@Stephen: Chắc chắn có thể, bạn sẽ chỉ phải gặp rắc rối với các đường dẫn tương đối, nhưng nó không được khuyến khích. Lấy lợi ích của classpath làm cho nó mạnh mẽ hơn. – BalusC

+0

tùy thuộc vào ứng dụng đang hoạt động. Ví dụ, nếu nó là thao tác các tập tin được cung cấp bởi người dùng, thì cách tiếp cận classpath có ý nghĩa rất ít. –

1

Giả sử người dùng không nhập đường dẫn tệp đầy đủ vào tệp và nhập nội dung như "myfilenameonly". File file = new File(".", args[0]) là cần thiết trong trường hợp này để định vị tệp (chú ý đến đối số đầu tiên được chuyển).

Tất cả nền tảng: File.getParent() không trả về thư mục mẹ, phải trả về ".." hoặc tên của thư mục mẹ trong hệ thống tệp cách cụ thể.

Nếu bạn tạo Tệp "myfilenameonly" mà không chỉ định đường dẫn đầy đủ vào thư mục nơi đặt trụ sở, số File.getParent(), ví dụ: sẽ trả về giá trị rỗng.

Xem thêm: http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=1228537

2

Tôi đã có một vấn đề tương tự, tôi đặt các file trong một thư mục có tên cobolcopybooks bên trong thư mục src và cố gắng để truy cập chúng bên trong dự án của tôi sử dụng classloader.getResource ("cobolcopybooks/demostud. Cob ") nhưng tôi đã nhận được ngoại lệ con trỏ null, tôi đã thử nó nhiều lần bằng cách làm sạch và xây dựng không gian làm việc sau nhiều lần thất bại tôi nhận ra rằng tôi không làm mới dự án để cho phép các tệp được xây dựng cùng với dự án. tức là các tệp này sẽ được hiển thị cùng với các tệp lớp khác như khi chạy thư mục gốc sẽ là thư mục bin và nó tìm kiếm các tệp đó ở đó.

8

Tùy chọn khác chỉ đơn giản là tìm ra thư mục "đường dẫn hiện tại" trỏ đến trong môi trường của bạn - bất kể đó là gì. Một khi bạn tìm ra, bạn có thể chọn giải pháp của bạn từ đó. Có thể đó là sử dụng đường dẫn tương đối phù hợp với vị trí tệp của bạn hoặc di chuyển tệp.

File testFile = new File(""); 
    String currentPath = testFile.getAbsolutePath(); 
    System.out.println("current path is: " + currentPath); 
+1

Đây không phải là lý tưởng trong mã vận chuyển, nhưng nó chắc chắn hữu ích như mã gỡ lỗi để xem những gì ứng dụng của bạn nghĩ là đang xảy ra. – Steve

Các vấn đề liên quan