Frage

Ich arbeite mit Quickbook des IIF-Dateiformat und ich brauche einen Parser zu schreiben, zu lesen und IIF-Dateien zu schreiben und ich bin in einige Probleme laufen, die Dateien zu lesen.

Die Dateien sind einfach, sie sind Registerkarte deliminated. Jede Zeile ist entweder eine Tabellendefinition oder eine Zeile. Definitionen beginnen mit ‚!‘ und der Name der Tabelle, und Zeilen beginnen mit nur den Tabellennamen. Hier ist das Problem in Ich bin mit: einige der Felder Zeilenumbrüche erlauben.

Als ich dies begegnete, dachte ich, okay es nur analysieren Registerkarte von Tab statt Zeile für Zeile, aber zu tun, dass ich die Zeilenumbrüche mit Tabs zu ersetzen hatte, und gewickelt mit mehr Werten bis als es waren Säulen, aber gewickelt ich mit den Werten mit Zeilenumbrüchen verteilt über zu viele Spalten auf.

Wie würden Sie eine solche Datei analysieren?

Edit: Ein Beispiel

!CUST   NAME    REFNUM  TIMESTAMP   BADDR1  BADDR2  BADDR3  BADDR4  BADDR5  SADDR1  SADDR2  SADDR3  SADDR4  SADDR5  PHONE1  PHONE2  FAXNUM  CONT1   CONT2   CTYPE   TERMS   TAXABLE LIMIT   RESALENUM   REP TAXITEM NOTEPAD SALUTATION  COMPANYNAME FIRSTNAME   MIDINIT LASTNAME    CUSTFLD1    CUSTFLD2    CUSTFLD3    CUSTFLD4    CUSTFLD5    CUSTFLD6    CUSTFLD7    CUSTFLD8    CUSTFLD9    CUSTFLD10   CUSTFLD11   CUSTFLD12   CUSTFLD13   CUSTFLD14   CUSTFLD15   JOBDESC JOBTYPE JOBSTATUS   JOBSTART    JOBPROJEND  JOBEND  HIDDEN  DELCOUNT
CUST    St. Mark    359 1176670332  Saint Mark Catholic Church  609 W Main St   City, State Zip 
!CLASS  NAME    REFNUM  TIMESTAMP   HIDDEN  DELCOUNT
!INVITEM    NAME    REFNUM  TIMESTAMP   INVITEMTYPE DESC    PURCHASEDESC    ACCNT   ASSETACCNT  COGSACCNT   QNTY    QNTY    PRICE   COST    TAXABLE PAYMETH TAXVEND TAXDIST PREFVEND    REORDERPOINT    EXTRA   CUSTFLD1    CUSTFLD2    CUSTFLD3    CUSTFLD4    CUSTFLD5    DEP_TYPE    ISPASSEDTHRU    HIDDEN  DELCOUNT    USEID
INVITEM Labor   1   1119915308  SERV    Labor                                                                                               0
!TIMEACT    DATE    JOB EMP ITEM    PITEM   DURATION    PROJ    NOTE    XFERTOPAYROLL   BILLINGSTATUS
TIMEACT 3/8/08  876 Development Jane Doe {Consultant}   Labor       00:15       Renewing all domain name for 876 Development.
REIMBURSEMENT: 44.72 for one year renewal on all domain names.  N   1
TIMEACT 3/17/08 Greg:Bridge Jane Doe {Consultant}   Labor       01:00       Preparing Studio    N   1
TIMEACT 3/17/08 John Doe and Associates Jane Doe {Consultant}   Labor       00:06       REIMBURSEMENT: Toner cartridge on ebay & Fuser from FastPrinters- ask wendell before invoicing to see if this fixed the problem
49.99 (include tax) toner
$175.18 (include tax) fuser
    N   1
TIMEACT 3/17/08 John Doe II Jane Doe {Consultant}   Labor       01:00       Fixing Kandis's computer - replaced entire computer with similar system N   1
War es hilfreich?

Lösung

