How to import .dat file into multiple arrays - java

Alright so I'm working on a program that reads a periodic table and you can search elements based on number or abbreviation.
Anyway, I'm a bit stuck trying to read the periodic table file into 4 different arrays: Atomic Number, Abbreviation, Element Name, and Atomic Weight.
I dunno how to write a single method to import all that info into each array in one go. I want to have a class that holds all these arrays and that I can call to later when I need each one.
Here is what I got so far, I'm a bit rusty by the way... I thought working on this program would refamiliarize me with the basics.
class PeriodicTable{
private String fileName = "periodictable.dat";
private int[] atomicNumTable = new int[200];
private String[] abbreviationTable = new String[200];
private String[] nameTable = new String[200];
private double[] atomicWeightTable = new double[200];
PeriodicTable(String fileName){
readTable(fileName);
}
public int[] readTable(String fileName){
Scanner inFile = null;
try{
inFile = new Scanner(new File(fileName));
}catch(FileNotFoundException nf){
System.out.println(fileName + " not found");
System.exit(0);
}
atomicNumTable = new int[200];
int i = 0;
while(inFile.hasNext() && i < atomicNumTable.length){
int number = inFile.nextInt();
atomicNumTable[i] = number;
i++;
}
inFile.close();
return atomicNumTable;
}
}
Here is what each line of the table looks like:
1 H Hydrogen 1.00794

Simply use java.lang.String.split(' ') (assuming that your columns are separated using spaces; if it is using something else; you just need to adapt that regular expression parameter!)
That will return an array of Strings; and you basically now: first column should be an int, then you got two Strings, and then a double value. Or lets be precise: you get strings, that mean something else; thus you have to look into methods like Integer.valueOf(); and similar for Double.
Shouldn't be too hard to work your way from there.
But I recommend some changes to your logic: having 4 different tables doesn't make sense at all. Good OO programming is about creating helpful abstractions. Without abstractions, your program becomes abstract itself.
Meaning: you should introduce a class like
public class Element {
private final int id;
private final String abbreviation;
private final String fullName;
private final double atomicWeight;
... with one constructor that takes all 4 parameters
... with getter methods for the fields of this class
... and meaningful overrides for equals() and hashcode()
}
And then, instead of creating 4 arrays; you create one array, or even better an ArrayList<Element>. And instead of pushing your 4 values into 4 different arrays, you create one new Element object in each loop iteration; and you add that new object to your list.
The major difference to your solution would be: you can deal with Elements as a whole; whereas in your solution, a single "Element" is basically an index that points into 4 different tables.

You can simplify this code a lot. Try something like this.
1) Read the file line by line, split lines as you go,
add values to some ArrayList containing String[]
2) Close your file
3) Turn the ArrayList into a String[][]
4) Print the result
Also, note that arrays in Java are indexed starting at 0 not at 1.
import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
import java.util.ArrayList;
import java.util.Arrays;
public class Test {
static public void main(String[] args) throws Exception {
File file = new File("periodictable.dat");
FileReader reader = new FileReader(file);
BufferedReader buffReader = new BufferedReader(reader);
String s = null;
ArrayList<String[]> lst = new ArrayList<String[]>();
String[][] res = null;
while((s = buffReader.readLine()) != null){
String[] arr = s.split("[\\s]+");
lst.add(arr);
}
buffReader.close();
res = new String[lst.size()][lst.get(0).length];
res = lst.toArray(res);
System.out.println();
// System.out.println(res);
// String result = Arrays.deepToString(res);
// System.out.println(result);
System.out.println();
for (int i=0; i<res.length; i++){
for (int j=0; j<res[i].length; j++){
System.out.println("res[" + (i+1) + "][" + (j+1) + "]=" + res[i][j]);
}
}
System.out.println();
}
}
OUTPUT:
res[1][1]=1
res[1][2]=H
res[1][3]=Hydrogen
res[1][4]=1.00794
value iterates indexing for each line

You can distinguish four cases in the loop:
i%4 == 0
i%4 == 1
i%4 == 2
i%4 == 3
Depending on this you know the kind of next value you have to read. So, you can search you an integer, string or floating point number and put the value in the right place.
I support the recommendation of GhostCat to only have one array and a class that contains all four values of a line instead of having four arrays.

