Java load external CSV file not working after compiling to Jar - java

I am writing a Java program using NetBeans that times the process of developing black and white film. One of the options allows you to set the times manually and the other allows you to select the chosen film and developer to calculate the required times from a CSV file that is loaded into an array.
There are 3 files that are loaded, filmdb.csv, masterdb.csv and usersettings.csv. The first two are loaded purely for reading and the third file is loaded and can be written too to save the users default settings.
All 3 files are stored in the project directory and loaded in a similar way to the following code and called from main:
static String[] filmArray;
static int filmRows = 125;
int selectedDevTime;
int tempDevTime;
int minDevTime;
int secDevTime;
public int count = -1;
static void createFilmArray() {
filmArray = new String[filmRows];
Scanner scanLn = null;
int Rowc = 0;
int Row = 0;
String InputLine = "";
String filmFileName;
filmFileName = "filmdb.csv";
System.out.println("\n****** Setup Film Array ******");
try {
scanLn = new Scanner(new BufferedReader(new FileReader(filmFileName)));
while (scanLn.hasNextLine()) {
InputLine = scanLn.nextLine();
filmArray [Rowc] = InputLine;
Rowc++;
}
} catch (Exception e) {
System.out.println(e);
}
}
When I press the run button in NetBeans all works well, the files are all loaded and stored to the appropriate array but when I build the file as a .jar the files are not loaded, I have tried copying the 3 files to the same directory as the .jar as well as importing the 3 files into the .jar archive but to no joy.
Is there a specific location where the files should be placed, or should they be imported in a different way?
I would rather not load them from an absolute directory as for example a Windows user may have the files stored in C:\users\somebody\devtimer\file.csv and a Linux user may have the files stored in /home/somebody/devtimer/file.csv.
Cheers
Jim

your files got packaged into the *.jar
once a file is in the jar you cannot accessit as a file using a path (since, if you think about it, its no longer a file. its inside another file, the jar file).
you need to do something like this:
InsputStream csvStream = this.getClass().getResourceAsStream("filmdb.csv");
then you can pass this input stream to your csv parser.
you will not be able to make any modifications to the fhile though. any file you want to change you will need to store outside of your jar.
as for file paths, things like new File("somename") are resolved relative to the current working directory. if you want to know where your root is try something like:
System.err.println(new File(".").getCanonicalPath());
this will be very sensitive to what the working directory was when your application was executed, which in turn depends on how exactly it was executed etc.

If you are running the jar from the same directory the csv files are in then it should be loading. There might be some issue though if you just double click on the jar. You could create a new File represented by the filename and check the absolute path to find out where the jar is running from.
If you want to include the csv inside the jar (cleaner for distribution), you need to load the files in a different way. See this question for a nice example.

Related

How to read a file that is located in any location on the computer?

