Revisione del modello del parser orientato agli oggetti

4

La prima volta che uso la revisione del codice. Al momento sto creando un'applicazione Android e la società per la quale lo sto sviluppando ha un formato di file specifico che può essere utilizzato per fornire regole di formattazione specifiche basate su testo. Guardalo come un formato rich text appositamente creato e progettato per il loro uso.

Il formato del file con cui lavorare

Il formato del file è stato o almeno è a mio parere progettato per essere utilizzato per motivi di formattazione pura e in realtà non è molto ben progettato per l'accesso ai dati. L'applicazione Android che sto scrivendo dipende in modo molto strong dall'accesso ai dati, il che significa che ho dovuto progettare un parser intelligente che sia in grado di trasformare il testo formattato in una sorta di modello di dati che potrebbe avere ancora la formattazione inclusa.

Un file tipico potrebbe esistere su 600 righe di testo con un totale di 60000 caratteri. E potrebbe assomigliare a questo:

\id sit amet
\ide UTF-8
\rem File version: 2013-06-18
\rem Copyright 1995-2012
\t Lorem ipsum
\h 1
\st Nunc at urna
\p
\v 1 Lorem ipsum dolor sit amet, consectetur adipiscing elit.
\v 2 Suspendisse ac turpis quis diam elementum cursus sit amet non urna.
\pq Vestibulum vulputate \nd dui\nd* eget pharetra adipiscing.
\pq Suspendisse viverra metus quis feugiat rutrum.
\v 3 Nunc at urna vehicula, luctus quam convallis, adipiscing metus.
\v 4 Integer sit amet neque condimentum, laoreet neque ut, feugiat tellus.
\h 2
\v 1 Nam commodo orci et magna eleifend imperdiet.
\v 2 Cras a lorem ac nunc interdum lacinia.
\v 3 Integer vestibulum mauris id scelerisque lacinia.
\st Ut molestie nibh
\p
\v 4 Duis aliquam \nd diam\nd* nec nisl hendrerit posuere.
\v 5 Donec eget sapien id purus sollicitudin elementum non eget arcu.
\b
\v 6 Fusce auctor justo vitae aliquam luctus.
\v 7 Phasellus eget nisl at dui dignissim pharetra.
\v 8 Mauris iaculis mi at augue mattis, eu bibendum nunc placerat.

Il marcatore \h e il marcatore \v sono i marcatori più importanti per identificare i dati usando l'intero dietro. Gli altri produttori sono per lo più irrilevanti (come i marcatori \id, \ide, \rem ), e alcuni come \pq vengono usati per aggiungere i loro dati alla riga sopra di esso ma con qualche formattazione applicata ad esso. E alcuni marker sono "in-text" come il \nd marker.

