Java не может найти файл при запуске через Eclipse

Когда я запускаю приложение Java, которое должно читать из файла в Eclipse, я получаю java.io.FileNotFoundException, даже если файл находится в правильном каталоге. Я могу отлично скомпилировать и запустить приложение из командной строки; проблема возникает только в Eclipse с более чем одним проектом и приложением. Есть ли параметр, который мне нужно изменить в конфигурациях запуска или путях сборки, чтобы он правильно нашел файл?


person derekerdmann    schedule 08.05.2010    source источник
comment
не видя вашего кода, мы не можем сказать вам, что может происходить   -  person Kevin Day    schedule 08.05.2010


Ответы (6)


Проблема, скорее всего, в том, что ваше приложение использует относительный путь. Как говорит @BalusC, относительные пути могут быть проблематичными. Но, по моему мнению, он заходит слишком далеко, когда говорит "[вам]не следует никогда использовать относительные пути в материалах java.io".

Когда приложение открывает файл с помощью (например) конструктора FileInputStream(File), относительные пути разрешаются относительно «текущего каталога» в процессе, описанном ниже в javadoc для File.getAbsolutePath().

[...] В противном случае это имя пути разрешается системно-зависимым способом. В системах UNIX относительный путь становится абсолютным путем разрешения его относительно текущего пользовательского каталога. В системах Microsoft Windows относительное имя пути становится абсолютным путем его сопоставления с текущим каталогом диска, названного по имени пути, если таковое имеется; если нет, то он разрешается относительно текущего пользовательского каталога.

Итак, мы сразу видим, что понятие «текущий каталог» имеет разные оттенки на платформах Windows и UNIX. Вторая проблема заключается в том, что в чистой Java вы не можете окончательно узнать, что такое текущий каталог, и вы, конечно, не можете изменить его для текущей JVM, используя чистую Java. (При запуске JVM для системного свойства "user.dir" устанавливается текущий каталог, но ничего не мешает приложению изменить это свойство, поэтому вы не можете полностью полагаться на него. Кроме того, изменение «user.dir» изменяет только способ разрешения пустого пути, а не относительные пути в целом.)

Итак, что вы должны сделать по этому поводу?

  • Один из вариантов — использовать абсолютные пути для ссылки на файлы. Это надежно (почти) во всех случаях, но использование абсолютных путей может быть проблематичным, если пользователь должен ввести путь, или если вам нужно избежать жестко запрограммированных (или настроенных) абсолютных путей.

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

  • Третий вариант — назвать файл относительно некоторого абсолютного каталога, который вы получили откуда-то еще; например new File(System.getProperty("home.dir"), "foo/bar");.

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

В частном случае Eclipse есть простое решение. Перейдите к «конфигурации запуска», которую вы используете для запуска приложения, откройте вкладку «Аргументы» и щелкните переключатель «Другое». Затем введите абсолютный путь в качестве рабочего каталога для запущенного приложения. Когда дочерняя JVM запускается, она будет иметь указанный рабочий каталог в качестве текущего каталога.

person Stephen C    schedule 08.05.2010

Другой вариант — просто выяснить, на какой каталог указывает «текущий путь» в вашей среде — что бы это ни было. Как только вы выясните, вы можете выбрать свое решение оттуда. Возможно, это связано с использованием соответствующего относительного пути к местоположению вашего файла или перемещением файла.

    File testFile = new File("");
    String currentPath = testFile.getAbsolutePath();
    System.out.println("current path is: " + currentPath);
person tgmoor    schedule 13.01.2014
comment
Это не идеально для выпуска кода, но определенно полезно в качестве кода отладки, чтобы увидеть, что думает ваше приложение. - person Steve; 09.09.2014

Когда вы создаете Java-приложение по умолчанию в Eclipse, вы получаете следующую структуру каталогов:

./ProjectName/ — корневой каталог
./ProjectName/bin/ — выходной каталог, содержащий файлы .class
./ProjectName/ src/ - исходный каталог, содержащий файлы .java

Если ваше приложение запрашивает "./data.txt", оно будет искать его относительно корневого каталога. Это «рабочий каталог», и его можно настроить на вкладке аргументов в соответствии с ответом Мартина выше.

