2016-04-04 10 views
2

Ich habe Probleme bei einem Projekt, bei dem es um chemische Formeln geht. Ich habe zwei Klassen, Term und Formel.Java: Eine Zeichenfolge analysieren und in Begriffe zerlegen

Der Begriff empfängt Eingaben wie "H" oder "C2" - mit nur einem Buchstaben und einer beliebigen Anzahl nachfolgender Zahlen. Seine Felder sind Element und Atome - Element speichert den Charakter des Elements "H" oder "C", und Atome speichern den int-Wert der Anzahl der Atome, 1 oder 2.

Ich schrieb den Konstruktor als solchen;

public Term(String s) 
    { 

     if(s.length() == 1){ 
      element = s.charAt(0); 
      atoms = 1; } 

     else { 
      element = s.charAt(0); 
      String sA = s.substring(1,s.length()); 
      atoms = Integer.parseInt(sA); 
     } 
    } 

Ich verstehe das. Mein Problem ist in der Klasse Formel, die Strings wie "CH3CH2CH2CH2CH2CH3" empfängt. Es beinhaltet eine ArrayList, die Term-Objekte, die Terme genannt werden, speichert. Ich muss einen Konstruktor schreiben, der eine Formel wie "CH2O" erhalten kann, und würde die Begriffe {Term ('C', 1), Term ('H', 2), Term ('O', 1) } etc.

Ich habe wirklich Mühe herauszufinden, wie man den Konstruktor die Begriffe innerhalb der Eingabe-String zu identifizieren - ich habe über ValueOf und IndexOf gedacht, oder Teilstring, aber kann nicht scheinen, es zu bekommen. Mein erster Versuch war;