Related

How to read an empty set from a text file in Java

I have 3 String fields per line within my text file. There are 4 lines in total. The first 2 fields (field[0] and field[1]) are already filled in but field 3 (field[2]) is yet to be generated so it shall remain empty. Is there any way I can read in this text file line by line without getting a java.lang.ArrayIndexOutOfBoundsException: 1 error? I have included my code used for reading in the file.
import java.io.*;
public class PassGen {
public static void main(String args[]) throws Exception{
BufferedReader inKb = new BufferedReader(new InputStreamReader(System.in));
BufferedReader inF = new BufferedReader(new FileReader(new File("students.txt")));
String line = inF.readLine();
int cnt = 0;
Student pupil[] = new Student[6];
while(line != null) {
String field[] = line.split("//s");
pupil[cnt] = new Student(field[0], field[1], field[2]);
cnt++;
inF.readLine();
}
}
}
You can simply add a check on the number of fields:
if(field.length > 2) {
pupil[cnt] = new Student(field[0], field[1], field[2]);
} else {
pupil[cnt] = new Student(field[0], field[1], null);
}
Alternatively, you can use the overloaded split method that takes a limit parameter and set that to -1 to include the empty field. From the documentation of String#split(String regex, int limit):
The limit parameter controls the number of times the pattern is applied and therefore affects the length of the resulting array. If the limit n is greater than zero then the pattern will be applied at most n - 1 times, the array's length will be no greater than n, and the array's last entry will contain all input beyond the last matched delimiter. If n is non-positive then the pattern will be applied as many times as possible and the array can have any length. If n is zero then the pattern will be applied as many times as possible, the array can have any length, and trailing empty strings will be discarded.
Note that you need to use \\s instead of //s for the whitespace regex (this needs to be corrected either way).
String field[] = line.split("\\s", -1);
I think you problem lies in the way you are managing your data, but you can have something like this to read from any array and not getting any exceptions:
public static String getIfExists(final String[] values, final int position) {
return (values != null) && (values.length > position) ? values[position] : null;
}
Then you can fill every field like new Student(getIfExists(field, 0), getIfExists(field, 1), getIfExists(field, 2));
Of course you can optimize this a little bit more...but that would make the trick without having to think on how many fields you might get in the future or having a lot of if/case conditions.

Read and print sequences and print merged sequence

