尝试解析由 ( , ) 分隔并使用引号的 CSV 文件

Trying to parse a CSV file, delimited by ( , ) and using quotes

我在网上找不到太多帮助。我有一个要解析的 CSV 文件。分隔符是一个逗号,但是如果它是字段的一部分,我希望忽略它,所以我使用引号。当我在我的领域没有逗号时,我的方法很有效。但是,当我尝试通过向其中一个字段添加逗号来尝试将其视为单个记录时,我收到 ArrayIndexOutOfBoundsException 错误。这是我的代码。我 运行 它脱离了 AsyncTask。您会注意到我插入了代码 - r.get(1); r.get(2); 这仅用于测试。 r.get(1) 是抛出错误的行

class ParseCsvTask extends AsyncTask<File, Void, Void>{

        @Override
        protected void onPreExecute() {
            mProgressBar.setVisibility(View.VISIBLE);
        }

        @Override
        protected Void doInBackground(File... files) {
            BufferedReader reader = null;
            CSVParser parser = null;


            File file = files[0];

            CSVFormat formatter = CSVFormat.RFC4180.withFirstRecordAsHeader();

            try {
                reader = new BufferedReader(new FileReader(file));

                parser = CSVParser.parse(reader, formatter);

                List<CSVRecord> list = parser.getRecords();

                for (CSVRecord r : list) {
                    r.get(1);
                    r.get(2);
                    Competitor competitor = new Competitor(r.get(1), r.get(2));
                    if (!r.get(0).equals("")) {
                        competitor.setMemberNum(r.get(0));
                    }
                    if(!r.get(4).equals("")){
                        competitor.setEmail(r.get(4));
                    }
                    if(!r.get(5).equals("")){
                        competitor.setPhone(r.get(5));
                    }

                    switch (r.get(7)){
                        case "":
                            competitor.setAge(Competitor.Age.ADULT);
                            break;
                        case "Junior":
                            competitor.setAge(Competitor.Age.JUNIOR);
                            break;
                        case "Senior":
                            competitor.setAge(Competitor.Age.SENIOR);
                            break;
                        case "Super Senior":
                            competitor.setAge(Competitor.Age.SUPER_SENIOR);
                            break;
                        default:
                            break;
                    }

                    if(r.get(8).equals("")){
                        competitor.setLady(false);
                    } else {
                        competitor.setLady(true);
                    }

                    mImportedComps.add(competitor);

                }

                FileHelper.writeMasterCompetitorsFile(mContext, mImportedComps);

                Intent intent = new Intent(mContext, MasterCompetitorListActivity.class);
                startActivity(intent);

            } catch (Exception e) {
                e.printStackTrace();
                Log.d("record", "what is going on");
            } finally {
                try {
                    assert parser != null;
                    parser.close();
                    reader.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }

            return null;
        }

        @Override
        protected void onPostExecute(Void aVoid) {
            mProgressBar.setVisibility(View.INVISIBLE);
        }
    }

记住:当我在记录中不使用逗号时效果很好。 "first name" 工作正常,但如果记录显示 "first , name " 我会收到错误消息。 另外,我正在使用 *org.apache.commons.csv*

有人建议我 post 提出的这个问题可能与此 post: Apache commons CSV: quoted input doesn't work 重复。这个 post 的错误是 invalid char between encapsulated token and delimiter 而我的错误与数组索引越界有关这一事实清楚地表明我们正在处理不同的场景。我没有被告知分隔符之间有任何无效字符。我的情况有所不同

这是我捕获此错误时调用的堆栈跟踪:

03-05 15:34:44.397 778-778/com.checkinsystems.ez_score D/ViewRootImpl@4ca832c[MasterCompetitorListActivity]: ViewPostImeInputStage processPointer 0
03-05 15:34:44.479 778-778/com.checkinsystems.ez_score D/ViewRootImpl@4ca832c[MasterCompetitorListActivity]: ViewPostImeInputStage processPointer 1
03-05 15:34:44.550 778-825/com.checkinsystems.ez_score W/System.err: java.lang.ArrayIndexOutOfBoundsException: length=1; index=1
03-05 15:34:44.550 778-825/com.checkinsystems.ez_score W/System.err:     at org.apache.commons.csv.CSVRecord.get(CSVRecord.java:79)
03-05 15:34:44.550 778-825/com.checkinsystems.ez_score W/System.err:     at com.checkinsystems.ez_score.ImportMasterCompsFileFragment$ParseCsvTask.doInBackground(ImportMasterCompsFileFragment.java:186)
03-05 15:34:44.550 778-825/com.checkinsystems.ez_score W/System.err:     at com.checkinsystems.ez_score.ImportMasterCompsFileFragment$ParseCsvTask.doInBackground(ImportMasterCompsFileFragment.java:158)
03-05 15:34:44.550 778-825/com.checkinsystems.ez_score W/System.err:     at android.os.AsyncTask.call(AsyncTask.java:304)
03-05 15:34:44.550 778-825/com.checkinsystems.ez_score W/System.err:     at java.util.concurrent.FutureTask.run(FutureTask.java:237)
03-05 15:34:44.550 778-825/com.checkinsystems.ez_score W/System.err:     at android.os.AsyncTask$SerialExecutor.run(AsyncTask.java:243)
03-05 15:34:44.550 778-825/com.checkinsystems.ez_score W/System.err:     at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1133)
03-05 15:34:44.550 778-825/com.checkinsystems.ez_score W/System.err:     at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:607)
03-05 15:34:44.550 778-825/com.checkinsystems.ez_score W/System.err:     at java.lang.Thread.run(Thread.java:762)
03-05 15:34:44.550 778-825/com.checkinsystems.ez_score D/record: what is going on

