I am making a program to help students learning a second language for my school. The main function of this program will be flash cards. What I am having trouble with is figuring out how to store said words and their respective translations. Would an array be the best way to do this? My teacher has talked for a whole two seconds on arrays so I only know the general purpose of them.
Also how would I call those stored, corresponding strings so the user can see them? If there is some better and more efficient way to store them, I would love to find out.
Edit: I also plan to allow the user to input the words through a text field or something of the sort, if that changes the answer.
I think that you should store the said words and their respective translations into a HashMap with said word is key, and value is the translation. And to get the stored, just find by key.
A Map<String, String> would be the perfect data type for mapping one String to another. I would definitely read up on the documentation here if I were you.
For example:
Map<String, String> translatedWords = new HashMap<>();
translatedWords.put("Hello", "Hola");
System.out.println(translatedWords.get("Hello"));
>> "Hola"
It can be taken as a simple one and challenging one also. If you expecting a simple idea then you can use a Map. then store the key as first language word and value as second language word,These are the map implementations
HashMap (if you need more speed then use this implementation)
Map<String,String> translatedValues = new HashMap();
LinkedHahMap (If you need the data as the same oreder of insertion)
TreeMap (Less speed but sorted one)
You could use a 2D array, because it uses rows and columns. Consider the following example, it is Spanish:
String[][] array = new String[][]{
{"You","Tu"},
{"Sandwich","Bocadillo"},
{"You eat a sandwich","Tu comes un bocadillo"}
};
Look at this Javadoc for more info: https://docs.oracle.com/cd/E58500_01/pt854pbh1/eng/pt/tpcr/task_CreatingandPopulatingMulti-DimensionalArrays-071663.html#topofpage
EDIT:
It looks like you are trying to have the user input their own words and translations... Unless you use file reading and writing, this is going to be a really difficult process. This is an example of a way to store translations via file writing, if I learn more about 2D ArrayLists, I'll get back to you.
Scanner sc = new Scanner(System.in);
System.out.println("Please enter a word:");
String word = sc.nextLine();
//You can also use textfields: word = textfield.getText();
System.out.println("Please enter the translation:");
String trans = sc.nextLine();
//Always use UTF-8 if you are using any European or American alphabets
PrintWriter pw = new PrintWriter("lang.txt", "UTF-8");
/*
* You can name the file anyting you want, with any extension:
*"Cake.weirdFile, Juice.lump, Ilike.Chicken"
*it should be readable by Java as long as you use UTF-8
*/
pw.print(word);
pw.println(trans);
pw.close();
//Make sure to catch/throw FileNotFoundException & UnsupportedEncodingException
And you can read the file for flash card quizzes/reviews:
Scanner sc2 = new Scanner(new File("lang.txt"));
//Make sure to catch/throw FileNotFoundException
String[] wordAndTrans = sc2.nextLine().split(" ");
sc2.close();
System.out.println("The translation of '"
+ wordAndTrans[0] + "' is: '" + wordAndTrans[1] + "'");
Although this code works, I wouldn't suggest copy/pasting this because the way it is laid out might not be compatible with your code. This is merely an example usage of Scanners, PrintWriters, and String splitting so you can save your translations each to their own line in a file.
ResourceBundle
Also known as properties files
Don't clutter your code with Hashmaps and arrays. You also can have additional translations without editing any Java code
A properties file is a simple text file. You can create and maintain a properties file with just about any text editor
# This is the default LabelsBundle.properties file
s1 = computer
s2 = disk
s3 = monitor
s4 = keyboard
To support an additional Locale, your localizers will create a new properties file that contains the translated values. No changes to your source code are required, because your program references the keys, not the values.
# This is the LabelsBundle_de.properties file
s1 = Computer
s2 = Platte
s3 = Monitor
s4 = Tastatur
Use
Locale currentLocale;
ResourceBundle labels = ResourceBundle.getBundle("LabelsBundle", currentLocale);
String computer = labels.getString("s1");
Related
I have a problem where I want to scan the files that are in a certain folder and output them.
the only problem is that the output is: (1.jpg , 10.jpg , 11.jpg , 12.jpg , ... , 19.jpg , 2.jpg) when I want it to be: (1.jpg , 2.jpg and so on). Since I use: File actual = new File(i.); (i is the number of times the loop repeats) to scan for images, I don't know how to sort the output.
this is my code for now.
//variables
String htmlHeader = ("<!DOCTYPE html>:\n"
+ "<html lang=\"en\">\n"
+ "<head>\n"
+ "<meta charset=\"UTF-8\">\n"
+ "<meta http-equiv=\"X-UA-Compatible\" content=\"IE=edge\">\n"
+ "<meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\n"
+ "<title>Document</title>\n"
+ "</head>"
+ "<body>;\n");
String mangaName = ("THREE DAYS OF HAPPINESS");
String htmlEnd = ("</body>\n</html>");
String image = ("image-");
//ask for page number
Scanner scan = new Scanner(System.in);
System.out.print("enter a chapter number: ");
int n = scan.nextInt();
//create file for chapter
File creator = new File("manga.html");
//for loop
for (int i = 1; i <= n; ++i) {
//writing to HTML file
BufferedWriter bw = null;
bw = new BufferedWriter(new FileWriter("manga"+i+".html"));
bw.write(htmlHeader);
bw.write("<h2><center>" + mangaName + "</center></h2</br>");
//scaning files
File actual = new File("Three Days Of Happiness Chapter "+i+" - Manganelo_files.");
for (File f : actual.listFiles()) {
String pageName = f.getName();
//create list
List<String> list = Arrays.asList(pageName);
list.sort(Comparator.nullsFirst(Comparator.comparing(String::length).thenComparing(Comparator.naturalOrder())));
System.out.println("list");
//for loop
//writing bpdy to html file
bw.write("<p><center><img src=\"Three Days Of Happiness Chapter "+i+" - Manganelo_files/" + pageName + "\" <br/></p>\n");
System.out.println(pageName);
}
bw.write(htmlEnd);
bw.close();
System.out.println("Process Finished");
}}
}```
When you try to sort the names, you'll most certainly notice that they are sorted alphanumerically (e.g. Comparing 9 with 12; 12 would come before 9 because the leftmost digit 1 < 9).
One way to get around this is to use an extended numbering format when naming & storing your files.
This has been working great for me when sorting pictures, for example. I use YYYY-MM-DD for all dates regardless whether the day contains one digit (e.g. 9) or two digits (11). This would mean that I always type 9 as 09. This also means that every file name in a given folder has the same length, and each digit (when compared to the corresponding digit to any other adjacent file) is compared properly.
One solution to your problem is to do the same and add zeros to the left of the file names so that they are easily sorted both by the OS and by your Java program. The drawback to this solution is that you'll need to decide the maximum number of files you'll want to store in a given folder beforehand – by setting the number of digits properly (e.g. 3 digits would mean a maximum of 1000 uniquely & linearly numbered file names from 000 to 999). The plus, however, is that this will save you the hassle of having to sort unevenly numerered files, while making it so that your files are pre-sorted once and are ready to be quickly read whenever.
Generally, file systems do not have an order to the files in a directory. Instead, anything that lists files (be it an ls or dir command on a command line, calling Files.list in java code, or opening Finder or Explorer) will apply a sorting order.
One common sorting order is 'alphanumerically'. In which case, the order you describe is correct: 2 comes after 1 and also after 10. You can't wave a magic wand and tell the OS or file system driver not to do that; files as a rule don't have an 'ordering' property.
Instead, make your filenames such that they do sort the way you want, when sorting alphanumerically. Thus, the right name for the first file would be 01.jpg. Or possibly even 0001.jpg - you're going to have to make a call about how many digits you're going to use before you start, unfortunately.
String.format("%05d", 1) becomes "00001" - that's pretty useful here.
The same principle applies to reading files - you can't just rely on the OS sorting it for you. Instead, read it all into e.g. a list of some sort and then sort that. You're going to have to write a fairly funky sorting order: Find the dot, strip off the left side, check if it is a number, etc. Quite complicated. It would be a lot simpler if the 'input' is already properly zero-prefixed, then you can just sort them naturally instead of having to write a complex comparator.
That comparator should probably by modal. Comparators work by being handed 2 elements, and you must say which one is 'earlier', and you must be consistent (if a is before b, and later I ask you: SO, how about b and a, you must indicate that b is after a).
Thus, an algorithm would look something like:
Determine if a is numeric or not (find the dot, parseInt the substring from start to the dot).
Determine if b is numeric or not.
If both are numeric, check ordering of these numbers. If they have an order (i.e. aren't identical), return an answer. Otherwise, compare the stuff after the dot (1.jpg should presumably be sorted before 1.png).
If neither are numeric, just compare alphanum (aName.compareTo(bName)).
If one is numeric and the other one is not, the numeric one always wins, and vice versa.
So basically, for this assignment I'm working on, we have to read in from a huge file of about a million lines, store the keys and values in a data structure of our choice (I'm using hash tables), offer functionality to change values for keys, and then save the key value stores back into a file. I'm using the cuckoo hashing method along with a method I found from a Harvard paper called "stashing" to accomplish this, and I'm fine with all of it. My only concern is the amount of time it is taking the program just to read in the data from the file.
The file is formatted so that each line has a key (integer) and a value (String) written like this:
12345 'abcdef'
23456 'bcdefg'
and so on. The method I have come up with to read this in is this:
private static void readData() throws IOException {
try {
BufferedReader inStream = new BufferedReader(new FileReader("input/data.db"));
StreamTokenizer st = new StreamTokenizer(inStream);
String line = inStream.readLine();
do{
String[] arr = line.split(" ");
line = inStream.readLine();
Long n = Long.parseLong(arr[0]);
String s = arr[1];
//HashNode<Long, String> node = HashNode.create(n, s);
//table = HashTable.empty();
//table.add(n, s);
}while(line != null);
} catch (FileNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
The method works fine for actually getting the data, however I tested it with our test file of a million lines and it took about 20 minutes for it to get all the way through reading this all in. Surely, this isn't a fast time for reading in data from a file, and I am positive there must be a better way of doing it.
I have tried several different methods for input (BufferedInputStream with FileInputStream, using Scanner however the file extension is .db so Scanner didn't work, I initially didn't have the tokenizer but added it in hopes it would help). I don't know if the computer I'm running it on makes much of a difference. I have a MacBook Air that I am currently doing the run on; however, I am having a mate run it on his laptop in a bit to see if that might help it along. Any input on how to help this or what I might be doing to slow things SO much would be sincerely and greatly appreciated.
P.S. please don't hate me for programming on a Mac :-)
You can use "java.nio.file.*", the following code is written in Java 8 style but can be easily modified to earlier versions on Java if needed:
Map<Long, String> map = new HashMap<>();
Files.lines(Paths.get("full-path-to-your-file")).forEach(line -> {
String[] arr = line.split(" ");
Long number = Long.parseLong(arr[0]);
String string = arr[1];
map.put(number, string);
});
There is an additional performance gain since Files.lines(..).forEach(...) is executed in parallel. Which means that the lines will not be in-order (and in our case - you don't need it to), in case you needed it to be in order you could call: forEachOrdered().
On my MacBook it took less than 5 seconds to write 2 million such records to a file and then read it and populate the map.
Get rid of the StreamTokenizer. You can read millions of lines per second with BufferedReader.readLine(), and that's all you're really doing: no tokenization.
But I strongly suspect the time isn't being spent in I/O but in processing each line.
NB Your do/while loop is normally written as a while loop:
while ((line = in.readLine()) != null)
Much clearer that way, and no risk of NPEs.
I read a dictionary that might be 100MB or so in size (sometimes gets bigger up to max 500MB). It is a simple dictionary of two columns, the first column words the second column a float value. I read the dictionary file it in this way:
BufferedReader br = new BufferedReader(new FileReader(file));
String line;
while((line = br.readLine()) != null) {
String[] cols = line.split("\t");
setIt(cols[0], cols[1]);
and for the setIt function:
public void setIt(String term, String value) {
all.put(term, new Double(value));
}
When I have a big file, it takes a long time to load it and it often goes out of memory. Even with a reasonable size file (100MB) it does need a 4GB memory in Java to be run.
Any clue how to improve it while not changing the structure of the whole package?
EDIT: I'm using a 50MB file with -Xmx1g and I still get the error.
UPDATE: There were some iterations over the file that I fixed them and now the memory problem was partially solved. Yet to try the properties and other solutions and report on that.
You are allocating a new String for every line. There is some overhead associated with a String. See Here for a calculation. This article also addresses the subject of object memory use in java.
There is a stack overflow question on the subject of more memory efficient replacements for strings here.
Is there something you can do to avoid all those allocations? For example, are there a limited number of strings that you could represent as an integer in your data structure, and then use a smaller lookup table to translate?
You can do a lot of things to reduce memory usage. for example :
1- replace String[] cols = line.split("\t"); with :
static final Pattern PATTERN = Pattern.compile("\t");
//...
String[] cols = PATTERN.split(line);
2- use .properties file to store your dictionary and simply load it this way :
Properties properties = new Properties();
//...
try (FileInputStream fileInputStream = new FileInputStream("D:/dictionary.properties")) {
properties.load(fileInputStream);
}
Map<String, Double> map = new HashMap<>();
Enumeration<?> enumeration = properties.propertyNames();
while (enumeration.hasMoreElements()){
String key = (String) enumeration.nextElement();
map.put(key, new Double(properties.getProperty(key)));
}
//...
dictionary.properties :
A = 1
B = 2
C = 3
//...
3- use StringTokenizer :
StringTokenizer tokenizer = new StringTokenizer(line, "\t");
setIt(tokenizer.nextToken(), tokenizer.nextToken());
Well my solution will deviate little bit from your code ...
Use Lucene or more specifically Lucene Dictionary or even more specifically Lucene Spell Checker depends upon what you want.
Lucene handle any amount of data with efficient memory usage ..
Your problem is that you are storing whole Dictionary in memory ... Lucene store it in file with hashing and then it take search result from file at runtime but efficiently. This save lot of memory. You can customize search depends upon your needs
Small Demo of Lucene
A few causes for this problem would be.
1). The String array cols is using up too much memory.
2). The String line might also be using too much memory, unlikely though.
3). While java is opening and reading the file its also using memory so that's also a probability.
4). Your map put will also be taking up a small amount of memory.
It might also be all these things combined, so maybe try and comment some lines out and see if works then.
The most likely cause is all these things added up is eating your memory. So a 10 megabyte file could end up being 50 megabytes. Also make sure to .close() all input steams and try to reallocate ram by splitting up your methods so variables get garbage collected.
As for doing this without changing package structure or java heap size arguments i'm not sure it will be very easy, if possible at all.
Hope this helps.
I have an app that will create 3 arrays : 2 with double values and one with strings that can contain anything,alphanumeric,commas,points,anything the user might want to type or type by accident. The double arrays are easy.The string one i find to be tricky.
It can contain stuff like cake red,blue 1kg paper-clip,you get the ideea.
I will need to store those arrays somehow(i guess in a file is the easiest way),read them and get them back into the app whenever the user wants to.
Also,it would be well if they wouldn't be human readable,to only be able to read them thru my app.
What's the best way to do this ? My issue is,how can i read them back into arrays.Its easy to write to a file but then to get them back in the same array i put them in...How can i separate array elements for it not to split one element in two because it has a space or any other element.
Can i like,make 3 rows of text,each element split by a tab \t or something and when i read it each element will by split by that tab ? Will this be able to create any issues when reading ?
I guess i want to know how can i split the elements of the array so that it won't be able to ever read them wrong.
Thanks and have a nice day !
If you don't want the file to be human readable, you could usejava.io.RandomAccessFile.
You would probably want to specify a maximum string size if you did this.
To save a string:
String str = "hello";
RandomAccessFile file = new RandomAccessFile(new File("filename"));
final int MAX_STRING_BYTES = 100; // max number of bytes the string could use in the file
file.writeUTF(str);
file.skipBytes(MAX_STRING_BYTES - str.getBytes().length);
// then write another..
To read a string:
// instantiate again
final int STRING_POSITION = 100; // or whichever place you saved it
file.seek(STRING_POSITION);
String str = new String(file.read(MAX_STRING_BYTES));
You would probably want a use the beginning of the file to store the size of each array. Then just store all the values one by one in the file, no need for separators.
I'm writing a personal program that will help my Dnd group and at the same time expand my java knowledge a little :) now part of that involves some arrays, and loading text into them from a text file. Now I have succeeded in that and with them all set statically it's all fine, since the end result will have lots of arrays I thought rather than making each array do all the leg work itself I would create an array handler method.
So I would do filetest(filename,arrayName) (ie filetest(table1,table1Array)
and it would make the array, but I'm stuck on one thing: How do I make the array using the name from arrayName?
It's pretty much got me stumped my so far failed code is:
public class arrayFileHandler {
public static void fileTest(String fileName,String arrayName) throws FileNotFoundException{
int a = 0;
System.out.println("test");
System.out.println(System.getProperty("user.dir")); //this is the folder where the file SHOULD be
Scanner testTable1 = new Scanner(new File("data/"+fileName+".txt")); //with luck this will load the file ! if i understand + correct!
//Scanner testTable1 = new Scanner(new File("C:/Dev/newjava/dnd/src/dnd/test.txt")); //this works but is no good for our needs
ArrayList<String> testTable = new ArrayList<String>(); //create the array list since at this stage we dont know how long the array will be
while(testTable1.hasNextLine()){ //see if the file we are using has a next line (could cause me issues if the txt has blank lines...hmmm)
String line = testTable1.nextLine(); //put that line into the string "line"
testTable.add(line); //add that line to the array list
System.out.println(line); //lets see what that line says
a++; //to help count the lines(not needed now)
speechHandler.speechSynth(2, 1, 0, 60, line); //a debug line
}
System.out.println("there are "+ a +" lines"); //print how many lines there are
String arrayName[] = new String [testTable.size()]; //create an array with the number of "slots" equal to the number of slots in the arraylist and named with the String in arrayName
arrayName = testTable.toArray(arrayName);//copy the arraylist to the array
System.out.println(arrayName[0]);
//System.out.println(tableList[2]);
speechHandler.speechSynth(2, 1, 0, 60, arrayName[2]); //also a debug line
}
Now the important line is String arrayName[] = new String [testTable.size()]; it's trying to create an array called arrayName, but I need it to be created with whatever name is in the string called arrayName, so in my example in the second paragraph it would be called table1Array.
Googling hasn't helped me much and I'm wondering if what I want to do is actually possible.
It's not possible and it also doesn't make any sense in your case. What's your goal? You're just creating a local variable, it doesn't matter which name did you choose. It'd only get complicated, because you'd need to access it using another variable (containing the name). I really don't see any usage in this.
If you were using some dynamic interpreted language, it could be done by using something like "eval", which would create variable with defined name at runtime. But in the Java, all code (including all variables etc) is compiled into bytecode and then executed. Do you see the problem? At compile time, the variable might not be recognized (because of missing name), and therefore it's not possible. Truth is, that bytecode doesn't contain local variable's names, but why JVM would have to solve issues like "isn't there already a variable with such a name" and so on? Bytecode would only get bigger, without actually bringing some new functionality.
If you really need this for some reason (and I just can't imagine which is that), I'd suggest you to use some sort of associative array, e.g. java.lang.Hashtable<>. It allows you to change names runtime.
From you last comment, if I understand right, you want to create arrays of strings based on the contents of text files.
You started with reading the lines into an ArrayList of Strings. I suggest you stick with ArrayLists, and not bother with arrays.
If you are going to read lots of files, I suggest you put your code which creates the ArrayLists into a method so you can call it as many times as you want, once for each file.
Below is such a function, copied from your code and modified where necessary (I removed the comments):
Here goes:
public List<String> getLinesFromFile(String filename) {
Scanner testTable1 = new Scanner(new File(filename));
List<String> testTable = new ArrayList<String>();
while(testTable1.hasNextLine()){
String line = testTable1.nextLine();
testTable.add(line);
}
return testTable;
}
You can call that as many times as you like. If you have lots of files, you can 'name' them the same as your filename by storing them in a Map:
First, create a Map whose keys are Strings (so we can look up by filename) and whose values are List (lists of strings - the content of the files)
Map<String, List<String>> fileContentMap = new HashMap<String, List<String>>();
Next, every time we read a file, add it to the Map:
String filename = // ... whatever file to read next
fileContentMap.put(filename, getLinesFromFile(filename));
Finally, when you want to retrieve them:
//retrieve lines from a file I read earlier:
List<String> lines = fileContentMap.get(filename);