So I have a project, and this is one of the demands:
You should have a class named Project3, containing a main method.
This program reads the levels information from a file whose name is
specified as a command-line parameter (The file should also be
relative to the class-path as described here:)
All the file names specified in the levels and block definition files
should be relative to the class path. The reason we want them to be
relative to the class path is that later we will be able to read the
files from inside a jar, something we can not do with regular File
references.
To get an input stream relative to the class path (even if it's inside
a jar), use the following:
InputStream is =
ClassLoader.getSystemClassLoader().getResourceAsStream("image.png");
The idea is to keep a folder with files(definitions and images) and
then add that folder to the class path when running the JVM:
java -cp bin:resources ... If you don't add the resources folder to
you class path you wont be able to load them with the command from
above.
When run without parameters, your program should read a default level
file, and run the game accordingly. The location of the default level
file should be hard-coded in your code, and be relative to the
classpath_.
When run without parameters, your program should read a default level file, and run the game accordingly. The location of the default level file should be hard-coded in your code, and be relative to the classpath_.
The part of the code that handles the input is:
public Void run() throws IOException {
LevelReader level = new LevelReader();
List<level> chosenLevels = new ArrayList<>();
if (args.length >= 1) {
File f = new File(args[0]);
if (f.exists()) {
chosenLevels = level.makeLevel(args[0]);
}
}
if (chosenLevels.size() == 0) {
game.runLevels(defaultLevels);
} else {
game.runLevels(chosenLevels);
}
return null;
}
So my question is:
An argument should be the full path of a file which means:
D:\desktop\level3.txt
Is it possible to read a file from every location on my computer?
Because right now I can do it only if my text file is in the
project's directory (not even in the src folder).
I can't understand the rest of their demands. What does is mean "should be hard-coded in your code, and be relative to the
classpath_." and why is it related to InputStream method(?)
I'm confused all over this.
Thanks.
A classpath resource is not the same as a file.
As you have correctly stated, the full path of a file is something like D:\desktop\level3.txt.
But if ever want to distribute your application so it can run on other computers, which probably won’t have that file in that location, you have two choices:
Ask the user to tell the program where to find the file on their computer.
Bundle the file with the compiled program.
If you place a non-.class file in the same place as .class files, it’s considered a resource. Since you don’t know at runtime where your program’s class files are located,¹ you use the getResource or getResourceAsStream method, which is specifically designed to look in the classpath.
The getResource* methods have the additional benefit that they will work both when you are developing, and when the program is packaged as a .jar file. Individual entries in a .jar file are not separate files and cannot be read using the File or FileInputStream classes.
If I understand your assignment correctly, the default level file should be an application resource, and the name of that resource is what should be hard-coded in your program. Something like:
InputStream is;
if (args.length > 0) {
is = new BufferedInputStream(
new FileInputStream(args[0]));
} else {
// No argument provided, so use program's default level data.
is = ClassLoader.getSystemClassLoader().getResourceAsStream("defaultlevel.txt");
}
chosenLevels = level.makeLevel(is);
¹ You may find some pages that claim you can determine the location of a running program’s code using getProtectionDomain().getCodeSource(), but getCodeSource() may return null, depending on the JVM and ClassLoader implementation, so this is not reliable.
To answer your first question, it doesn't seem like they're asking you to read from anywhere on disk, just from within your class path. So that seems fine.
The second question, "What does is mean 'should be hard-coded in your code, and be relative to the classpath'?". You are always going to have a default level file in your project directory. Define the path to this file as a String in your program and that requirement will be satisfied. It's related to the InputStream because the stream requires a location to read in from.

Print Class-Content to Console in java

