欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

ANTLR4(七) 加载CSV数据

程序员文章站 2022-04-13 14:09:44
...

需求

目标:将.csv文件的第一行作为列名,将接下来的每行的信息提取出来并打印。

输入t.csv
ANTLR4(七) 加载CSV数据
输出
ANTLR4(七) 加载CSV数据

初步构想

我们将语法应用程序部分解耦合。

语法部分

由于首行作为列名,我们需要将它与普通的行区分开。

每行的元素可以是String、text、甚至是空。

应用程序部分

保存首行的值作为列名。

创建一个保存所有行信息的List。

在进入改行时,每一行建立一个map,将首行的列名与值一个一个对应起来输入。

在离开该行时,将该行内容输出。

实际实现

语法部分

首先是row规则,‘\r’? 是为了判断有没有回车符,无论匹配与否,都换行。

TEXT词法规则中,由于, “ \n \r都需要被实际匹配,因此标识符的匹配中不能有它们。

STRING词法规则中,我们用了‘ ”“ ’|~‘ ” ’而不是‘ ”“ ’|‘ . ’去匹配双引号中的双引号。这是因为非贪婪匹配的规则是 在满足匹配父规则情况下,尽可能少的匹配字符 。简单地说 “x""y" 在匹配时,我们希望匹配的是 x“”y 这一整个包含“”的字符串,而非贪婪匹配会使得我们匹配到“x”“y“两个子字符串,因为显然 x 的字符数量要比 x“”y 少的多。所以,我们在"“中碰到单个"需要跳过,只有碰到”"才会匹配。

注意#text等标签:由于我们将调用Listener模式,而该模式本身只对文法规则设置exit和enter,我们需要对词法规则也加上标签。

grammar CSV;

file
    :   hdr row+
    ;

hdr
    :   row
    ;

row
    :   field (',' field)* '\r'? '\n'
    ;

field
    :   TEXT        #text
    |   STRING      #string
    |               #empty
    ;

TEXT
    :   ~[,"\n\r]+
    ;

STRING
    :  '"' ('""'|~'"')* '"'
    ;

应用程序部分

在初步构想中,我们的想法是接近的,但是逻辑不够严谨。

首先我们先将自定义Listener将会用到的数据结构定义一下:

  1. 创建一个List<map<String,String>> rows 保存所有信息。
  2. 创建一个List header 保存列名。
  3. 创建一个List currentRowFieldValue 保存当前行的field的值。

然后从hdr规则入手,递归地去考虑两种情况。

首行情况:

  1. enterHdr:什么都不做。
  2. enterRow: 实例化currentRowFieldValue
  3. enterString enterText enterEmpty:什么都不做。
  4. exitstring exitText exitEmpty :添加每行的内容到currentRowFieldValue
  5. exitRow:如果上层是hdr,直接return;
  6. exitHdr:实例化header,将currentRowFieldValue保存到header中。

其它行情况:

  1. enterRow: 重新实例化currentRowFieldValue
  2. enterString enterText enterEmpty:什么都不做。
  3. exitString exitText exitEmpty :添加内容到currentRowFieldValue
  4. exitRow:如果不是,根据列名,一个个插入rows中。

编码细节:

  1. 注意extends(自定义Listener继承自BaseListener)和implements(BaseListener实现了Listener接口)区别。
    extends与implements区别
  2. 实例化List->ArrayList,实例化Map->LinkedHashMap
  3. 判断row的父结点是否为hdr:ctx.getParent().getRuleIndex()==CSVParser.Rule_hdr
  4. Map添加是put,List添加是add
import org.antlr.v4.runtime.ANTLRInputStream;
import org.antlr.v4.runtime.CommonTokenStream;
import org.antlr.v4.runtime.tree.ParseTree;
import org.antlr.v4.runtime.tree.ParseTreeWalker;

import java.io.FileInputStream;
import java.io.InputStream;
import java.util.*;

public class LoadCSV {

    public static class Loader extends CSVBaseListener {

        public static final String EMPTY="";

        List<Map<String,String>> rows=new ArrayList<Map<String,String>>();

        List<String> header;

        List<String> currentRowFieldValues;

        public void exitHdr(CSVParser.HdrContext ctx) {
            header=new ArrayList<String>();
            header.addAll(currentRowFieldValues);
        }

        public void enterRow(CSVParser.RowContext ctx){
            currentRowFieldValues=new ArrayList<String>();
        }

        public void exitString(CSVParser.StringContext ctx){
            currentRowFieldValues.add(ctx.STRING().getText());
        }

        public void exitText(CSVParser.TextContext ctx){
            currentRowFieldValues.add(ctx.TEXT().getText());
        }

        public void exitEmpty(CSVParser.EmptyContext ctx){
            currentRowFieldValues.add(EMPTY);
        }

        public void exitRow(CSVParser.RowContext ctx){
            if(ctx.getParent().getRuleIndex()==CSVParser.RULE_hdr){
                return ;
            }
            else{
                Map<String,String> m=new LinkedHashMap<String,String>();
                int i=0;
                for(String v:currentRowFieldValues){
                    m.put(header.get(i),v);
                    ++i;
                }
                rows.add(m);
            }
        }

    }

    public static void main(String []args) throws Exception{
	...
        ParseTreeWalker walker=new ParseTreeWalker();
        Loader loader=new Loader();
        walker.walk(loader,tree);
        System.out.println(loader.rows);

    }


}
相关标签: ANTLR4 ANTLR4 CSV