所以我发现了为什么会抛出 ArrayIndexOutOfBounds 错误。 我运行代码:

for(CSVRecord  r : list){
                    Log.d("record", r.toString());
                }

刚刚拿到名单。我注意到由于某种原因,我得到了一条空白记录,然后是正确的记录。换句话说,这种模式重复出现,我以某种方式获得的记录数量是我需要的两倍,但其他记录都是空白的,这就是我会遇到索引问题的原因。但我仍然不明白为什么我会收到这些空白记录。这是调用代码的按钮 onClick:

@Override
        public void onClick(View view) {

            File file = new File(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS).getAbsolutePath()
                    + "/" + mFileName.getText().toString());

            new ParseCsvTask().execute(file);

        }

这是一些 logcat 输出....我更改了数据以隐藏人们的信息:

03-05 16:25:40.223 13019-13633/com.checkinsystems.ez_score D/record: CSVRecord [comment=null, mapping={member=0, first name=1, last name=2, name=3, email=4, phone=5, squad=6, age=7, gender=8, division=9, power factor=10, class=11, special =12}, recordNumber=1, values=[]]
03-05 16:25:40.223 13019-13633/com.checkinsystems.ez_score D/record: CSVRecord [comment=null, mapping={member=0, first name=1, last name=2, name=3, email=4, phone=5, squad=6, age=7, gender=8, division=9, power factor=10, class=11, special =12}, recordNumber=2, values=[A9J41, Bob, Al,len, Bob Allen, bob@comcast.net, 5555555555, 7, , , Production, Minor, D, ]]
03-05 16:25:40.223 13019-13633/com.checkinsystems.ez_score D/record: CSVRecord [comment=null, mapping={member=0, first name=1, last name=2, name=3, email=4, phone=5, squad=6, age=7, gender=8, division=9, power factor=10, class=11, special =12}, recordNumber=3, values=[]]
03-05 16:25:40.223 13019-13633/com.checkinsystems.ez_score D/record: CSVRecord [comment=null, mapping={member=0, first name=1, last name=2, name=3, email=4, phone=5, squad=6, age=7, gender=8, division=9, power factor=10, class=11, special =12}, recordNumber=4, values=[TY912111, Fred , Jones , Fred Jones , fred@gmail.com, 5555555555, 5, , , Revolver, Minor, C, ]]

请记住,只有当我在第一条记录的姓氏中间添加逗号时才会发生这种情况。如果我去掉那个逗号,它就可以正常工作

我解决了!我使用的是依赖 RFC4180 标准的格式化程序。这个标准默认是这样的:

withDelimiter(',')
withQuote('"')
withRecordSeparator("\r\n")
withIgnoreEmptyLines(false)

最后一个属性 withIgnoreEmptyLines 需要设置为 true,否则格式化程序会在每隔一条记录之后插入一条空白记录。我不确定为什么在我的记录之间插入空白记录是标准,但我用这一行修复了它:

CSVFormat formatter = CSVFormat.RFC4180.withFirstRecordAsHeader()
                    .withIgnoreEmptyLines(true);

这就是我得到 ArrayIndexOutOfBounds

的原因

我希望这对其他人有帮助。感谢大家帮我解决这个问题