Es wurde der auf http://www.diaware.de/html/roemzahl.html beschriebene Aufbau für die Lösung der Aufgabe genutzt. Im folgenden werden die auf dieser Webseite angeführten Punkte für Regeln und Aufbau darum ergänzt, wie sie im Prg. umgesetzt wurden.
romanSign
bezeichnet. intValue(String,boolean)
Berücksichtigung.
Die Werte der römischen Zeichen werden immer erst ausgewertet, wenn sichergestellt
ist, dass es vollständig ist. D.h. bei nebeneinanderstehenden Grundzeichen
(Bsp. 'IX') wird immer überprüft, ob sie "zs.gehören"
(wie es bei 'IX' der Fall ist).basicRomanSign
bezeichnet.BasicRomanSign
implementiert. Diese wird schließlich in
RomanNumber
für ein Array ROMAN_SIGN
genutzt,
in welcher alle Grundzeichen und deren Wertigkeit gespeichert werden. Dieses
Array kann theoretisch (würde es mehr röm. Grundzeichen geben) erweitert
werden, ohne das die Methoden des Prg. angepasst werden müssen.tolerant = false
übergeben wird. Da beim Konstruieren eines RomanNumber-Objekts die röm.
Zahl "tolerant" interpretiert werden soll, wird im Konstruktor dieselbe
Methode mit tolerant = true
aufgerufen.tolerant = false
sichergestellt ist, dass die vorherige Bedingung
(4.) erfüllt ist. Die Berechnung der Wertigkeit erfolgt in der Methode
rating(char[],tolerant)
(für subtraktive Kombinationen)
bzw. mit rating(char)
(für Grundzeichen)intValue(String,boolean)
Berücksichtigung. Die besondere Regel wird in rating(char[],tolerant)
berücksichtigt und zwar nur im Falle von tolerant = false
.rating(char[],tolerant)
berechnet. tolerant = false
Berücksichtigung.Das Programm wurde auch ausführlich im Source dokumentiert.
/** * Softwareentwicklung I - Exercise 7/1. * Roman number. * It allows the creation of roman numbers on base of a roman number in a * String or an integer value. * The class also allows the transformation from roman number to int and * reversed without creation of a RomanNumber object - * method intValue(String,tolerant) and getRomanNumber(int). * In-source comments are followed by the described code. * * @see http://www.diaware.de/html/roemzahl.html * @author Daniel Brunthaler (0255054) * @version 1.0 */ public class RomanNumber { /** * Constant for roman numbers which are not valid. */ public static final String NOT_VALID = "???"; // This array contais all possible basic roman signs. It is initialized in // the static initializer below. private static final BasicRomanSign[] ROMAN_SIGN; static { // The possible signs can be extended ad libitum, but the last basic roman // sign always must be a 10 to the power of n. I know, this does not make // really sense since there are no more basic roman signs, but who knows, // what it is anytime good for ... :) ROMAN_SIGN = new BasicRomanSign[7]; ROMAN_SIGN[0] = new BasicRomanSign('I',1); ROMAN_SIGN[1] = new BasicRomanSign('V',5); ROMAN_SIGN[2] = new BasicRomanSign('X',10); ROMAN_SIGN[3] = new BasicRomanSign('L',50); ROMAN_SIGN[4] = new BasicRomanSign('C',100); ROMAN_SIGN[5] = new BasicRomanSign('D',500); ROMAN_SIGN[6] = new BasicRomanSign('M',1000); } // The roman number in a String. private String romanNumber; // The integer value for this roman number. private int intValue; /** * Constructs a RomanNumber object. * @param intValue integer value which this roman number should stand for. */ public RomanNumber(int intValue) { super(); romanNumber = getRomanNumber(intValue); this.intValue = intValue; } /** * Constructs a RomanNumber object. * @param romanNumber roman number, this number is interpreted "tolerant", * this means that certain rules are ignored. */ public RomanNumber(String romanNumber) { super(); // Check roman number with tolerant rules. intValue = intValue(romanNumber,true); if (intValue < 0) this.romanNumber = null; else this.romanNumber = romanNumber; } /** * Finds the basic roman sign for the given integer value. * @param intValue for which the basic roman sign should be found. * @return basic roman sign for the given integer value. */ private static final String getBSR(int intValue) { // The ROMAN_SIGN array is scanned for the sought integer value. for (int i = 0;i < ROMAN_SIGN.length;i++) if (intValue == ROMAN_SIGN[i].getIntValue()) return Character.toString(ROMAN_SIGN[i].getSign()); return "?"; } /** * The maximum integer value represented by a basic roman sign. * Depends on the containing of the ROMAN_SIGN array. * @return maximum integer value represented by a basic roman sign. */ private static final int getMax() { int max = 0; // The ROMAN_SIGN array is scanned for the maximum integer value. for (int i = 0;i < ROMAN_SIGN.length;i++) if (ROMAN_SIGN[i].getIntValue() > max) max = ROMAN_SIGN[i].getIntValue(); return max; } /** * Finds a roman number for a certain integer value. * @param intValue for which a roman number should be found. * @return a roman number representing the given integer value. */ public static String getRomanNumber(int intValue) { String romanNumber = "", sign; int max = getMax(); int div = max; int digit, i; if (intValue >= max * 4 || intValue <= 0) // the given integer value is bigger than the maximum presentable // roman number return NOT_VALID; while (div >= 1) { // digit for digit of the integer number is analysed. // for example 1995: 1995 / 1000 = 1,995 absolute = 1 => 'M' // (1995 - 1000) / 100 = 9,95 absolute = 9 => 'M' + 'CM' // (995 - 900) / 10 = 9,5 absoulte = 9 => 'MCM' + 'XC' // (95 - 90) / 1 = 5 absolute = 5 => 'MCMXC' + 'V' digit = Math.abs(intValue / div); if (digit == 0) { } else if (digit <= 3) { sign = getBSR(div); for (i = 0;i < digit;i++) romanNumber += sign; } else if (digit == 4) { romanNumber += getBSR(div) + getBSR(div * 5); } else if (digit == 5) { romanNumber += getBSR(div * 5); } else if (digit <= 8) { romanNumber += getBSR(div * 5); sign = getBSR(div); for (i = 0;i < digit - 5;i++) romanNumber += sign; } else if (digit == 9) { romanNumber += getBSR(div) + getBSR(div * 10); } intValue -= div * digit; div /= 10; } return romanNumber; } /** * Finds the integer value of the given roman number. * The method allows a "tolerant" analyse of the roman number, if tolerant * is set to true. * @param romanNumber which should be analysed. * @param tolerant analyse of the roman number. Certain rules for roman * numbers are ignored, if this is set to true. * @return integer value which represents the given roman number. * The function returns a negative integer, if the given * roman number is not valid. */ public static int intValue(String romanNumber, boolean tolerant) { int rnLength = romanNumber.length(); // length of the roman number int intValue = 0; // int value of the roman number int bsCounter = 1; // Counter for basic roman signs, if it counts the // same sign more than three times => invalid roman number char basicRomanSign = ' '; // certain basic sign out of the roman number char lastBasicSign = ' '; // last analysed basic roman sign char[] romanSign = {' ',' '}; // roman sign char[] lastRomanSign = {' ',' '}; // last roman sign (for checking // if signs are in ascending order) int ratingBRS, ratingLBS, ratingRS, ratingLRS; if (rnLength < 1) { return -1; } else if (rnLength == 1) { // roman number contains only one sign => return rating of this sign return rating(romanNumber.charAt(0)); } else if (rnLength > 1) { basicRomanSign = romanNumber.charAt(0); ratingBRS = rating(basicRomanSign); if (ratingBRS < 0) return -1; else { romanSign[0] = basicRomanSign; lastBasicSign = basicRomanSign; } } ratingLBS = rating(lastBasicSign); ratingLRS = rating(lastRomanSign,tolerant); for (int i = 1;i < rnLength;i++) { // check sign for sign from given number basicRomanSign = romanNumber.charAt(i); if (basicRomanSign == lastBasicSign) bsCounter++; else bsCounter = 1; if (bsCounter > 3) return -1; ratingBRS = rating(basicRomanSign); if (ratingBRS < 0) // basic roman sign does not exist. return -1; if (romanSign[1] == ' ' && ratingBRS > ratingLBS) { // subtractive combination (ex. 'IX') romanSign[1] = basicRomanSign; } else { // find out rating of roman sign, could be a subtractive // combination like 'IX' ratingRS = rating(romanSign,tolerant); if (ratingRS < 0) return -1; if (tolerant || ratingLRS == 0 || ratingLRS >= ratingRS) // check (if tolerant = false) if rating of last roman // sign is greater than rating of following roman sign // only if there is a last roman sign (!= 0) intValue += ratingLRS; else return -1; lastRomanSign[0] = romanSign[0]; lastRomanSign[1] = romanSign[1]; romanSign[0] = basicRomanSign; romanSign[1] = ' '; } lastBasicSign = basicRomanSign; ratingLBS = rating(lastBasicSign); ratingLRS = rating(lastRomanSign,tolerant); } ratingRS = rating(romanSign,tolerant); // if loop is exited, last analysed roman signs have to be considered // in calculation if (ratingRS < 0) return -1; if (tolerant || ratingLRS == 0 || ratingLRS >= ratingRS) intValue += ratingLRS + ratingRS; else return -1; return intValue; } /** * Rating or integer value of a basic roman sign, for example 'V' = 5. * @param basicRomanSign for which the integer value should be returned. * @return integer value for a certain basic roman sign. */ private static final int rating(char basicRomanSign) { if (basicRomanSign == ' ') return 0; else // scan the ROMAN_SIGN array for the given basic roman sign for (int i = 0;i < ROMAN_SIGN.length;i++) if (ROMAN_SIGN[i].getSign() == basicRomanSign) return ROMAN_SIGN[i].getIntValue(); return -1; } /** * Rating or integer value of roman signs, like 'IX' (subtractive * combination). If the roman sign only contains a basic roman sign, the * function calls the function rating(char). * The paramter tolerant gives the option to ignore the rule * 'I' only stands before 'V' or 'X', 'X' only stands before 'L' or 'C' and * 'C' only stands before 'D' or 'M'. * @param romanSign which should be analysed. * @param tolerant if true, the described rule is ignored. * @return integer value for this roman sign. */ private static final int rating(char[] romanSign, boolean tolerant) { if (romanSign.length == 0 || romanSign.length > 2) { return -1; } else if (romanSign[1] == ' ') { // roman sign contains only one sign // => find out rating of this sign. return rating(romanSign[0]); } else { int ratingLeft = rating(romanSign[0]); int ratingRight = rating(romanSign[1]); // the rating of the left basic sign in a roman sign is greater // than the right basic sign => roman sign is invalid. if (ratingLeft > ratingRight) // the rating of the right basic roman sign is greater than the // rating of the left basic roman sign * 10 (like 'IC' or 'XM'). // this rule is ignored, if tolerant is true. return -1; if (!tolerant && ratingRight > ratingLeft * 10) return -1; if (!tolerant && Integer.toString(ratingLeft).charAt(0) == '5') // The left basic roman sign is euqal 5 * 10^n => roman sign // is invalid. // this rule is ignored, if tolerant is true. return -1; return ratingRight - ratingLeft; } } /** * Returns the integer value of this roman number object. * @return the integer value of this roman number object. */ public int intValue() { if (intValue < 0) return 0; else return intValue; } /** * Returns the roman number as a String. * @return the roman number as a String. */ public String toString() { if (romanNumber == null) return NOT_VALID; else return romanNumber; } /** * For testing purpose only. * @param args no arguments have to be given. */ public static void main(String[] args) { // Test of method RomanNumber.intValue(String,boolean) IO.writeLn("intValue(\"\"),false) = " + RomanNumber.intValue("",false)); IO.writeLn("intValue(\"X\"),false) = " + RomanNumber.intValue("X",false)); IO.writeLn("intValue(\"CCC\"),false) = " + RomanNumber.intValue("CCC",false)); IO.writeLn("intValue(\"IX\"),false) = " + RomanNumber.intValue("IX",false)); IO.writeLn("intValue(\"XIX\"),false) = " + RomanNumber.intValue("XIX",false)); IO.writeLn("intValue(\"IXX\"),false) = " + RomanNumber.intValue("IXX",false)); IO.writeLn("intValue(\"CMXCIX\"),false) = " + RomanNumber.intValue("CMXCIX",false)); IO.writeLn("intValue(\"IM\"),false) = " + RomanNumber.intValue("IM",false)); // Test of creation a RomanNumber object with a String. IO.writeLn("(new RomanNumber(\"\")).intValue() = " + (new RomanNumber("")).intValue()); IO.writeLn("(new RomanNumber(\"X\")).intValue() = " + (new RomanNumber("X")).intValue()); IO.writeLn("(new RomanNumber(\"CCC\")).intValue() = " + (new RomanNumber("CCC")).intValue()); IO.writeLn("(new RomanNumber(\"IX\")).intValue() = " + (new RomanNumber("IX")).intValue()); IO.writeLn("(new RomanNumber(\"XIX\")).intValue() = " + (new RomanNumber("XIX")).intValue()); IO.writeLn("(new RomanNumber(\"IXX\")).intValue() = " + (new RomanNumber("IXX")).intValue()); IO.writeLn("(new RomanNumber(\"IXX\")).toString() = " + (new RomanNumber("IXX")).toString()); IO.writeLn("(new RomanNumber(\"CMXCIX\")).intValue() = " + (new RomanNumber("CMXCIX")).intValue()); IO.writeLn("(new RomanNumber(\"IM\")).intValue() = " + (new RomanNumber("IM")).intValue()); // Test of RomanNumber.getRomanNumber(int) IO.writeLn("getRomanNumber(-5)" + RomanNumber.getRomanNumber(-5)); IO.writeLn("getRomanNumber(0)" + RomanNumber.getRomanNumber(0)); IO.writeLn("getRomanNumber(1)" + RomanNumber.getRomanNumber(1)); IO.writeLn("getRomanNumber(3)" + RomanNumber.getRomanNumber(3)); IO.writeLn("getRomanNumber(5)" + RomanNumber.getRomanNumber(5)); IO.writeLn("getRomanNumber(8)" + RomanNumber.getRomanNumber(8)); IO.writeLn("getRomanNumber(9)" + RomanNumber.getRomanNumber(9)); IO.writeLn("getRomanNumber(89)" + RomanNumber.getRomanNumber(89)); IO.writeLn("getRomanNumber(99)" + RomanNumber.getRomanNumber(99)); IO.writeLn("getRomanNumber(459)" + RomanNumber.getRomanNumber(459)); IO.writeLn("getRomanNumber(1381)" + RomanNumber.getRomanNumber(1381)); IO.writeLn("getRomanNumber(3999)" + RomanNumber.getRomanNumber(3999)); IO.writeLn("getRomanNumber(9111)" + RomanNumber.getRomanNumber(9111)); // Test of creation a RomanNumber object with an integer value. IO.writeLn("(new RomanNumber(-5)).toString() = " + (new RomanNumber(-5)).toString()); IO.writeLn("(new RomanNumber(0)).toString() = " + (new RomanNumber(0)).toString()); IO.writeLn("(new RomanNumber(1)).toString() = " + (new RomanNumber(1)).toString()); IO.writeLn("(new RomanNumber(3)).toString() = " + (new RomanNumber(3)).toString()); IO.writeLn("(new RomanNumber(5)).toString() = " + (new RomanNumber(5)).toString()); IO.writeLn("(new RomanNumber(8)).toString() = " + (new RomanNumber(8)).toString()); IO.writeLn("(new RomanNumber(9)).toString() = " + (new RomanNumber(9)).toString()); IO.writeLn("(new RomanNumber(89)).toString() = " + (new RomanNumber(89)).toString()); IO.writeLn("(new RomanNumber(459)).toString() = " + (new RomanNumber(459)).toString()); IO.writeLn("(new RomanNumber(1381)).toString() = " + (new RomanNumber(1381)).toString()); IO.writeLn("(new RomanNumber(3999)).toString() = " + (new RomanNumber(3999)).toString()); IO.writeLn("(new RomanNumber(9111)).toString() = " + (new RomanNumber(9111)).toString()); System.exit(0); } } /** * Softwareentwicklung I - Exercise 7/1. * Basic roman sign for roman numbers. * In-source comments are followed by the described code. * * @see RomanNumber * @author Daniel Brunthaler (0255054) * @version 1.0 */ class BasicRomanSign { private char basicRomanSign; private int intValue; /** * Constructor for a basic roman sign. * @param basicRomanSign which should be created. * @param intValue of the basic roman sign. */ BasicRomanSign(char basicRomanSign, int intValue) { this.basicRomanSign = basicRomanSign; this.intValue = intValue; } /** * Basic roman sign. * @return basic roman sign. */ char getSign() { return basicRomanSign; } /** * Integer value of this basic roman sign. * @return integer value of this basic roman sign. */ int getIntValue() { return intValue; } }
Es wurden in der main
-Methode möglichst viele verschiedenartige
Testfälle implementiert. Alle diese Testfälle hier mit Zweck und erwarteter
Ausgabe aufzulisten, erscheint mir nicht zielführend. Die Ausgabe wurde
in eine Datei umgeleitet und ist im folgenden angedruckt.
intValue(""),false) = -1 intValue("X"),false) = 10 intValue("CCC"),false) = 300 intValue("IX"),false) = 9 intValue("XIX"),false) = 19 intValue("IXX"),false) = -1 intValue("CMXCIX"),false) = 999 intValue("IM"),false) = -1 (new RomanNumber("")).intValue() = 0 (new RomanNumber("X")).intValue() = 10 (new RomanNumber("CCC")).intValue() = 300 (new RomanNumber("IX")).intValue() = 9 (new RomanNumber("XIX")).intValue() = 19 (new RomanNumber("IXX")).intValue() = 19 (new RomanNumber("IXX")).toString() = IXX (new RomanNumber("CMXCIX")).intValue() = 999 (new RomanNumber("IM")).intValue() = 999 getRomanNumber(-5)??? getRomanNumber(0)??? getRomanNumber(1)I getRomanNumber(3)III getRomanNumber(5)V getRomanNumber(8)VIII getRomanNumber(9)IX getRomanNumber(89)LXXXIX getRomanNumber(99)XCIX getRomanNumber(459)CDLIX getRomanNumber(1381)MCCCLXXXI getRomanNumber(3999)MMMCMXCIX getRomanNumber(9111)??? (new RomanNumber(-5)).toString() = ??? (new RomanNumber(0)).toString() = ??? (new RomanNumber(1)).toString() = I (new RomanNumber(3)).toString() = III (new RomanNumber(5)).toString() = V (new RomanNumber(8)).toString() = VIII (new RomanNumber(9)).toString() = IX (new RomanNumber(89)).toString() = LXXXIX (new RomanNumber(459)).toString() = CDLIX (new RomanNumber(1381)).toString() = MCCCLXXXI (new RomanNumber(3999)).toString() = MMMCMXCIX (new RomanNumber(9111)).toString() = ???
Das Prg. wurde ausschließlich in der main-Methode implementiert. Mit Hilfe der in der Aufgabenstellung angeg. Klassen und Methoden fällt das Prg. verhältnismäßig klein aus. Der Text aus der Datei wird Zeichen für Zeichen analysiert, bei Leerzeichen wird überprüft, ob das Wort in der Zeile noch Platz findet (mit Hilfe eines "Spalten-Zeigers"), wenn nicht wird es in die nächste Zeile geschrieben. Außerdem wird bei jedem neuen Zeichen überprüft, ob das Wort als Ganzes noch in der Zeile Platz findet, ansonsten wird ein Teil des Wortes in eine ganze Zeile geschrieben.
import IO.*; /** * Softwareentwicklung I - Exercise 7/2. * Text formatter. * Outputs formated text read out from a given file with given number of * columns. * In-source comments are followed by the described code. * * @author Daniel Brunthaler (0255054) * @version 1.0 */ public class TextFormatter { /** * Constructor for TextFormatter. */ public TextFormatter() { super(); } /** * Formats given file with given number of columns. Ouputs the result in * System.out. * @param args*/ public static void main(String[] args) { int columns = 0; String infoColumn = "01234567890123456789012345678901234567890123456789012345678901234567890"; String infoColumn2 = "0 10 20 30 40 50 60 70"; String errMsgInsuffArgs = "Zu wenig Argumente angegeben!"; String errMsgColumns = "Argument ungültig!"; String errMsgFile = "Datei nicht gefunden oder Lesefehler!"; String infoSyntax = "TextFormatter "; if (args.length < 2) { IO.writeLn(errMsgInsuffArgs); IO.writeLn(infoSyntax); System.exit(0); } try { columns = Integer.parseInt(args[1]); } catch (NumberFormatException e) { IO.writeLn(errMsgColumns); IO.writeLn(infoSyntax); System.exit(0); } try { String word = ""; // open file. Infile in = new Infile(args[0]); IO.writeLn(infoColumn + "\n" + infoColumn2 + "\n"); int column = 0; char ch = in.read(); while (ch != IO.EOF) { // analyse char for char until end of file. if (ch == ' ') { if (column + word.length() <= columns) { // word finds place in actual line. if (column != 0) IO.write(" "); } else { // word finds no place in actual line => next line. IO.writeLn(); column = 0; } column += word.length() + 1; IO.write(word); word = ""; } else if (ch == '\n') { // line feed in file, check if actual word finds place in // actual line or not. if (column + word.length() > columns) IO.writeLn(); else IO.write(" "); IO.writeLn(word); word = ""; column = 0; } else { // check if length of word is greater than columns of line. if (word.length() + 1 > columns) { IO.write("\n" + word + "\n"); column = 0; word = ""; } word += Character.toString(ch); } ch = in.read(); } in.close(); } catch (Exception e) { // error on file operation. IO.writeLn(errMsgFile); IO.writeLn(infoSyntax); } } }
Zum Testen wurde der in der Aufgabenstellung angeg. Beispieltext herangezogen. Die Ausgaben des Prg. TextFormatter wurden in eine Datei umgeleitet. Im Folgenden die Textdatei, sowie anschließend die Ausgaben des Prg. bei 70, 30 und 5 Zeichen pro Zeile.
Testdatei
Es war einmal ein kleines, süßes Mädchen, das immer ein Käppchen aus rotem Samt trug. Aufgrund dieses Attributes erhielt es ein Assign unter dem symbolischen Namen Rotkäppchen. Eines Tages sprach die Mutter: "Rotkäppchen, die Gesundheit deiner Großmutter hat einen Interrupt bekommen. Wir müssen ein Pflegeprogramm entwickeln und zur Großmutter bringen, um das Problem zu lösen. Verirre Dich jedoch nicht im Wald der alten Sprachen, sondern gehe nur ...
70 Zeichen pro Zeile
01234567890123456789012345678901234567890123456789012345678901234567890 0 10 20 30 40 50 60 70 Es war einmal ein kleines, süßes Mädchen, das immer ein Käppchen aus rotem Samt trug. Aufgrund dieses Attributes erhielt es ein Assign unter dem symbolischen Namen Rotkäppchen. Eines Tages sprach die Mutter: "Rotkäppchen, die Gesundheit deiner Großmutter hat einen Interrupt bekommen. Wir müssen ein Pflegeprogramm entwickeln und zur Großmutter bringen, um das Problem zu lösen. Verirre Dich jedoch nicht im Wald der alten Sprachen, sondern gehe nur ...
30 Zeichen pro Zeile
01234567890123456789012345678901234567890123456789012345678901234567890 0 10 20 30 40 50 60 70 Es war einmal ein kleines, süßes Mädchen, das immer ein Käppchen aus rotem Samt trug. Aufgrund dieses Attributes erhielt es ein Assign unter dem symbolischen Namen Rotkäppchen. Eines Tages sprach die Mutter: "Rotkäppchen, die Gesundheit deiner Großmutter hat einen Interrupt bekommen. Wir müssen ein Pflegeprogramm entwickeln und zur Großmutter bringen, um das Problem zu lösen. Verirre Dich jedoch nicht im Wald der alten Sprachen, sondern gehe nur ...
5 Zeichen pro Zeile
01234567890123456789012345678901234567890123456789012345678901234567890 0 10 20 30 40 50 60 70 Es war einma l ein klein es, süßes Mädch en, das immer ein Käppc hen aus rotem Samt trug. Aufgr und ...