I'm trying to make a little program from school better, because I am more advanced then the others in my class and want to have a bit fun. It is a simple command line program in java but I want to make it with a full GUI.
So basically I want to access the JAR-File when executed and print the code written in a (by menu selected) class-file. I already know how to find the JAR-File and this works, but I can't find any way to get INTO the JAR-File. I tried creating a File object and putting the path to the JAR combined with the path to the class file I want to access. (Ex: "C:\temp\Test\program.jar\de\bbzsogr\Main.class" as found in WinRAR)
Here is some Code of the "CodeGrabber" class i wrote to access the JAR and then the file in the JAR.
public class CodeGrabber {
private static File JAR;
public static void grabCode(String className) {
try {
JAR = new File(Main.class.getProtectionDomain().getCodeSource().getLocation().toURI().getPath());
} catch (URISyntaxException e) {
e.printStackTrace();
}
System.out.println("JAR is located in: " + JAR);
// -> "JAR is located in: C:\temp\Test.jar"
System.out.println("Searching for \"" + JAR + File.separator + "ch" + File.separator + "bbzsogr" + File.separator + "Main.class");
// -> "Searching for "C:\temp\Test.jar\ch\bbzsogr\Main.class" "
File main = new File(JAR + File.separator + "ch" + File.separator + "bbzsogr" + File.separator + "Main.class");
try {
Scanner scanner = new Scanner(main);
while(scanner.hasNext()) {
System.out.println(scanner.nextLine());
}
} catch (FileNotFoundException e) {
System.out.println("File MAIN not found...");
return;
}
// -> "File MAIN not found..."
}
}
I excepted to get a scrambled mess of data because the file, if I could access it, is still encoded/compiled, but I get the Message, that the wanted file was not found.
Thanks in advance!!
If you want to add and access a jar file inside a java program,you must import the java classes this jar contains and use their methods.You should write something like
import prog.mainclass
at the beginning of your program rather than trying to access it through the Jar.
For what you are asking now,the reason your program can't find the jar is because the path you imported is not valid.Java can't search inside any program but only inside a filesystem.Any path should be without dots like C:/temp/path and can't be,for example C:/temp.csv/path
TLDR: jar entries are not files.
A jar file is a file -- note 'a' meaning 'one'. A jar file is typically created by taking several files (often as many as hundreds, thousands, or more), usually at least some of them java (compiled) class files, (usually) compressing the data from each one, and writing the (compressed) data and name for each file as an entry in the jar. It is possible however for a jar entry not to come from a file; for example the manifest entry is often created 'on the fiy' rather than read from a file, and for a signed jar the signature entries always are. But even for jar entries that are created from files, the jar entries themselves are not files, and cannot be accessed as files using basic pre-NIO file access.
You have three options.
For a jar in the classpath -- which your jar obviously is, since you found it as the source for a loaded class, ClassLoader allows you to read any entry as a 'resource'. This is normally used for things like images, audio, video or other data packed in a jar with an application, but it works on entries that are class files.
// you can invoke it based on a known class like
InputStream is = Main.class.getResourceAsStream("path/for/package/Foo.class");
// or globally
InputStream is = ClassLoader.getResourceAsStream("path/for/package/Foo.class");
jar files are really zip files 'underneath', and Java has long allowed you to access zip files using java.io.ZipFile (directly) or java.io.ZipInputStream (layered on a FileInputStream). The former allows you to access entries 'randomly' using the so-called central directory, while the latter requires you to access entries in the order they occur in the file (and works on nonseekable underlying file forms like pipes and socket connections, but you don't need that here) which makes it a little less convenient for your purpose but still workable. See the javadoc for either.
NIO in Java 7 up (and pretty much everybody should be there by now) adds support for alternate filesystems which provide file-like (or at least stream-like) access to things other than actual files supported by the underlying operating system or its file system(s). And although more can be added, it comes with one alternate provider already installed which handles jars (really zips) -- just as you want.
String jarname = Main.class.getProtectionDomain().getCodeSource().getLocation().getPath();
FileSystem fs = FileSystems.newFileSystem(Paths.get(jarname), null);
InputStream is = Files.newInputStream(fs.getPath("package/Foo.class"));
Note that in all cases I've opened an InputStream, not a Reader (or Scanner). Reader and Scanner are for text consisting of characters, and in most cases lines (which by definition contain characters). Class files have some characters here and there, but are mostly not characters and thus not text; they need to be read and processed as binary (with the few parts that are characters converted if desired). Have fun.

Trying to create a new file throws FileNotFoundException but file exists in the same package [duplicate]

This question already has answers here:
java.io.FileNotFoundException: the system cannot find the file specified
(8 answers)
Closed 4 years ago.
I have a csv file in the same path as everything else. Now, when I try to create a File object:
public void getMenu() {
File fileMenu = new File("FastFoodMenu.csv");
try {
Scanner inputStream = new Scanner(fileMenu);
while (inputStream.hasNext()) {
String data = inputStream.next();
System.out.println(data);
}
} catch (FileNotFoundException ex) {
Logger.getLogger(FileHandler.class.getName()).log(Level.SEVERE, null, ex);
}
}
it throws a FileNotFoundException.
the absolute path to all files in the project is:
C:\Users\kenyo\Documents\NetBeansProjects\OrderFastFood\src\fastfoodorderingsystem
I also checked the name a couple of times. fileMenu.exists() returns false.
First, in your root/working directory (in your case it's the folder containing your project), create a folder called 'menus', here you can store all your menus (so you can play around with multi-file input).
Second, move your FastFoodMenu.csv file to that menus folder.
The FastFoodMenu.csv relative path should now look like this: OrderFastFood\menus\FastFoodMenu.csv.
Third, get your working directory from the System properties. This is the folder in which your program is working in. Then, get a reference (File object) to the menus folder.
Lastly, get a reference to the file in question inside the menu folder. When you get to multi-file reading (and at some point, multi-folder reading), you're gonna want to get the files inside the menu folder as a list so that's why I say to just get the menus folder as it's own reference (or just get the file without the isolated reference to the parent aka '\menus\').
So your code should really look like this:
public void getMenu() {
final File workingDir = File(System.getProperty("user.dir"));
final File menusDir = File(workingDir, "menus");
final File fastFoodMenu = File(menusDir, "FastFoodMenu.csv");
try {
final FileInputStream fis = new FileInputStream(fastFoodMenu);
final BufferedInputStream bs = new BufferedInputStream(fis);
while((l = bs.readLine()) != null) {
System.out.println(l);
}
} catch(FileNotFoundException e) {
System.out.println(e.getMessage());
e.printStackTrace()
}
}
This is all psuedocode but that should at least get you started. Make sure to use BufferedInputStream for efficiency, and when reading files, always pass them into FileInputStream's. It's much better than using the Scanner class. I should also mention that when creating a File object, you're not actually creating a file. What you're doing is your're creating an object, giving it the data you want it to have (such as whether it's a folder, and if it is, what child files/folders do you want it to have, whether it's protected or not, hidden or not, etc) before actually telling the system to create the file with everything else.
Your csv file is probably at the wrong place. You're just specifying the file name, which is a relative path.
Relative paths are always resolved against the working directory of your application, not against the directory where your source file(s) are.
To solve the issue, you can
move the files to the real working directory.
use an absolute path (not advisable!)
specify the folder of your data files as program argument or in a config file (in your working directory)
put the files somewhere into the classpath of your application and load them from there via classloader. Note that files that are in your classpath are usually packed with your application and hence not easily modifiable by the user, so this solution doesn't work if the file must be changed by the user.

Including an Excel file and call once packed into a jar

I have developed an ExcelReader/Writer program that is intended to be ran on other computers. In this program, it takes in an excel.xls of hard data, reads it and writes it in a formatted way to an excel.xlsx file, and then saves it in a directory "CoC-Forms".
To reduce the steps to run my program, I have included the empty .xlsx file that it writes to in the project. However, when exporting it to an executable jar (to run on other computers), I seem to be having trouble accessing this empty form. I know this is probably a very simple answer, but I have been stuck for over a full work day and it has completely halted my progress.
Here is a snippet of my environment. On the left is my Project directory in Intellij (CoC.xlsx is the empty form) and the highlights on the right are where I am attempting to access the file and where the error is happening respectively.
JOptionPane.showMessageDialog(null,"About to look for CoC");
//fileFrom = new File(s + "/out/production/XML Reader/CoC.xlsx");
//fileFrom = new File("/XML Reader/out/production/XML Reader/CoC.xlsx");
//fileFrom = new File("CoC.xlsx");
//fileFrom = new File(ExcelWriter.class.getResource("CoC.xlsx").getPath());
CodeSource src = ExcelWriter.class.getProtectionDomain().getCodeSource();
/*if (src != null) {
URL url = new URL(src.getLocation(), "CoC.xlsx");
fileFrom = new File()
System.out.println(url);
} else {
JOptionPane.showMessageDialog(null,"Failed");
} */
fileFrom = new File(new File("."), "CoC.xlsx");
I solved my problem by having people download the whole project folder, file referencing with a ../../ from point of execution, and then going deeper into the file path to find the location at which I placed the file. Choppy, but it works and solved the problem.

Make the file path not specify drive to make it work universally, no matter where the program is saved

This is my current code
public static void readText() throws FileNotFoundException {
Scanner scan = new Scanner(new File("D:\\BookList\\src\booklist\\Booklist.txt"));
while(scan.hasNextLine()){
String line = scan.nextLine();
System.out.println(line);
}
}
it works fine put it specifies the d drive(it is stored on a usb) and no other folders.
Users could save it anywhere, and the filepath would theoretically be different every time, so my question is how do i start from the Booklist project folder that the program exists in, vs the drive of the comptuer, folders leading up to the project folder(if any), and then in the project folder, to find my file for my program to use.
I'm not sure about your case, but I know in windows batch/cmd that if one were to run a file and have it use the current directory as a variable for file paths you type "cd" (without quotations) and that then specifies the current directory. I do remember it being possible to adjust parameters of the "cd" command so that it returns just the drive letter not the whole path.
I'm sure something of that nature is available for your case.

Categories