Ich habe es:

 public DataSet parseIIF(Stream file) {
            iifSet = new DataSet();
            String fileText;

            using (StreamReader sr = new StreamReader(file)) {
                fileText = sr.ReadToEnd();
            }
            //replace line breaks with tabs
            //fileText.Replace('\n', '\t');
            fileText = fileText.Replace("\r\n", "\n");
            fileText = fileText.Replace('\r', '\n');

            //split along tabs
            string[] lines = fileText.Split('\n');

            this.createTables(lines, iifSet);
            this.fillSet(lines, iifSet);

            return iifSet;
        }

        /// <summary>
        /// Reads an array of lines and parses them into tables for the dataset
        /// </summary>
        /// <param name="lines">String Array of lines from the iif file</param>
        /// <param name="iifSet">DataSet to be manipulated</param>
        private void fillSet(string[] lines, DataSet set) {
            //CODING HORROR
            //WARNING: I will monkey with the for loop index, be prepared!
            for (int i = 0; i < lines.Length; i++) {
                if (this.isTableHeader(lines[i])) {
                    //ignore this line, tables are alread defined
                    continue;
                }
                if (lines[i] == "" || lines[i] == "\r" || lines[i] == "\n\r" || lines[i] == "\n") {
                    //ignore lines that are empty if not inside a record
                    //probably the end of the file, it always ends with a blank line break
                    continue;
                }

                if (lines[i].IndexOf(";__IMPORTED__") != -1) {
                    continue;
                    //just signifying that it's been imported by quickbook's timer before, don't need it
                }

                string line = lines[i];
                while (!isFullLine(line, set)){
                    i++;            //<--------------------------- MONKEYING done here!
                    line += lines[i];       
                }
                //now, the line should be complete, we can parse it by tabs now
                this.parseRecord(line, set);
            }
        }

        private void parseRecord(string line, DataSet set) {
            if (isTableHeader(line)) {
                //we don't want to deal with headers here
                return;
            }

            String tablename = line.Split('\t')[0];
            //this just removes the first value and the line break from the last value
            String[] parameters = this.createDataRowParams(line);

            //add it to the dataset
            set.Tables[tablename].Rows.Add(parameters);
        }

        private bool isFullLine(string line, DataSet set) {
            if (isTableHeader(line)) {
                return true;    //assumes table headers won't have line breaks
            }
            int values = line.Split('\t').Length;
            string tableName = line.Split('\t')[0];
            int columns = set.Tables[tableName].Columns.Count;

            if (values < columns) {
                return false;
            } else {
                return true;
            }
        }

        private void createTables(string[] lines, DataSet set) {
            for (int index = 0; index < lines.Length; index++) {
                if (this.isTableHeader(lines[index])) {
                    set.Tables.Add(createTable(lines[index]));
                }
            }
        }

        private bool isTableHeader(string tab) {
            if (tab.StartsWith("!"))
                return true;
            else
                return false;
        }

        private bool isNewLine(string p) {
            if (p.StartsWith("!"))
                return true;
            if (iifSet.Tables[p.Split('\t')[0]] != null)    //that little mess there grabs the first record in the line, sorry about the mess
                return true;
            return false;
        }

    private DataTable createTable(string line) {
        String[] values = line.Split('\t');

        //first value is always the title
        //remove the ! from it
        values[0] = values[0].Substring(1);     //remove the first character
        DataTable dt = new DataTable(values[0]);
        values[0] = null;   //hide first title so it doesn't get used, cheaper than resetting array
        foreach (String name in values) {
            if (name == null || name == "")
                continue;
            DataColumn dc = new DataColumn(name, typeof(String));
            try {
                dt.Columns.Add(dc);
            } catch (DuplicateNameException) {
                //odd
                dc = new DataColumn(name + "_duplicateCol" + dt.Columns.Count.ToString());
                dt.Columns.Add(dc);
                //if there is a triple, just throw it
            }
        }

        return dt;
    }

   private string getTableName(string line) {
        String[] values = line.Split('\t');

        //first value is always the title
        if(values[0].StartsWith("!")){
            //remove the ! from it
            values[0] = values[0].Substring(1);     //remove the first character
        }
        return values[0];
    }

    private string[] createDataRowParams(string line) {
        string[] raw = line.Split('\t');
        string[] values = new string[raw.Length - 1];

        //copy all values except the first one
        for (int i = 0; i < values.Length; i++) {
            values[i] = raw[i + 1];
        }

        //remove last line break from the record
        if (values[values.Length - 1].EndsWith("\n")) {
            values[values.Length - 1] = values[values.Length - 1].Substring(0, values[values.Length - 1].LastIndexOf('\n'));
        } else if (values[values.Length - 1].EndsWith("\n\r")) {
            values[values.Length - 1] = values[values.Length - 1].Substring(0, values[values.Length - 1].LastIndexOf("\n\r"));
        } else if (values[values.Length - 1].EndsWith("\r")) {
            values[values.Length - 1] = values[values.Length - 1].Substring(0, values[values.Length - 1].LastIndexOf('\r'));
        }


        return values;
    }

    private string[] createDataRowParams(string line, int max) {
        string[] raw = line.Split('\t');

        int length = raw.Length - 1;
        if (length > max) {
            length = max;
        }

        string[] values = new string[length];
        for (int i = 0; i < length; i++) {
            values[i] = raw[i + 1];
        }

        if (values[values.Length - 1].EndsWith("\n")) {
            values[values.Length - 1] = values[values.Length - 1].Substring(0, values[values.Length - 1].LastIndexOf('\n'));
        } else if (values[values.Length - 1].EndsWith("\n\r")) {
            values[values.Length - 1] = values[values.Length - 1].Substring(0, values[values.Length - 1].LastIndexOf("\n\r"));
        } else if (values[values.Length - 1].EndsWith("\r")) {
            values[values.Length - 1] = values[values.Length - 1].Substring(0, values[values.Length - 1].LastIndexOf('\r'));
        }

        return values;
    }

