문제

저는 Quickbook의 IIF 파일 형식으로 작업하고 있으며 IIF 파일을 읽고 쓰기 위해 파서를 작성해야 하는데 파일을 읽는 데 몇 가지 문제가 발생했습니다.

파일은 간단하며 탭으로 구분됩니다.모든 줄은 테이블 정의이거나 행입니다.정의는 '!'로 시작합니다. 테이블 이름과 행은 테이블 이름으로 시작합니다.내가 겪고 있는 문제는 다음과 같습니다.일부 필드에서는 줄 바꿈을 허용합니다.

처음 이 문제를 접했을 때 줄 단위가 아닌 탭 단위로 구문 분석하면 좋겠다고 생각했습니다. 하지만 그렇게 하려면 줄 바꿈을 탭으로 바꿔야 했고 열보다 더 많은 값으로 마무리해야 했지만 상처를 입었습니다. 줄 바꿈이 너무 많은 열에 분산된 값으로 구성됩니다.

그러한 파일을 어떻게 구문 분석하겠습니까?

편집하다:예

!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
도움이 되었습니까?

해결책

나는 그것을했다 :

 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;
    }

다른 팁

내가 IIF를 해낸 지 오래되었지만 그들이 수정하지 않으면 QuickBooks는 어쨌든 그 라인 브레이크에서 바프가됩니다. 보인다 이 사람들 같은 문제가 있고 공간으로 처리했습니다.

개인적으로 나는 파이프 또는 QuickBooks에있을 때 라인 브레이크를 명확하게 묘사 할 수있는 무언가를 기대할 것입니다. 당신이 절대적으로 긍정적으로 라인이 나누어야한다면, 가입하십시오. 직관 개발자 네트워크 프로그램이 가져 오면 SDK를 사용하여 이러한 값을 QB로 보냅니다.

라인 브레이크를 탭 대신 공백으로 교체하지 않겠습니까?

나는 항상 이런 종류의 일을 마친다. 핵심은 구문 분석을 할 때 이와 같은 특별한 경우를 처리하는 것입니다. 특별한 경우를 텍스트에서 발생하지 않을 가능성이 거의없는 것으로 대체 한 다음 완료되면 다시 교체하는 것입니다.

예를 들어, 출력에 라인 브레이크가 있으며, 이는 선수로 쉽게 감지 될 수 있습니다. regex.replace를 사용하여와 같은 것으로 변환하십시오 줄 바꿈. 디버깅의 편집기에서 눈에 띄는 것을 만드십시오. 그런 다음 나머지 구문 분석을 평소처럼 수행하고 마지막 단계로 특수 토큰을 원래 값 (또는 새로운 것)으로 바꾸십시오.

아이디어:

  1. 파일을 사전 처리하여 줄바꿈(단일 CR 또는 LF라고 ​​가정)을 일부 고급 ASCII 문자로 바꿉니다.그런 다음 탭으로 구문 분석하고 마지막으로 해당 고급 ASCII를 줄 바꿈으로 바꿉니다.

  2. 한 줄씩 처리하는 대신 문자별로 처리합니다.내장된 줄 바꿈이 레코드 끝의 표준 CRLF와 다소 다른 경우에만 여전히 작동합니다.

CodePlex에서 이것을 찾았습니다. 너겟 패키지를 사용하여 얻을 수 있습니다.https://qif.codeplex.com/

나는 그것을 테스트했고 Quick Book 형식을 읽고 쓸 수있었습니다.

라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top