Continuing on from my previous
explorations that followed Learn to Code Grand Rapids “Building a Real World App in VS 2017, Part I”
of August 10 I decided to try Java.
Java, like Python, is a programming language that I hadn’t tried before
so provided something for me to do.
In my previous Max Column Sum
by Key document I described my initial C# attempt before I had the file
containing the data that was aborted for that reason, then my application using
Ada as the language after getting the data file, then with C# once again using
the data file, and finally with Python as a new language for me. The different languages to show the
similarities and the differences since I followed the same general outline in
each language.
Continuing from that, as
something to do, I downloaded Java to do the same application in that
language. These notes will provide some
comments about Java along with the code.
Then, based upon how I was able
to use a Java class as a record structure, I revisited C# to determine whether
I could do the same with it since I had previously had a problem as described
in the initial document. A brief
discussion of the C# application follows that of Java.
Discussion of the Java Application
As described in the discussion of the Ada application in the
previous write up, the Java application reads the file supplied by Jeffrey
Fuller. As I had discovered it had
records with extraneous text prior to the Key and two Value fields (or what I
assume are both Value fields).
Examining the data I found that each record only had a trailing NL (new
line) so not a DOS formatted file that would also have a carriage return
(CR). And that the final record was
without the trailing NL. Preceding the
Key and each of the two Value fields was a HT (horizontal tab). That is, the first two records look like
49 44 57 49 50 95 78 85 77 9 49 48 48 48 9 49 9 49
10
49 44 57 49 50 95 78 85 77 9 49 48 48 48 9 50 9 49
10
which translate to ASCII as below.
1 ,
9 1 2 _ N
U M HT 1 0
0 0 HT 1 HT 1 NL
1 ,
9 1 2 _ N
U M HT 1 0
0 0 HT 2 HT 1 NL
where the numbers below are the byte positions.
1 2
3 4 5 6 7
8 9 10 11 12 3 14 15 6 17 18 19
Therefore I decided to find the Key with the greatest number
of combined value fields of a particular Value. In the two record sample above this would be a value of 1 since
there are three instances of 1 and only one instance of 2 for the Key of 1000.
As with the use of the other languages, I first opened and
read the file into a buffer. As
mentioned in the previous document, this would require a different method if
the file had been too large to completely read at the start. Perhaps I’ll do another version to show a
way how this could be accomplished. In
any case, starting with a language new to me, this provided the a necessary
starting point for how data files might be accessed in Java. The code to accomplish this is as follows
where the initial few lines of code were eventually followed by other necessary
code that will be shown later.
Note: Indentation
of four columns was used because that was the “standard” for Python and not due
to any necessity for indentation in Java.
Also, print statements that were included for debugging have been
commented out to avoid extraneous output in the completed application.
import java.io.*;
import
java.io.IOException;
public class LearnToCodeGR
{
. . . code added later
public static void main(String args[]) throws IOException
{
// Open the file.
File file = new File(
"C:\\Source\\LearnToCodeGR\\max-col-sum-by-Key.tsv" );
FileInputStream inputFile = null;
try
{
// create FileInputStream object
inputFile = new FileInputStream(file);
byte buffer[] = new byte[(int)file.length()];
// Read from this input stream into an array of bytes
inputFile.read(buffer);
// Create string from byte array
// String s = new String(buffer);
// System.out.println("File
content: " + s);
// Parse the buffer and build data structure
Parse( buffer.length, buffer );
// Report the results
Report();
}
catch (FileNotFoundException e)
{
System.out.println("File not found" + e);
}
catch (IOException ioe)
{
System.out.println("Exception while reading file
" + ioe);
}
finally
{
// Close the stream using close method
try
{
if (inputFile != null)
{
inputFile.close();
}
}
catch (IOException ioe)
{
System.out.println("Error while closing
stream: " + ioe);
}
}
} // end method main
} // end class
LearnToCodeGR
The invocations of the Parse
and Report functions were added later since the initial attempt was only to
discover how to open and read a data file.
(I started on September 7 and it took a couple of days to download the
Java compiler for Windows and discover that I needed to add the Windows system
path so as to run the compiler executable without needing to point to it in the
Command Prompt (e.g., DOS) window, to be able to run a Helloworld application
found on the internet, and then do the initial code shown above.
Java, like Python, doesn’t seem
to have a compiler that runs in a Windows window. So it has to be run in a Command Prompt window. I, as usual, changed the folder in the
window to that where I was developing the application (c:\Source\Java) so the
command was just
> javac
LearnToCodeGR.java
since LearnToCodeGR.java was
the file name that I used and javac.exe is the java compiler.
During this time I also
discovered that to attempt to run the application I had to use the command
> java –classpath
C:\source\java LearnToCodeGR
since the LearnToCodeGR class
as shown in the above code sample was also in the same Windows folder. If there had been classes located elsewhere
they also would have had to have been named following the classpath
parameter. (In Ada, for instance, a
project is first defined that specifies where the various source and user
libraries are to be found.)
After quickly getting this far
the addition of the Parse function was added (the Report function was done
last). The Parse function was done for
only a few data records of the buffer to start with to determine how to
implement it in Java while following the method developed in the previous
languages. (The completed application
was done in parts of three days following a long weekend.) Of course, the only thing to learn was how
to implement already working code in Java.
And, how to do a class that represented the data structure that I wanted
to use to keep track of the parsed key and its values as I had wanted to do in
C#.
The Parse function is similar
to that of the application in the other languages and is shown below with its
debug print statements commented out.
// Parse array of data as previously read from file to
// obtain Key and two Values from each record of file
private static void Parse(int count, byte[] data)
{
Byte NINE = 57;
Byte ZERO = 48;
Byte TAB = 9;
Byte CR = 13;
Byte LF = 10;
String keyString = "";
String value1String = new String("");
String value2String = new String("");
int offset = 0; // offset into data
Scan scanPhase = Scan.BYPASS;
// Parse all bytes previously read from the file
while (offset < count)
{
// System.out.println(" " +
offset);
switch (scanPhase)
{
case BYPASS:
// String xyz = "bypass
";
// xyz =
xyz.concat(Integer.toString(offset));
// xyz = xyz.concat("
");
// xyz =
xyz.concat(Byte.toString(data[offset]));
// System.out.println(xyz);
//"bypass");
if (data[offset] == TAB)
{
scanPhase = Scan.FKEY;
//
System.out.print("new phase of Key");
// Initialize for next set of data
keyString = "";
value1String = "";
value2String = "";
}
break;
case FKEY:
// System.out.println("Key");
if (data[offset] != TAB)
{
if ((data[offset] >= ZERO) &
(data[offset] <= NINE))
{
char digit = (char)data[offset];
String dataDigit =
Character.toString(digit);
keyString =
keyString.concat(dataDigit);
}
}
else
{
scanPhase = Scan.VALUE1;
//
System.out.print("new phase of Value1");
}
break;
case VALUE1:
//
System.out.println("Value1");
if (data[offset] != TAB)
{
if ((data[offset] >= ZERO) &
(data[offset] <= NINE))
{
char digit = (char)data[offset];
String dataDigit = Character.toString(digit);
value1String =
value1String.concat(dataDigit);
}
}
else
{
scanPhase = Scan.VALUE2;
// System.out.print("new
phase of Value2");
}
break;
case VALUE2:
//
System.out.println("Value2");
if ((data[offset] == LF) | (data[offset] ==
CR))
{
// Update tables with the data from the
record.
Update( keyString, value1String,
value2String );
// Initialize for next record
keyString = "";
value1String = "";
value2String = "";
scanPhase = Scan.BYPASS;
//
System.out.print("new phase of bypass");
}
else
{
if ((data[offset] >= ZERO) &
(data[offset] <= NINE))
{
char digit = (char)data[offset];
String dataDigit = Character.toString(digit);
value2String = value2String.concat(dataDigit);
}
}
break;
} // end switch
// Complete processing of final record when not
terminated by New Line
offset++; //
increment to next data buffer position
if (offset >= count) // no more data
{
if (value2String.length() > 0) // last record
fully parsed without trailing NL
{ Update(
keyString, value1String, value2String ); }
break; // exit loop
}
} // end loop
return;
} // end Parse
A curiosity in the above is in
the use of enumerated Scan type
public enum Scan
{ BYPASS, FKEY, VALUE1, VALUE2 }
that was declared outside of all the declared classes
(except the all encompassing LearnToCodeGR class). It took me a while to learn that the case statements of the
switch statement had to only use the particular enumerated name whereas in
setting a scanPhase the dot notation had to be used. That is,
switch (scanPhase)
{
case BYPASS:
versus scanPhase = Scan.BYPASS;
As can be seen, in this version
of the application, the possibility of a non-numeric Key or Value is prevented
by only adding numeric characters to the string object that is being
created. The other implementations
instead did this checking in a conversion function that converted a byte array
to a numeric value. In this
application, since there can be no non-digits in the string, the Java supplied
function to convert a string to a number can be invoked as is done in the
Update function.
As done in the other
applications, the Update function in invoked when the second value has been
parsed. The Update code, with its debug
statements commented out, is as follows.
private static void Update(String key, String value1, String
value2)
{
// Convert strings to
integers. Already have avoided any
non-digits
// in the strings.
int nKey = Integer.parseInt(key);
int nValue1 = Integer.parseInt(value1);
int nValue2 = Integer.parseInt(value2);
//String yyy =
"Update ";
//yyy = yyy.concat(key);
//yyy = yyy.concat("
");
//yyy =
yyy.concat(value1);
//yyy = yyy.concat("
");
//yyy =
yyy.concat(value2);
//yyy = yyy.concat("
");
//yyy =
yyy.concat(Integer.toString(nKey));
//yyy = yyy.concat("
");
//yyy =
yyy.concat(Integer.toString(nValue1));
//yyy = yyy.concat("
");
//yyy =
yyy.concat(Integer.toString(nValue2));
//System.out.println(yyy);
int keyIndex = -1;
int valueIndex = -1;
//int numKeys =
keyTable.getKeyCount();
//String yaa = "Key
Count ";
//yaa =
yaa.concat(Integer.toString(numKeys));
//System.out.println(yaa);
for (int i = 0; i < keyTable.getKeyCount(); i++)
{
if (keyTable.getKey(i) == nKey)
{
keyIndex = i;
// key already in table
break; // exit loop
}
} // end loop
if (keyIndex < 0) // key not in the table
{
keyIndex = keyTable.setKey(nKey); // add key
valueIndex = keyTable.setValue(keyIndex,nValue1); //
add value for key
if (nValue1 != nValue2)
{ keyTable.setValue(keyIndex,nValue2); }
else
{ keyTable.incSum(keyIndex,valueIndex); }
}
else // key in the table
{
int valueCount = keyTable.getValueCount(keyIndex);
//String yab = "Value
Count ";
//yab =
yab.concat(Integer.toString(valueCount));
//System.out.println(yab);
valueIndex = keyTable.setValue(keyIndex,nValue1); //
add value for key
if (nValue1 != nValue2)
{ keyTable.setValue(keyIndex,nValue2); }
else
{ keyTable.incSum(keyIndex,valueIndex); }
}
return;
} // end Update
It first converts the strings
for the key and the two values to integers and then uses the KeyTable class, as
instantiated to the keyTable object, to save the data. Like the enumerated type, the instatiation
is done only within the applications main LearnToCodeGR class via the
static public KeyTable keyTable = new
KeyTable(); // visible instance of KeyTable class
statement. As will be seen below, the KeyTable class
has a constructor that is executed by the above statement.
// Key class
static class Key
{
int key; //
value of key
int valueCount; // number of different values associated
with key
int[] values = new int[20]; // up to 20 different values associated
with
int[] sums = new int[20];
// key and number of instances
of value
}
// KeyTable class
static class KeyTable
{
public int keyCount; // number of different keys
public Key[] keys = new Key[30]; // instances of
"struct" class
public KeyTable() // constructor
{
this.keyCount = 0;
// Instantiate pointer for each instance of
"struct" class
for (int i=0; i<30; i++)
{
keys[i] = new Key();
}
} // end constructor
// getters
\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\
// Return key at index
public int getKey(int index)
{
//String xxx =
"getKey index ";
//xxx =
xxx.concat(Integer.toString(index));
//xxx = xxx.concat("
keyCount ");
//xxx =
xxx.concat(Integer.toString(keyCount));
//xxx = xxx.concat("
key ");
//xxx =
xxx.concat(Integer.toString(this.keys[index].key));
//System.out.println(xxx);
return this.keys[index].key;
} // end getKey
// Return number of keys in table
public int getKeyCount()
{
return keyCount;
} // end getKeyCount
// Return number of different values for key at index
public int getValueCount(int index)
{
return this.keys[index].valueCount; //[index];
} // end getValueCount
// setters
\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\
// Store new key into table and return index of key in the
table
public int setKey(int key)
{
for (int i = 0; i < keyCount; i++)
{
//String xx = "setKey
search ";
//xx =
xx.concat(Integer.toString(i));
//xx = xx.concat("
");
//xx =
xx.concat(Integer.toString(this.keys[i].key));
//System.out.println(xx);
if (this.keys[i].key == key) // key already in
table
{
return i;
}
} // end for loop
if (this.keyCount < 30) // add key to table if room
{
Key cKey =
new Key();
this.keys[keyCount].key = key;
int index = keyCount;
keyCount++;
//String xxx =
"setKey add ";
//xxx = xxx.concat(Integer.toString(index));
//xxx = xxx.concat("
");
//xxx =
xxx.concat(Integer.toString(key));
//System.out.println(xxx);
return index;
}
else
{
System.out.println("Error: Too many different
keys");
return -1;
}
} // end setKey
// Add value for key at index, return value index
public int setValue(int index, int value)
{
//String bbb =
"setValue index ";
//bbb = bbb.concat(Integer.toString(index));
//bbb = bbb.concat("
value ");
//bbb =
bbb.concat(Integer.toString(value));
//bbb = bbb.concat("
valueCount ");
//bbb =
bbb.concat(Integer.toString(this.keys[index].valueCount));
//bbb = bbb.concat("
key ");
//bbb = bbb.concat(Integer.toString(this.keys[index].key));
//System.out.println(bbb);
for (int i = 0; i < this.keys[index].valueCount;
i++)
{
if ( this.keys[index].values[i] == value )
{
// Increment sum
this.keys[index].sums[i]++;
//String xxx =
"setValue already exists ";
//xxx =
xxx.concat(Integer.toString(keyCount));
//xxx = xxx.concat("
");
//xxx =
xxx.concat(Integer.toString(this.keys[index].key));
//xxx = xxx.concat("
");
//xxx =
xxx.concat(Integer.toString(i));
//xxx = xxx.concat("
Value ");
//xxx =
xxx.concat(Integer.toString(this.keys[index].values[i]));
//xxx = xxx.concat("
Sum ");
//xxx =
xxx.concat(Integer.toString(this.keys[index].sums[i]));
//System.out.println(xxx);
return i;
}
} // end for loop
// Add value to array for key since didn't return for
existing value
this.keys[index].values[this.keys[index].valueCount] =
value;
this.keys[index].sums[this.keys[index].valueCount]++;
//String xxx =
"setValue add to table";
//xxx =
xxx.concat(Integer.toString(index));
//xxx = xxx.concat("
key ");
//xxx =
xxx.concat(Integer.toString(this.keys[index].key));
//xxx = xxx.concat("
valueCount ");
//xxx =
xxx.concat(Integer.toString(this.keys[index].valueCount));
//xxx = xxx.concat("
Sum ");
//xxx =
xxx.concat(Integer.toString(this.keys[index].sums[this.keys[index].valueCount]));
//System.out.println(xxx);
int valueIndex = this.keys[index].valueCount;
this.keys[index].valueCount++; // increment number of
values for key
return valueIndex;
} // end setValue
// Get value for key at indexes, return value
public int getValue(int indexKey, int indexValue)
{
return this.keys[indexKey].values[indexValue];
} // end getValue
// Increment Sum for value of key at indexKey and value at
indexValue
public void incSum(int indexKey, int indexValue)
{
//String bbb =
"incSum indexes ";
//bbb =
bbb.concat(Integer.toString(indexKey));
//bbb = bbb.concat("
");
//bbb =
bbb.concat(Integer.toString(indexValue));
//System.out.println(bbb);
// Increment sum
this.keys[indexKey].sums[indexValue]++;
//String xxx =
"incSum after update key ";
//xxx =
xxx.concat(Integer.toString(this.keys[indexKey].key));
//xxx = xxx.concat("
Value ");
//xxx =
xxx.concat(Integer.toString(this.keys[indexKey].values[indexValue]));
//xxx = xxx.concat("
Sum ");
//xxx = xxx.concat(Integer.toString(this.keys[indexKey].sums[indexValue]));
//System.out.println(xxx);
return;
} // end incSum
// Get sum for key at indexes, return sum
public int getSum(int indexKey, int indexValue)
{
return
this.keys[indexKey].sums[indexValue];
} // end getSum
} // end class KeyTable
static public
KeyTable keyTable = new KeyTable(); // visible instance of KeyTable class
where the last statement is the
instantiation of the KeyTable class as mentioned above.
Here are two classes: the KeyTable class and the Key class where
the Key class is a Java version of a C struct type. In Ada it would be
type Key_Type
is record
key : Integer; //
value of key
valueCount : Integer; // number of different values associated
with key
values : array(1..20)
of Integer;
sums : array(1..20)
of Integer;
end record;
where the type name of Key_Type
is used since in Ada Key and key refer to the same thing. That is, case doesn’t matter in a name. The valueCount is the number of locations in
both values and sums that have been supplied an actual value.
Actually, in Ada the array
would first be declared similar to
type Array_Type
is array(1..20) of
Integer;
and then used to declare the
type of values and sums in the record type.
Spreading the declaration across multiple lines is just a matter of
preference as is
type Key_Type
is record
versus
type Key_Type is record
Note that the Key_Table class
was filled in as I went along first getting the getKey and getKeyCount
functions to work and adding other functions as needed using a different set of
array items to save the data as in the C# application. I first tried a triple dimension array to
keep track of the key, the values of the key and the number of instances of the
particular value. I was having trouble
with how to correctly implement that and was suspicious that I was doing so
correctly which use of debug output verified.
I wasted quite a bit of time trying to figure out how make the triple
dimensioned array work for what I wanted.
Then I thought that maybe I
could do something similar to the namedtuple of the Python application. This was when I decided to do the Key class
to see if I could use it as a C struct and found that Java didn’t
complaint. However, although I added
the
public Key[] keys = new Key[30]; // instances of
"struct" class
statement to go with the
public int keyCount; // number of different keys
statement I forgot to include
the code
// Instantiate
pointer for each instance of "struct" class
for (int i=0; i<30; i++)
{
keys[i] = new Key();
}
to instantiate of the Key class
for each instance of keys in the array.
This compiled ok after I changed the KeyTable class and Update code to
start using the new structure. However,
when I then attempted to execute the newly built LearnToCodeGR.class (that is,
.class not .exe) it threw an exception of
Exception in thread “main”
java.lang.NullPointerException
at
LearnToCodeGR$KeyTable.setKey<LearnToCodeGR.java:91>
at
LearnToCodeGR.KeyTable.Update<LearnToCodeGR.java:218>
... Parse
... main
where the statement at line 91
was
this.keys[keyCount].key = key;
This is where the power of the
subconscious came into play. After
puzzling about the exception a little bit I decided to put the matter away for
another day. Almost immediately while
doing something else it occurred to me that the reason for a null pointer was
that this.keys[keyCount] didn’t point to anything and that its constructor
needed to be executed. So I noted that
down and the next morning implemented it in the for loop of the KeyTable class
constructor. Problem solved.
The only problem left was to
implement the incSum function that had been left null. Then running the application with the debug
output showed that the correct results were being obtained.
So the Report function was
added – a simple matter.
// Report the results
private static void Report()
{
// Report individual results
System.out.println( "Report" );
for (int i = 0; i < keyTable.getKeyCount(); i++)
{
for (int j = 0; j < keyTable.getValueCount(i); j++)
{
String report = "Key ";
report =
report.concat(Integer.toString(keyTable.getKey(i)));
report = report.concat(" Value ");
report =
report.concat(Integer.toString(keyTable.getValue(i,j)));
report = report.concat(" Sum ");
report =
report.concat(Integer.toString(keyTable.getSum(i,j)));
System.out.println(report);
} // end for loop
} // end for loop
// Items identifying the key with the largest number of a
particular value
int key = 0;
int value = 0;
int sum = 0;
for (int i = 0; i < keyTable.getKeyCount(); i++)
{
for (int j = 0; j < keyTable.getValueCount(i); j++)
{
if (keyTable.getSum(i,j) > sum) // save key and
value with greater sum
{
key = keyTable.getKey(i);
value = keyTable.getValue(i,j);
sum = keyTable.getSum(i,j);
}
} // end for loop
} // end for loop
String report = "Key and Value with greatest Sum
";
report = report.concat(Integer.toString(key));
report = report.concat(" ");
report = report.concat(Integer.toString(value));
report = report.concat(" ");
report = report.concat(Integer.toString(sum));
System.out.println( report );
return;
} // end Report
The second part of this is the
same code as in the Python version except that I realized that the initialize
of its if statement wasn’t necessary since the j = 0 of the inner loop for i =
0 of the outer loop would store the initial values into key, value, and sum.
The debug output was then commented
out so only
Report
Key 1000 Value 1 Sum 13
Key 1000 Value 2 Sum 7
Key 2000 Value 1 Sum 16
Key 2000 Value 2 Sum 4
Key 3000 Value 1 Sum 20
Key 4000 Value 1 Sum 15
Key 4000 Value 3 Sum 2
Key 4000 Value 2 Sum 2
Key 4000 Value 4 Sum 1
Key 5000 Value 2 Sum 8
Key 5000 Value 1 Sum 7
Key 5000 Value 3 Sum 4
Key 5000 Value 4 Sum 1
Key and Value with
greatest Sum 3000 1 20
was displayed in the Command
Prompt window.
Discussion of the modified C# Application
I also declared the enum type
outside of any function as I found that I had to do in the Java
application. This worked as well
without the curiosity that I mentioned above for Java.
Using the Key and KeyTable
classes (in their separate .cs files), the Update was changed similar to in
Java while the Parse was modified to use the Scan enum type in the switch
statement.
Otherwise, the C# code is
pretty much as it was before.
The complete code is as
follows. Note, that since Visual C# has
a debugger, no debug output is in the code.
Key.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace MaxColSumbyKey
{
// Key class
public class Key
{
public int key; // value of key
public int valueCount; // number of different values
associated with key
public int[] values = new int[20]; // up to
20 different values associated with
public int[] sums = new int[20];
// key and
number of instances of value
} // end Key class
} // end MaxColSumbyKey
KeyTable.cs
using System;
using
System.Collections.Generic;
using System.Linq;
using System.Text;
using
System.Threading.Tasks;
using System.Windows.Forms;
namespace MaxColSumbyKey
{
// KeyTable class
public class KeyTable
{
public int keyCount; // number of different keys
public Key[] keys = new Key[30]; // instances of "struct" class
public KeyTable() // constructor
{
this.keyCount = 0;
// Instantiate pointer for each
instance of "struct" class
for (int i = 0; i < 30; i++)
{
keys[i] = new Key();
}
} // end constructor
// getters
\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\
// Return key at index
public int getKey(int index)
{
return this.keys[index].key;
} // end getKey
// Return number of keys in table
public int getKeyCount()
{
return keyCount;
} // end getKeyCount
// Return number of different values for key
at index
public int getValueCount(int index)
{
return this.keys[index].valueCount; //[index];
} // end getValueCount
// setters \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\
// Store new key into table and return index
of key in the table
public int setKey(int key)
{
for (int i = 0; i < keyCount; i++)
{
if (this.keys[i].key == key) // key already in
table
{
return i;
}
} // end for loop
if (this.keyCount < 30) // add key to table if
room
{
Key cKey = new Key();
this.keys[keyCount].key = key;
int index = keyCount;
keyCount++;
return index;
}
else
{
MessageBox.Show("Error: Too
many different keys");
return -1;
}
} // end setKey
// Add value for key at index, return value
index
public int setValue(int index, int value)
{
for (int i = 0; i < this.keys[index].valueCount; i++)
{
if (this.keys[index].values[i] == value)
{
// Increment sum
this.keys[index].sums[i]++;
return i;
}
} // end for loop
// Add value to array for key since
didn't return for existing value
this.keys[index].values[this.keys[index].valueCount]
= value;
this.keys[index].sums[this.keys[index].valueCount]++;
int valueIndex = this.keys[index].valueCount;
this.keys[index].valueCount++; // increment number of
values for key
return valueIndex;
} // end setValue
// Get value for key at indexes, return value
public int getValue(int indexKey, int indexValue)
{
return this.keys[indexKey].values[indexValue];
} // end getValue
// Increment Sum for value of key at indexKey
and value at indexValue
public void incSum(int indexKey, int indexValue)
{
// Increment sum
this.keys[indexKey].sums[indexValue]++;
return;
} // end incSum
// Get sum for key at indexes, return sum
public int getSum(int indexKey, int indexValue)
{
return this.keys[indexKey].sums[indexValue];
} // end getSum
} // end class KeyTable
} // MaxColSumbyKey
Form1.cs
using System;
using
System.Collections.Generic;
using
System.ComponentModel;
using System.Data;
using System.Drawing;
using System.IO;
using System.Linq;
using System.Text;
using
System.Threading.Tasks;
using
System.Windows.Forms;
namespace MaxColSumbyKey
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
// Instantiate the KeyTable class
KeyTable keyTable = new KeyTable(); // visible instance of KeyTable class
// Declare an enumerated type to use in Parse
public enum Scan
{ BYPASS, KEY,
VALUE1, VALUE2 }
// Constants. That is, not
to be changed.
const Byte NINE = 57;
const Byte ZERO = 48;
private void toolStripMenuItem1_Click_1(object sender,
EventArgs e)
{
// Click on File -- not needed
}
private void ReportResults()
{
int keyMostCount = 0;
int keyMostValues = 0;
int keyMostSums = 0;
// Search the keys
for (int keyIndex = 0; keyIndex < keyTable.keyCount;
keyIndex++)
{
int
valuesCount = keyTable.getValueCount(keyIndex);
if (valuesCount >= keyMostValues) // can be tie
for most instances of key
{
// search for greatest number of values
for (int valueIndex = 0; valueIndex <
valuesCount; valueIndex++)
{
if (keyTable.getSum(keyIndex, valueIndex)
> keyMostSums) // save key and value with greater sum
{
keyMostCount =
keyTable.getKey(keyIndex);
keyMostValues =
keyTable.getValue(keyIndex, valueIndex);
keyMostSums = keyTable.getSum(keyIndex,
valueIndex);
}
} // end for loop
} // end if
} // end for loop
keyTextBox.Text = keyMostCount.ToString();
valueTextBox.Text = keyMostValues.ToString();
sumTextBox.Text = keyMostSums.ToString();
this.Refresh();
} // end ReportResults
// Update tables with 'key' and 'value'
public void Update(int key, int[] value)
{
// Search keyTable
int keyIndex = -1;
int valueIndex = -1;
for (int i = 0;
i < keyTable.keyCount; i++) // getKeyCount()
{
if (key == keyTable.getKey(i))
{
keyIndex = i;
break; // exit loop
}
} // end loop
if (keyIndex < 0) // key not in the table
{
keyIndex = keyTable.setKey(key); // add key
valueIndex = keyTable.setValue(keyIndex, value[0]);
// add value for key
if (value[0] != value[1])
{ keyTable.setValue(keyIndex, value[1]); }
else
{ keyTable.incSum(keyIndex, valueIndex); }
}
else // key in the table
{
int valueCount = keyTable.getValueCount(keyIndex);
valueIndex = keyTable.setValue(keyIndex, value[0]);
// add value for key
if (value[0] != value[1])
{ keyTable.setValue(keyIndex, value[1]); }
else
{ keyTable.incSum(keyIndex, valueIndex); }
}
} // end Update
private int ConvertToNumeric(byte[] keyField, int index)
{
// Convert the byte string to an integer.
// Check the StructClasses to find the Key or add a new
one.
// Remember the index into the KeyValueRec to use for
the field Value.
int start;
start = 0;
int finish;
finish = index - 1;
if (finish < 0) // debug
{
MessageBox.Show("ConvertToNumeric error 1"); }
int keyInt = 0;
if ((keyField[finish] >= ZERO) &
(keyField[finish] <= NINE))
{
keyInt = keyField[finish] - ZERO; // convert ASCII
to digit
}
finish--;
int m = 10;
while (finish >= start)
{
if (finish < 0) // debug
{ MessageBox.Show("ConvertToNumeric error
2"); }
if ((keyField[finish] >= ZERO) & (keyField[finish]
<= NINE))
{
keyInt = keyInt + (m * (keyField[finish] -
ZERO));
}
m = m * 10;
finish--;
}
return keyInt;
} // ConvertToNumeric
private void Parse(int count, Byte[] data)
{
// Build a table of keys (second field of a record)
and, for each key,
// the number of value items (third and fourth fields)
of the key and
// the number of instances of the particular value.
// The first field ends with a TAB. The second field (Key) ends the
// same way as does both the Value fields or at the end
of record
// following the second Value field (in this case NL
but for a different
// file - a DOS file - in the CR LF pair).
// All other data is ignored until end-of-file (EOF) or
end of record.
const Byte HT = 9; // horizontal tab
const Byte CR = 13;
const Byte LF =
10;
const Byte NL = 10; // new line
Scan scanPhase = Scan.BYPASS;
int keyCount = 0; // number of actual bytes in keyField
Byte[] keyField = new Byte[16]; // max of 16 bytes for
a key
int value1Count
= 0;
Byte[] value1Field = new Byte[10];
int value2Count = 0;
Byte[] value2Field = new Byte[10];
int index = 0; // index into keyField, etc for next
Byte
int keyValue = 0; // keyField as converted
int[] valueValues = new int[2];
valueValues[0] = 0;
valueValues[1] = 0;
for (int i = 0; i < count; i++) // examine each byte
of data
{
Byte debugxx = data[i]; // examine byte with
debugger
// Parse key
switch (scanPhase)
{
case Scan.BYPASS: // ignore text until after
first HT found
if (data[i] == HT)
{
scanPhase = Scan.KEY;
// Initialize for next set of data
keyCount = 0;
value1Count = 0;
value2Count = 0;
}
break;
case Scan.KEY:
// capture the Key
if (data[i] != HT)
{
keyField[index] = data[i];
index++;
}
else
{
keyCount = index;
keyValue = ConvertToNumeric(keyField,
keyCount);
scanPhase = Scan.VALUE1; // Value
immediately follows HT
index = 0;
}
break;
case Scan.VALUE1: // capture first value
if (data[i] != HT)
{
value1Field[index] = data[i];
index++;
}
else
{
value1Count = index;
valueValues[0] = ConvertToNumeric(value1Field,
value1Count);
scanPhase = Scan.VALUE2; // Value
immediately follows HT
index = 0; // Capture the first value
}
break;
case Scan.VALUE2: // capture 2nd value
if ((data[i] != HT) && (data[i] !=
NL) && (data[i] != CR) && (data[i] != LF))
{
value2Field[index] = data[i];
index++;
}
else
{
value2Count = index;
valueValues[1] = ConvertToNumeric(value2Field,
value2Count);
index = 0;
// Update tables with the data from the
record.
Update(keyValue, valueValues);
// Initialize for next record
keyCount = 0;
value1Count = 0;
value2Count = 0;
scanPhase = Scan.BYPASS;
}
break;
}
if (i >= count)
{
return;
}
} // end for
return;
} // Parse
private void ReadAndParse(Stream file)
{
System.Byte[] buffer = new byte[file.Length]; // buffer
to contain file
int numBytesRead = 0;
try
{
// Read everything in file.
numBytesRead = file.Read(buffer, 0,
(int)file.Length);
file.Close();
}
catch (Exception ex)
{
MessageBox.Show("Error reading file");
}
Parse(numBytesRead, buffer); // Parse the bytes read
return;
} // ReadAndParse
private void
openToolStripMenuItem_Click(object sender, EventArgs e)
{ // to open the file
Stream myStream = null;
// Get an instance of a FileDialog.
OpenFileDialog openFileDialog = new OpenFileDialog();
// Use a filter
to allow only certain file extensions.
openFileDialog.InitialDirectory = "c:\\";
openFileDialog.Filter = "txt files
(*.txt)|*.txt|All files (*.*)|*.*";
openFileDialog.FilterIndex = 2;
openFileDialog.RestoreDirectory = true;
if (openFileDialog.ShowDialog() == DialogResult.OK)
{
try
{
if ((myStream = openFileDialog.OpenFile()) !=
null)
{
using (myStream)
{
// Read the file and build tables from
the data.
ReadAndParse(myStream);
ReportResults();
return;
}
}
}
catch (Exception ex)
{
MessageBox.Show("Error: Could not read
file from disk. Original error: " + ex.Message);
}
}
} // openToolStripMenuItem_Click
private void keyTextBox_TextChanged(object sender,
EventArgs e)
{
// don't care if has changed
}
private void Form1_Load(object sender, EventArgs e)
{
}
private void valueTextBox_TextChanged(object sender,
EventArgs e)
{
}
} // class Form1
} // namespace
MaxColSumbyKey
Form1.Designer.cs
namespace MaxColSumbyKey
{
partial class Form1
{
/// <summary>
/// Required designer variable.
/// </summary>
private System.ComponentModel.IContainer components = null;
/// <summary>
/// Clean up any resources being used.
/// </summary>
/// <param name="disposing">true if managed
resources should be disposed; otherwise, false.</param>
protected override void Dispose(bool disposing)
{
if (disposing && (components != null))
{
components.Dispose();
}
base.Dispose(disposing);
}
#region Windows Form Designer generated code
/// <summary>
/// Required method for Designer support - do
not modify
/// the contents of this method with the code
editor.
/// </summary>
private void InitializeComponent()
{
this.directorySearcher1 = new
System.DirectoryServices.DirectorySearcher();
this.directorySearcher2 = new
System.DirectoryServices.DirectorySearcher();
this.menuStrip1 = new System.Windows.Forms.MenuStrip();
this.menuStrip2 = new System.Windows.Forms.MenuStrip();
this.toolStripMenuItem1 = new System.Windows.Forms.ToolStripMenuItem();
this.toolStripMenuItem2 = new System.Windows.Forms.ToolStripMenuItem();
this.keyTextBox = new System.Windows.Forms.TextBox();
this.valueTextBox = new System.Windows.Forms.TextBox();
this.sumTextBox = new System.Windows.Forms.TextBox();
this.label1 = new System.Windows.Forms.Label();
this.label2 = new System.Windows.Forms.Label();
this.label3 = new System.Windows.Forms.Label();
this.menuStrip2.SuspendLayout();
this.SuspendLayout();
//
// directorySearcher1
//
this.directorySearcher1.ClientTimeout = System.TimeSpan.Parse("-00:00:01");
this.directorySearcher1.ServerPageTimeLimit =
System.TimeSpan.Parse("-00:00:01");
this.directorySearcher1.ServerTimeLimit = System.TimeSpan.Parse("-00:00:01");
//
// directorySearcher2
//
this.directorySearcher2.ClientTimeout = System.TimeSpan.Parse("-00:00:01");
this.directorySearcher2.ServerPageTimeLimit =
System.TimeSpan.Parse("-00:00:01");
this.directorySearcher2.ServerTimeLimit = System.TimeSpan.Parse("-00:00:01");
//
// menuStrip1
//
this.menuStrip1.ImageScalingSize = new System.Drawing.Size(20, 20);
this.menuStrip1.Location = new System.Drawing.Point(0, 24);
this.menuStrip1.Name = "menuStrip1";
this.menuStrip1.Padding = new System.Windows.Forms.Padding(4, 2, 0, 2);
this.menuStrip1.Size = new System.Drawing.Size(212, 24);
this.menuStrip1.TabIndex = 0;
this.menuStrip1.Text = "menuStrip1";
//
// menuStrip2
//
this.menuStrip2.ImageScalingSize = new System.Drawing.Size(20, 20);
this.menuStrip2.Items.AddRange(new System.Windows.Forms.ToolStripItem[] {
this.toolStripMenuItem1});
this.menuStrip2.Location = new System.Drawing.Point(0, 0);
this.menuStrip2.Name = "menuStrip2";
this.menuStrip2.Padding = new System.Windows.Forms.Padding(4, 2, 0, 2);
this.menuStrip2.Size = new System.Drawing.Size(212, 24);
this.menuStrip2.TabIndex = 1;
this.menuStrip2.Text = "menuStrip2";
//
// toolStripMenuItem1
//
this.toolStripMenuItem1.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] {
this.toolStripMenuItem2});
this.toolStripMenuItem1.Name = "toolStripMenuItem1";
this.toolStripMenuItem1.Size = new System.Drawing.Size(37, 20);
this.toolStripMenuItem1.Text = "File";
this.toolStripMenuItem1.Click += new System.EventHandler(this.toolStripMenuItem1_Click_1);
//
// toolStripMenuItem2
//
this.toolStripMenuItem2.Name = "toolStripMenuItem2";
this.toolStripMenuItem2.Size = new System.Drawing.Size(103, 22);
this.toolStripMenuItem2.Text = "Open";
this.toolStripMenuItem2.Click += new System.EventHandler(this.openToolStripMenuItem_Click);
//
// keyTextBox
//
this.keyTextBox.Location = new System.Drawing.Point(9, 50);
this.keyTextBox.Margin = new System.Windows.Forms.Padding(2);
this.keyTextBox.Name = "keyTextBox";
this.keyTextBox.Size = new System.Drawing.Size(99, 20);
this.keyTextBox.TabIndex = 2;
this.keyTextBox.TextChanged += new System.EventHandler(this.keyTextBox_TextChanged);
//
// valueTextBox
//
this.valueTextBox.Location = new System.Drawing.Point(9, 90);
this.valueTextBox.Margin = new System.Windows.Forms.Padding(2);
this.valueTextBox.Name = "valueTextBox";
this.valueTextBox.Size = new System.Drawing.Size(99, 20);
this.valueTextBox.TabIndex = 3;
this.valueTextBox.TextChanged += new System.EventHandler(this.valueTextBox_TextChanged);
//
// sumTextBox
//
this.sumTextBox.Location = new System.Drawing.Point(9, 132);
this.sumTextBox.Name = "sumTextBox";
this.sumTextBox.Size = new System.Drawing.Size(99, 20);
this.sumTextBox.TabIndex = 4;
//
// label1
//
this.label1.AutoSize = true;
this.label1.Location = new System.Drawing.Point(6, 116);
this.label1.Name = "label1";
this.label1.Size = new System.Drawing.Size(28, 13);
this.label1.TabIndex = 5;
this.label1.Text = "Sum";
//
// label2
//
this.label2.AutoSize = true;
this.label2.Location = new System.Drawing.Point(9, 76);
this.label2.Name = "label2";
this.label2.Size = new System.Drawing.Size(32, 13);
this.label2.TabIndex = 6;
this.label2.Text = "Vaue";
//
// label3
//
this.label3.AutoSize = true;
this.label3.Location = new System.Drawing.Point(12, 34);
this.label3.Name = "label3";
this.label3.Size = new System.Drawing.Size(25, 13);
this.label3.TabIndex = 7;
this.label3.Text = "Key";
//
// Form1
//
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
this.ClientSize = new System.Drawing.Size(212, 206);
this.Controls.Add(this.label3);
this.Controls.Add(this.label2);
this.Controls.Add(this.label1);
this.Controls.Add(this.sumTextBox);
this.Controls.Add(this.valueTextBox);
this.Controls.Add(this.keyTextBox);
this.Controls.Add(this.menuStrip1);
this.Controls.Add(this.menuStrip2);
this.MainMenuStrip = this.menuStrip1;
this.Margin = new System.Windows.Forms.Padding(2);
this.Name = "Form1";
this.Text = "Form1";
this.Load += new System.EventHandler(this.Form1_Load);
this.menuStrip2.ResumeLayout(false);
this.menuStrip2.PerformLayout();
this.ResumeLayout(false);
this.PerformLayout();
}
#endregion
private System.DirectoryServices.DirectorySearcher directorySearcher1;
private System.DirectoryServices.DirectorySearcher directorySearcher2;
private System.Windows.Forms.MenuStrip menuStrip1;
private System.Windows.Forms.MenuStrip menuStrip2;
private System.Windows.Forms.ToolStripMenuItem toolStripMenuItem1;
private System.Windows.Forms.ToolStripMenuItem toolStripMenuItem2;
private System.Windows.Forms.TextBox keyTextBox;
private System.Windows.Forms.TextBox valueTextBox;
private System.Windows.Forms.TextBox sumTextBox;
private System.Windows.Forms.Label label1;
private System.Windows.Forms.Label label2;
private System.Windows.Forms.Label label3;
}
}
Note: Most of Form1.Designer.cs was generated by C#. Small changes had to be made when one of
text boxes had its name changed after it had been created.
The result of running the
application is
No comments:
Post a Comment