Naturalmente ci sono molte regole su come i tag possono essere usati in un file valido, ma è del tutto irrilevante per la recensione che penso. (O c'è qualcuno disposto a leggere 90 pagine?)

La struttura del parser

Quindi ho trovato il seguente modello di analisi (orientato agli oggetti):

Il parser deve essere:

  • In grado di separare i dati da FORMATTING
  • Veloce
  • separa i dati e la formattazione in modo che entrambi possano essere facilmente accessibili
  • In grado di analizzare un sottoinsieme di dati o un intervallo specifico di dati da un file

Una selezione di dati viene spesso indicata come: File - Livello - Sottolivello (dati effettivi)

Dove file è solo un file da analizzare, level un numero intero che può essere trovato nel file dal marcatore \h e Sub-level proprio come Livello con \v maker.

Il parser che ho scritto restituisce un oggetto che contiene due liste, una con tutta la formattazione e una con tutti i dati.

public class ParseResult {
    protected ArrayList<FileData> data = new ArrayList<FileData>();
    protected ArrayList<FileFormat> format = new ArrayList<FileFormat>();
}

L'oggetto FileData e l'oggetto FileFormat possono essere estesi per creare numerosi formati e oggetti dati diversi. Questa è la struttura che utilizzo nel parser che ho scritto:

Per dati:

FileData  
    -FileDataLocation extends FileData  
        -FileDataText extends FileDataLocation  
        -FileDataThing extends FileDataLocation  
    -FileDataAnotherThing extends FileData  

Per la formattazione:

FileFormat  
    -FileFormatLevel extends FileFormat  
    -FileFormatTitle extends FileFormat  
    -FileFormatSubLevel extends FileFormat  
    -FileFormatSpecialTokens extends FileFormat 

Per utilizzare i dati dal file analizzato, utilizzo i dati ArrayList . Per stampare il ciclo I formattato in base al formato ArrayList che viene ordinato in modo che l'inizio del file sia nell'indice 0 della lista e la fine del file analizzato sia l'ultimo indice della lista.

Ogni oggetto di formattazione ha un riferimento a un oggetto di dati nella lista dei dati. In modo che quando si utilizzano gli oggetti di formattazione i dati siano sempre accessibili. Gli oggetti di formattazione contengono solo informazioni su quale pace dei dati deve essere applicato il formato e su quale tipo di formattazione.

Metodo di analisi

Quando analizzi un file, il parser legge il file riga per riga e tiene costantemente traccia dei marcatori \h e \v e inizia a catturare i dati nell'oggetto ParseResult quando viene trovata una combinazione specifica dei marcatori ( Consente di chiamare una posizione).

Per verificare quale tipo di marker il parser sta trattando, scruto la linea fino a trovare il primo spazio bianco. Quando è stato trovato uno spazio bianco, è possibile eseguire una sottostringa dall'inizio della riga alla posizione dello spazio bianco.

Confrontando la sottostringa con un elenco di produttori noti si dice al parser cosa fare con la linea.

Dopo che il marker è stato trovato, la riga viene elaborata usando regex. Questo è un esempio di espressione regolare per una riga con l'indicatore \v :

\v\s(([0-9]{1,3})(?:-([0-9]{1,3}))?)\s{1}(.+)[\n\r$]*

Commenta?

Le prestazioni del parser non sono affatto male, i file più grandi richiedono circa 600ms per essere processati completamente (9000 righe, 280000 caratteri).

Cose che vorrei sapere

  • I dati sono ora (dopo l'analisi) memorizzati in un solo elenco, la ricerca di una pace dei dati specifica non è ottimizzata molto bene. Ci sono altre strutture che potrei usare, che posso cercare usando file(string) - level(integer) - sub-level(integer) così posso ottenere rapidamente un oggetto dati specifico.
  • Quando esegui il ciclo attraverso l'elenco di formattazione, devo verificare a quale istanza appartiene un oggetto, quindi lanciare l'oggetto in modo da poter usare i metodi e le funzioni specifici. Esiste un migliore, altro metodo alternativo?
  • Il formato del file è piuttosto semplice, dovrei prendere in considerazione la rimozione di tutte le funzioni regex e analizzarlo tutto solo con un codice normale?
posta Rolf ツ 19.11.2013 - 17:53
fonte

1 risposta

6

I dati sono ora (dopo l'analisi) memorizzati in un solo elenco, la ricerca di una pace dei dati specifica non è ottimizzata molto bene. Ci sono altre strutture che potrei usare, che posso cercare usando file (string) - level (integer) - sub-level (integer) così posso ottenere rapidamente un oggetto dati specifico.

Supponendo che tu voglia cercare solo per File, Livello, Sottolivello , hai una chiara struttura gerarchica in quella descrizione, sembra che tu possa dividere un ArrayList in più passaggi. Puoi persino rendere ogni livello gerarchico di livello una classe per renderlo più chiaro, ad esempio:

class SomeFileData {
   List<LevelData> levels;
}

class LevelData {
   List<SubLevelData> sublevels;
}

class SubLevelData {
   // Probably similar to your current 'FileData' implementation
}

E se hai bisogno di salvare più file contemporaneamente, puoi usare Map con il nome del file come chiave.

class LotsOfFileData {
    Map<String, SomeFileData> files; 
}

Utilizzando questa gerarchia, puoi facilmente ottenere il file "mydata.dat", livello 3, sublevel 4.

LotsOfFileData allData;
allData.getFile("mydata.dat").getLevel(3).getSublevel(4)

Ogni passaggio nel processo get esamina l'elenco o la mappa della classe corrente per recuperare i dati.

A proposito, dichiarare le variabili in base alla loro interfaccia, non la loro implementazione . Dichiara il tuo ArrayList s come List per consentire un facile cambio di implementazione.

    
risposta data 19.11.2013 - 22:41
fonte

Leggi altre domande sui tag