Andere Tipps

Es ist schon eine Weile her, seit ich IIF getan haben, aber wenn sie es behoben haben sowieso Quickbooks wird barf auf diese Zeilenumbrüche. Es scheint, diese Leute das gleiche Problem haben, und sie behandelt es mit Leerzeichen.

Persönlich würde ich in Richtung Rohre lehnen oder etwas, das den Zeilenumbruch deutlich abgrenzen wird, wenn es in Quickbooks kommt. Wenn Sie absolut positiv muss die Zeilenumbrüche haben, nehmen Sie den Intuit Developer Network und verwenden Sie das SDK, diese Werte zu senden QB einmal sich Ihr Programm importiert.

Warum nicht ersetzen Zeilenumbrüche mit Leerzeichen anstelle von Tabulatoren?

Ich laufe in dieser Art der Sache die ganze Zeit. Der Schlüssel ist, um Umgang mit speziellen Fällen wie diesem, wenn Sie das Parsen tust ist der Spezialfall mit etwas zu ersetzen, die extrem unwahrscheinlich ist, im Text auftreten, und es dann wieder zu ersetzen, wenn Sie fertig sind.

Zum Beispiel haben Sie Zeilenumbrüche in der Ausgabe, die ohne weiteres mit einem Regex nachgewiesen werden kann. Verwenden Regex.Replace wandeln sie in so etwas wie LINEBREAK . Machen Sie es etwas, das für das Debuggen in einem Editor abhebt. Dann tun Sie den Rest Ihres Parsing als normal, und als letzten Schritt, ersetzen Sie die spezielle Token mit dem ursprünglichen Wert (oder etwas neu).

Ideen:

  1. Preprocess Datei, Zeilenumbruch zu ersetzen (vorausgesetzt, es ist ein einzelner CR oder LF) mit einigen High-ASCII-Zeichen. Dann durch Tab analysieren und schließlich ersetzen die Hoch ascii mit dem Leitungsunterbrecher danach.

  2. Anstatt Prozess zeilen, Prozess zeichenweise. Wohlgemerkt, die noch funktioniert nur, wenn die eingebetteten Zeilenumbrüche irgendwie anders als der Standard CRLF am Ende eines Datensatzes sind.

Ich fand dies auf CodePlex. Sie können es mit dem Nugget Paket. https://qif.codeplex.com/

Ich habe es getestet und ich konnte schnell Buchformat lesen und schreiben.

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top