简单的 CSV 词法分析器

Posted

技术标签:

【中文标题】简单的 CSV 词法分析器【英文标题】:Simple CSV lexer 【发布时间】:2014-10-19 13:11:21 【问题描述】:

我想用 pygments 为 CSV 文件着色,如下所示:

看到同一列用相同的颜色着色。

目前 pygments 不包含 CSV 解析器,因为 CSV 是 said to be obscure format。所以我试着自己写一个最小的。这是我尝试过的:

tokens = 
    'root': [
        (r'^[^,\n]+', Name.Function), # first column
        (',', Comment),               # separator
        (r'[^,\n]+', Name.Decorator), # second column
        (',', Comment),               # separator
        (r'[^,\n]+', Name.Constant),  # third column
        (',', Comment),               # separator
    ],

但它无法为任何列着色,但首先:

据我所知,pygments 的工作原理是尝试逐个匹配正则表达式:当当前的正则表达式不匹配时,它会转到下一个,然后重新开始。如果没有匹配项,则会发出错误并推进一个字符(并将该字符放入红色框中)。对于嵌套 cmets 等高级案例,有状态,但我认为对于 CSV,一个状态可能就足够了。

然后我尝试了:

tokens = 
    'root': [
        (',', Comment),                           # separator
        (r'^[^,\n]+', Name.Function),             # first column
        (r'(?:^[^,\n]+)[^,\n]+', Name.Decorator), # second column
    ],

但它会将所有列着色为第二个:

这是一个示例数据:

account_id,parent_account_id,name,status
,A001,English,active
A001,,Humanities,active
A003,A001,,active
A004,A002,Spanish,

在 Emacs 中,我设法得到了我想要的东西:

(add-hook 'csv-mode-hook
             (lambda ()
               "colors first 8 csv columns differently"
               (font-lock-add-keywords nil '(("^\\([^,\n]*\\),"
                                              1 'font-lock-function-name-face)))
               (font-lock-add-keywords nil '(("^\\([^,\n]*\\),\\([^,\n]*\\)"
                                              2 'font-lock-variable-name-face)))
               (font-lock-add-keywords nil '(("^\\([^,\n]*\\),\\([^,\n]*\\),\\([^,\n]*\\)"
                                              3 'font-lock-keyword-face)))
               (font-lock-add-keywords nil '(("^\\([^,\n]*\\),\\([^,\n]*\\),\\([^,\n]*\\),\\([^,\n]*\\)"
                                              4 'font-lock-type-face)))
))

(我实际上添加了超过 4 列,但这并不重要)

这给出了:

【问题讨论】:

你的第一段和第二段代码是一样的。 如果字段可以为空,您应该使用[^,\n]* 而不是[^,\n]+。这就是您的 Emacs 版本所做的。 @AntonSavin:修复了,抱歉。 @AlanMoore:如果我这样做,它就会挂起:两种变体。 【参考方案1】:

哦,我使用状态解决了它:

tokens = 
    'root': [
        (r'^[^,\n]*', Name.Function, 'second'),
    ],
    'second': [
        (r'(,)([^,\n]*)', bygroups(Comment, Name.Decorator), 'third'),
    ],
    'third': [
        (r'(,)([^,\n]*)', bygroups(Comment, Name.Constant), 'fourth'),
    ],
    'fourth': [
        (r'(,)([^,\n]*)', bygroups(Comment, Name.Variable), 'fifth'),
    ],
    'fifth': [
        (r'(,)([^,\n]*)', bygroups(Comment, Keyword.Type), 'unsupported'),
    ],
    'unsupported': [
        (r'.+', Comment),
        ],

它对前 5 个 CSV 列进行不同的着色,其他所有列作为评论:

【讨论】:

以上是关于简单的 CSV 词法分析器的主要内容,如果未能解决你的问题,请参考以下文章

C词法分析器的Python简单实现

小白天堂之编写词法语法分析器何其简单

编译原理-第二章 一个简单的语法指导编译器-2.2 词法分析

编译原理实验:实验一 简单词法分析程序设计(必修)(Python实现)

Java 实现《编译原理》简单词法分析功能

词法分析实验报告