I suppose to write a Java program using array and method follows: It reads a sequence of strings, each on a separate line, and stores them in an array, let call it input1, with one string per cell, in the order they were read. The sequence ends with an empty line: one with a String of length 0. Same thing with 2nd sequence.Then prints the 1st sequence and 2nd sequence. And then create an array that contains all of the elements of the above two arrays. Merging is done by alternating between the arrays: that is, the first cell of input1 is copied followed by the first cell of input2. Then the second cell of input1 is copied followed by the second cell of input2. Of course, in general, the two sequences may have different lengths, so after the shorter sequence is finished, all elements of the longer sequence are simply appended to the output array. Finally, prints the merged array with 1 string each line.
import java.util.Scanner;
public class A4 {
public static void readInput(Scanner myScanner, String[] input) {
boolean streamEnded = false;
int index = 0;
while (!streamEnded && myScanner.hasNext()) {
String value = myScanner.nextLine();
if (value.length() == 0) {
streamEnded = true;
input[index] = value;
} else {
input[index] = value;
index++;
}
}
}
public static void main(String[] args) {
int size = 5;
String[] input1 = new String[size];
String[] input2 = new String[size];
String[] store = new String[size*2];
Scanner aScanner = new Scanner(System.in);
readInput(aScanner, input1);
for (int i = 0; i < input1.length; i++) {
System.out.println("input[" + i +"]" + input1[i]);
}
readInput (aScanner, input2);
for (int i = 0; i < input2.length; i++) {
System.out.println("input[" + i +"]" + input2[i]);
}
}
}
i still dont know how to merge those 2 inputs together.Can anyone show me how to do it? Thanks
Declare three arrays for sequence 1, sequence 2 and merged-sequence.
Use a variable whichToUse to store which array to be used and assign array1 to it before the while loop, then store values into array1 on the place of System.out.print, then when first reach value.length()==0 ('=' is not designed for comparing, it's a mistake in your code.), you change the whichToUse point to array2. When the second reach value.length()==0, end the reading loop. One place to be marked, declare streamEnded as a int to count how many times we reach the value.length()==0. Only exit loop while streamEnded==2.
Now you have two arrays which contains the values from file. Next step is to merge them. Use a for loop to iterate items in merged-sequence, and use loop-counter%2 to determine which array to read when assign value to merged-sequence items. after any of the array1 and array2 reaches the end, read the other array in the rest of loop.
As looks like you are new to Java, I think write code by yourself is much better than I provide the code to you. If you've any other question, just comment here.

Extracting characters from a string and putting into specific arrays by type

I'm new to Java so I'm trying to write random programs to figure it out. I'm trying to write something that takes as user-input a quadratic equation like so: x^2 + 3x -1
Maybe this is too advanced (or maybe it isn't) but I'm wondering how to extract the characters one-by-one in a loop. If it was all digits I think I could use .isDigit() and save them to an array, but because they're different data types I'm not sure how to go about doing this. My 'code' so far looks like this
import java.lang.String;
import java.lang.StringBuffer;
import java.util.Scanner;
import java.lang.Character;
public class Lab
{
public static void main(String[] args)
{
Scanner user_input = new Scanner(System.in);
System.out.print("Please input the quadratic equation (ex: 2x^2 + 3x - 2): ");
String request = user_input.nextLine();
int myArr[];
String lettArr[];
for (int i = 0; i <= request.length(); i++)
{
String c = request.charAt(i);
if (request.isDigit(c))
{
myArr[1] += c;
}
if(request.isLowerCase(c))
{
lettArr[1] += c;
}
}
System.out.println(myArr[0]);
}
}
my .isDigit() and .isLowerCase() methods are not working. I think I'm using them in the right sense. This is pretty complex for my level and I'm wondering if this is a dead-end or an acceptable strategy.
Thanks.
I think what your are trying to do is to extract the coefficients from the user input. Your approach might work but there would be many case that you have to consider (+/- signs for example). Instead why don't you try Java's regular expressions
String input = "2x^2 - 4x + 1";
input = input.replaceAll("\\s", ""); //removes all whitespaces
Pattern p = Pattern.compile("(-?\\d+)x\\^2((\\+|-)\\d+)x((\\+|-)\\d+)");
Matcher m = p.matcher(input);
if (!m.matches()) {
System.out.println("Incorrect input");
return;
}
int a, b, c;
a = Integer.parseInt(m.group(1));
b = Integer.parseInt(m.group(2));
c = Integer.parseInt(m.group(4));
System.out.println(String.format("a=%d, b=%d, c=%d", a, b, c));
You can adapt this fragment and use it in your code. I , however, supposed that your coefficients are integer numbers. If you need them, instead, to be double you have to change the format of the given regex and also to change Integer.parseInt to Double.parseDouble. I could write this in more details if you are interested.
There are a few things wrong with your code:
public class Lab
{
public static void main(String[] args)
{
Scanner user_input = new Scanner(System.in);
System.out.print("Please input the quadratic equation (ex: 2x^2 + 3x - 2): ");
String request = user_input.nextLine();
int myArr[]; //not initialized
String lettArr[]; //should be a character type & not initialized
for (int i = 0; i <= request.length(); i++)
{
String c = request.charAt(i); // returns a char
if (request.isDigit(c))
{
myArr[1] += c; // not right, myArr is ints and c is a char
}
if(request.isLowerCase(c))
{
lettArr[1] += c; // not right
}
}
System.out.println(myArr[0]); //only prints one char (you might want this
}
}
1.
You are extracting a character from the input string and trying to add it to the second entry in an uninitialized array. You're line in code is:
myArr[1] += c;
myArr is an integer array and c is a character. You can't do that in java. What's more, you are trying to add a char to an int, which was not initialized in the first place!! The type of everything in an array must be the same. This gets more complicated when it comes to inheritance and such, but for now just know that you can't do that. If you wanted the Integer value of a character you can use:
Integer.parseInt(c)
I'm not sure what you are trying to do with your statement, but I'm 90% sure that it's not trying to do what you want it to. For reference:
myCharArr[i] = c;
assigns the i-th element (starting from 0) to the value of c. So if i=1 and myCharArr was initialized to 3 elements long, it would look like this:
[ ? | c | ?]
where ? is just a garbage value.
2.
In java you need to initialize your arrays, or use a more dynamic List object. The thing with primitive arrays is that their size cannot change, i.e. when an primitive array is initialized:
int arr[] = new int[5];
it stays the same size (in this case 5). If you use something like an ArrayList, you can add as many things as you want. The way you would initialize ArrayLists would be like:
ArrayList<Integer> intArr = new ArrayList<Integer>();
ArrayList<Character> charArr = new ArrayList<Character();
and with those initialized you can do:
intArr.add(someInt);
charArr.add(someChar);
You can use primitive arrays for this problem but it will save you a bit of trouble if you use Lists.
Read up on arrays.

Java program - Counts all the words from a text file, and counts frequency of each word

I'm a beginner programmer and I'm trying to do one program that opens a text file with a large text inside and then it counts how many words it contains.
Then it should write how many different words are in the text, and write the frecuency of each word in the text.
I had the intention to use one array-string to store all unique words and one int-string to store the frequency.
The program counts the words, but I'm a little bit unsure about how could I write the code correctly to get the list of the words and the frequency them are repeated in the text.
I wrote this:
import easyIO.*;
import java.util.*;
class Oblig3A{
public static void main(String[] args){
int cont = 0;
In read = new In (alice.txt);
In read2 = new In (alice.txt);
while(read.endOfFile() == false)
{
String info = read.inWord();
System.out.println(info);
cont = cont + 1;
}
System.out.println(UniqueWords);
final int AN_WORDS = cont;
String[] words = new String[AN_WORDS];
int[] frequency = new int[AN_WORDS];
int i = 0;
while(les2.endOfFile() == false){
word[i] = read2.inWord();
i = i + 1;
}
}
}
Ok, here is what you need to do:
1. Use a BufferedReader to read the lines of text from the file, one by one.
2. Create a HashMap<String,Integer> to store the word, frequency relations.
3. When you read each line of text, use split() to get all the words in the line of text in an array of String[]
4. Iterate over each word. For each word, retrieve the value from the HashTable. if you get a null value, you have found the word for the first time. Hence, create a new Integer with value 1 and place it back in the HashMap
If you get a non-null value, then increment the value and place it back in the HashMap.
5. Do this till you do not reach EOF.
Done !
You can use a
Map<String, Integer> map = HashMap<String, Integer>();
And then add the words to the map asking if the value is already there. If it is not, add it to the map with a counter initialized to 1.
if(!map.containsKey(word))
{
map.put(word, new Integer("1"));
}
else
{
map.put(word, map.get(word) + new Integer(1));
}
In the end you will have a map with all the words that the file contains and a Integer that represents how many times does the word appear in the text.
You basically need a hash here. In java , you can use a HashMap<String, Integer> which will store words and their frequency.
So when you read in a new word, check it up in the hashMap, say h, and if it exists , increase the frequency or add a new word with frequency = 1.
If you can use a library you may want to consider using a Guava Multiset, it has the counting functionality already built in:
public void count() throws IOException {
Multiset<String> countSet = HashMultiset.create();
BufferedReader bufferedReader = new BufferedReader(new FileReader("alice.txt"));
String line;
while ((line = bufferedReader.readLine()) != null) {
List<String> words = Arrays.asList(line.split("\\W+"));
countSet.addAll(words);
}
bufferedReader.close();
for (Entry<String> entry : countSet.entrySet()) {
System.out.println("word: " + entry.getElement() + " count: " + entry.getCount());
}
}

Java - Most efficient way to convert string to double

Hi I am reading from a text file and saving each line (split by a comma) into an array. The only problem is that most of the elements in the array are double values where as two elements are strings. As a result of this I had to make the array a String[] array. Due to this, whenever I want to perform some equations on the double values in the array, I have to first parse them as a double value. I am literally running 1000+ iterations of these equations, therefore my code is constantly parsing the strings into a double. This is a costly way which is slowing down my program. Is there a better way I can convert the values from the string array to double values or is there a better approach I should take when saving the lines from the text file? Thanks
Here is what one of the arrays looks like after I have read from the text file:
String[] details = {"24.9", "100.0", "19.2" , "82.0", "Harry", "Smith", "45.0"};
I now need to multiply the first 2 elements and add that to the sum of the 3rd, 4th and 7th elements. In other words I am only using the numerical elements (that are ofcourse saved as strings)
double score = (Double.parseDouble(details[0]) * Double.parseDouble(details[1])) + Double.parseDouble(details[2]) + Double.parseDouble(details[3]) + Double.parseDouble(details[6]);
I have to do this for every single line in the text file (1000+ lines). As a result of this my program is running very slowly. Is there a better way I can convert the string values into a double? or is there a better way I should go about storing them in the first place?
EDIT: I have used profiler to check which part of the code is the slowest and it is indeed the code that I have shown above
Here's an example of generating an input file like the one you describe that's 10000 lines long, then reading it back in and doing the calculation you posted and printing the result to stdout. I specifically disable any buffering when reading the file in order to get the worst possible read performance. I'm also not doing any caching at all, as others have suggested. The entire process, including generating the file, doing the calculation, and printing the results, consistently takes around 520-550 ms. That's hardly "slow", unless you're repeating this same process for hundreds or thousands of files. If you see drastically different performance from this, then maybe it's a hardware problem. A failing hard disk can drop read performance to nearly nothing.
import java.io.*;
import java.util.Random;
public class ReadingDoublesFromFileEfficiency {
private static Random random = new Random();
public static void main(String[] args) throws IOException {
long start = System.currentTimeMillis();
String filePath = createInputFile();
BufferedReader reader = new BufferedReader(new FileReader(filePath), 1);
String line;
while ((line = reader.readLine()) != null) {
String[] details = line.split(",");
double score = (Double.parseDouble(details[0]) * Double.parseDouble(details[1])) + Double.parseDouble(details[2]) + Double.parseDouble(details[3]) + Double.parseDouble(details[6]);
System.out.println(score);
}
reader.close();
long elapsed = System.currentTimeMillis() - start;
System.out.println("Took " + elapsed + " ms");
}
private static String createInputFile() throws IOException {
File file = File.createTempFile("testbed", null);
PrintWriter writer = new PrintWriter(new FileWriter(file));
for (int i = 0; i < 10000; i++) {
writer.println(randomLine());
}
writer.close();
return file.getAbsolutePath();
}
private static String randomLine() {
return String.format("%f,%f,%f,%f,%s,%s,%f",
score(), score(), score(), score(), name(), name(), score());
}
private static String name() {
String name = "";
for (int i = 0; i < 10; i++) {
name += (char) (random.nextInt(26) + 97);
}
return name;
}
private static double score() {
return random.nextDouble() * 100;
}
}
You'd do better to create a proper object and store the values in that - this gives you two major benefits, 1) your code will be faster since you avoid needlessly recomputing double values and 2) your code will be clearer, since the fields will be named rather than making calls like details[0] where it's completely unclear what [0] is referring to.
Due to 2) I don't know what the fields are supposed to be, so obviously your class will look different, but the idea's the same:
public class PersonScore {
private double[] multipliers = new double[2];
private double[] summers = new double[3];
private String first;
private String last;
// expects a parsed CSV String
public PersonScore(String[] arr) {
if(arr.length != 7)
throw new InvalidArgumentException("Must pass exactly 7 fields");
multipliers[0] = Double.parseDouble(arr[0]);
multipliers[1] = Double.parseDouble(arr[1]);
summers[0] = Double.parseDouble(arr[2]);
summers[0] = Double.parseDouble(arr[3]);
summers[0] = Double.parseDouble(arr[6]);
first = arr[4];
last = arr[5];
}
public double score() {
double ret = 1;
for(double mult : multipliers)
ret *= mult;
for(double sum : summers)
ret += sum;
return ret;
}
public String toString() {
return first+" "+last+": "+score();
}
}
Notice there's an additional benefit, that the score method is now more robust. Your implementation above hard-coded the fields we wanted to use, but by parsing and storing the fields as structure content, we're able to implement a more readable, more scalable score calculation method.

Categories