terms = new ArrayList<>(); 
    for(int i = 0; i <= s.length();i++) 
    { 
     if ((Character.isUpperCase(s.charAt(i))) && (Character.isUpperCase(s.charAt(i+1))) 
     { Term formulaS = new Term(s.charAt(i)); 
      terms.add(formulaS); } 

Ich verstehe seine sehr falsch und nur identifiziert Begriffe, die nur einen Charakter haben, aber nicht Anzahl von Atomen, wie ‚H‘. Ich habe das Gefühl, die Antwort beinhaltet die for-Schleife. Ich entschuldige mich für die Länge der Post, aber jede Hilfe wäre sehr willkommen.

+2

Sie müssen schauen [lexikalische Analyse] (https://en.m.wikipedia.org/wiki/Lexical_analysis), die der Prozess ist eine Folge von Zeichen in sinnvollen „Token“ die Zerschlagung basierte auf einer [lexikalischen Grammatik] (https://en.m.wikipedia.org/wiki/Lexical_grammar), normalerweise definiert durch reguläre Ausdrücke. –

+1

Sie könnten wahrscheinlich sofort die gesamte Eingabezeichenfolge in Großbuchstaben schreiben. Dann müssten Sie keine Großschreibung überprüfen. Sie müssen auch ein paar Dinge in Ihrer for-Schleife anders machen. Erstens, du gehst zu weit. Sollte ich ManoDestra

+0

Müssen Sie mit 2-Buchstaben-Atomnamen wie "Na" und "Cl" umgehen? Oder ist es garantiert, dass man nur Moleküle erhält, die aus Atomen mit nur einem Zeichen bestehen? – AJNeufeld

Antwort

3

Dies sieht aus wie ein Job für die Syntaxanalyse regulärer Ausdrücke mit java.util.Pattern und java.util.Matcher. Insbesondere möchten Sie, dass der Regex Gruppen angibt, sodass Sie über die group()-Methode unter Matcher auf jede Term als Gruppe zugreifen können.

Hier ist Code, der Ihr Eingabemolekül analysiert und Term Objekte instanziiert. Ich habe es aus dem Original mit einigen sehr hilfreiche Vorschläge aus @AJNeufeld ausgearbeitet.

public class MoleculeParser { 
    private static final Pattern PATTERN = Pattern.compile ("([A-Z][a-z]?)(\\d*)"); 

    public static List<Term> parseMolecule (String molecule) { 
     List<Term> terms = new ArrayList<>(); 

     Matcher matcher = PATTERN.matcher (molecule); 
     while (matcher.find()) { 
      String element = matcher.group(1); 
      String group2 = matcher.group(2); 
      if (!group2.isEmpty()) { 
       int atoms = Integer.parseInt (matcher.group(2)); 
       terms.add(new Term(element, atoms)); 
      } 
      else { 
       terms.add(new Term(element)); 
      } 
     } 

     return terms; 
    } 

    public static void main (String[] args) { 
     String str = "CH3CH2CH2CH2CH2CH3"; 
     System.out.println (parseMolecule (str)); 

     str = "C12H22O11 "; 
     System.out.println (parseMolecule (str)); 

     str = "SiCl4"; 
     System.out.println (parseMolecule (str)); 
    }  
} 


public class Term { 
    public Term (String element) { 
     this(element, 1); 
    } 

    public Term (String element, int atoms) { 
     _element = element; 
     _atoms = atoms; 
    } 

    @Override 
    public String toString() { 
     return _element + (_atoms != 1 ? _atoms : "") ; 
    } 

    private String _element; 
    private int _atoms; 
} 

Dieser Code nutzt die Leistung von regex Gruppen nicht nur Ihr unmittelbares Problem zu lösen, sondern auch die Analyse behandeln, die Sie wurden innerhalb von Term tun, und auch Atom mit zwei Zeichen in ihrem periodischen Symbol unterstützen.

Eine Regex- "Gruppe" ist ein Teil einer Regex-Übereinstimmung, auf die Sie sich dann per Index beziehen können. So können Regexes nicht nur verwendet werden, um zu testen, ob der vollständige Eingabeausdruck übereinstimmt, sondern auch, um den übereinstimmenden Eingabeausdruck zu analysieren und ihn in Teile zu zerlegen, die Teile der Regex selbst - d. H. Gruppen - entsprechen. Sie verwenden Klammern, um Gruppen in einem Regex zu definieren. Bitte überprüfen Sie alle Tutorial-Links, die ich am Ende dieses Posts erwähnt habe, damit Sie dies vollständig verstehen.

Im obigen Code durchläuft der Regex Matcher die Eingabe. Jeder Abschnitt, den es mit find() findet, stimmt mit der Gruppe von Gruppen überein, die in der Regex angegeben ist. Gruppe 0 ist immer die gesamte übereinstimmende Menge von Gruppen, Gruppe 1 ist die erste Gruppe und Gruppe 2 ist die zweite Gruppe. Da die zweite Gruppe (Atome) optional ist, prüfen wir, ob sie leer ist. Bitte beachten Sie auch, dass die Pattern Kompilation nur einmal vorkommen sollte, daher ihre Instanziierung als static final Konstante.

Das wichtigste ist hier, dass wir alle Parsing aus Term gezogen haben und alles in dieser Parsing-Routine zentralisiert haben.Um dies zu unterstützen, habe ich die Term Konstruktoren für Sie umgeschrieben.

Reguläre Ausdrücke verstehen ist für jede Art von Parsing essentiell. Ich empfehle the Oracle tutorial für eine gute Java-basierte Einführung. Vergewissere dich insbesondere, dass du the section on groups verstehst, da wir das oben genannte nutzen, um dein Molekül aufzubrechen.

Wie @AJNeufeld in den Kommentaren betont, wird meine Lösung keine komplexeren Moleküle wie Al2 (SO4) 3 unterstützen. Wenn Sie diesen Code und die genannten Tutorials verstehen, sollten Sie in der Lage sein, sie so zu modifizieren, dass sie solche Eingaben unterstützen.

+1

Ich würde '\\ d *' ... Sie könnten "Zucker" ... C12H22O11 ... mehrere Ziffern nach jedem Atom bekommen. – AJNeufeld

+0

Einverstanden - danke. Ich arbeite daran und unterstütze alle Elemente mit Namen, die länger als ein Buchstabe sind (d. H. Die meisten). –

+1

Ausgezeichnet! Darf ich weiter vorschlagen '(([AZ] [az]?) (\\ d *))', so dass 'Gruppe (1)' der ganze Begriff ist, 'Gruppe (2)' das atomare Symbol und ' Gruppe (3) 'ist entweder die Zählung (oder eine leere Zeichenfolge, die" 1 "impliziert). – AJNeufeld

0

Sie haben richtig angenommen, dass Sie eine for Schleife benötigen. Ihre for Schleife ist eigentlich sehr nahe zu korrigieren. Das einzige Problem ist, dass Sie nur hinzufügen if das aktuelle Zeichen ist ein Buchstabe und das nächste Zeichen ist auch ein Brief. Wenn Sie es wie folgt ändern:

for (int i = 0; i < s.length();i++) { 
    if ((Character.isUpperCase(s.charAt(i)))) { // If current character is a letter 
    if (Character.isUpperCase(s.charAt(i+1))) { 
     // If the next char is also letter, add current only 
     terms.add(new Term(s.substring(i,i+1))); 
     } else { 
     // If next char is number, add both 
     terms.add(new Term(s.substring(i,i+2))); 
     } 
    } 
} 

hoffe, das hilft!

0

Oldschool aber angewiesen:

static void lex(String s) { 
    String token = ""; 

    for (int i = 0; i < s.length(); i++) { 
     char c = s.charAt(i); 
     if (token.length() > 0 && !Character.isDigit(c)) { 
      System.out.println(token); 
      token = ""; 
     } 
     token += c; 
    } 

    if (!token.isEmpty()) { 
     System.out.println(token); 
    } 
} 
0

Hier ist ein Beispiel, das durch Parsen Bedingungen eine Arraylist erstellt. Es könnte auch Ausdrücke mit einem zweiten Kleinbuchstaben behandeln, wenn Sie den Term-Konstruktor aktualisieren.

public ArrayList<Term> initList(String initString) { 
    ArrayList<Term> terms = new ArrayList<Term>(); 
    int nextTerm = 0; 
    for(int i = 1; i < initString.length();i++) 
    { 
     while(i < initString.length() && 
       !Character.isUpperCase(initString.charAt(i))) { 
      i++; 
     } 
     // parse the next term from the nextTerm start index to the current i index 
     terms.add(new Term(initString.substring(nextTerm, i))); 
     nextTerm = i; 
    } 
    return terms; 
} 
Verwandte Themen