Вы говорите, что это работает из командной строки? Вероятно, это связано с тем, что вы находитесь внутри папок bin или src, когда запускаете двоичный файл java. Рабочий каталог в этом случае — это тот каталог, в котором в данный момент находится командная строка. Если, например, вы зайдете в каталог /src/, скажите javac *.java, а затем запустите файлы оттуда, он будет искать «./data.txt» в каталоге /src/. Если вы войдете в каталог /bin/ и запустите оттуда свое приложение, оно будет искать файл относительно каталога /bin/.

person Gunslinger47    schedule 08.05.2010
comment
Спасибо за вашу помощь. Текстовые файлы, которые мне нужны, существуют в каталоге /bin/, так что проблема не в этом. Я поиграю с относительными путями и посмотрю, что я могу получить. - person derekerdmann; 08.05.2010

Вы не должны никогда использовать относительные пути в java.io материалах. Путь стал бы зависеть от текущего рабочего каталога, который зависит от того, как вы запустили приложение, и, таким образом, сам по себе не одинаков во всех средах. Это неуправляемо изнутри приложения Java. Проблема переносимости! Всегда используйте абсолютные пути. Таким образом, например. c:/path/to/file.ext или /path/to/file.ext (с косой чертой в начале) для UNIX и консортов (или даже для Windows, если буква диска не имеет значения).

Всякий раз, когда вы хотите отправить некоторые файлы вместе с вашим приложением, обычной практикой является размещение их также в classpath. Таким образом, вы можете просто использовать ClassLoader#getResource(), чтобы узнать его местоположение. Он возвращает URL. Вы можете использовать URL#toURI() или URL#getPath() и передать его конструктору java.io.File и далее использовать его обычным способом.

В вашем проекте Eclipse папка src (куда идет ваш исходный код Java) в основном является корнем пути к классам. Кроме того, это, конечно, также охватывает все другие проекты и (внешние) папки, которые взяты в Пути сборки проекта.

Предполагая, что вы поместили конкретный файл в root пути к классам:

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

Вы даже можете использовать ClassLoader#getResourceAsStream() для прямого получения InputStream :

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

Если он помещен внутри пакета, вы можете просто использовать обычные пути:

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

or

InputStream input = classLoader.getResourceAsStream("com/example/file.ext");
// ...
person BalusC    schedule 08.05.2010
comment
приложение командной строки без графического интерфейса может быть разумно спроектировано для использования относительных имен путей. Предостережение заключается в том, что пользователь должен понимать концепцию текущего каталога, а программист должен понимать, как это соотносится с поведением библиотек ввода-вывода Java. - person Stephen C; 08.05.2010
comment
@Stephen: Это, безусловно, возможно, вам придется возиться только с относительными путями, но это не рекомендуется. Использование пути к классам делает его более надежным. - person BalusC; 08.05.2010
comment
это зависит от того, что делает приложение. Например, если он манипулирует файлами, предоставленными пользователем, то подход classpath не имеет особого смысла. - person Stephen C; 08.05.2010
comment
@Stephen: Таким образом, это не так в исходном вопросе :) - person BalusC; 08.05.2010
comment
но ЕСТЬ дело в контексте вашего ответа - Вы не должны никогда использовать относительные пути в материалах java.io. - person Stephen C; 08.05.2010

У меня была аналогичная проблема, я поместил свои файлы в папку с именем cobolcopybooks внутри папки src и попытался получить к ним доступ внутри моего проекта, используя classloader.getResource("cobolcopybooks/demostud.cob"), но я получал исключение нулевого указателя, я пробовал это несколько раз, очищая и создавая рабочие пространства, после нескольких неудачных попыток я понял, что не обновлял проект, чтобы файлы могли быть созданы вместе с проектом. то есть эти файлы должны быть видны вместе с другими файлами классов, так как во время выполнения корневой каталог будет каталогом bin, и он ищет эти файлы там.

person Farheen Mohammad    schedule 29.11.2012

Предположим, что пользователь не вводит полный путь к файлу и вводит что-то вроде «myfilenameonly». File file = new File(".", args[0]) в этом случае необходим для поиска файла (учитывая первый переданный аргумент).

Все платформы: File.getParent() не возвращает родительский каталог, он должен возвращать «..» или имя родительского каталога в зависимости от файловой системы.

Если вы создадите файл "myfilenameonly" без указания полного пути к каталогу, в котором он находится, то File.getParent(), например, вернет null.

См. также: http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=1228537

person Marcos Leão    schedule 09.09.2011