我正在尝试使用TCL regexp解析简化的CSV格式。我选择regexp over split来执行基本格式一致性测试。
我的问题是我想使用计数量词,但想从匹配中排除','。
我的测试线:
set line "2017/08/21 16:06:20.0, REALTIME, late by 0.3, EOS450D, 1/640, F/8.0, ISO 100, Partial 450D 0.0%"
到目前为止,我有:
regexp -all {(?:([^\,]*)\,){8}} $line dummy date tm off cam exp fnum iso com
我的思维过程是: 获取所有不逗号到下一个逗号的字符的匹配组。 现在我想匹配这8次,所以我把它放入一个非捕获组,然后是一个计数量词。但这就失去了目的,因为现在没有什么是匹配的。我需要的是一种让匹配通过CSV 8次并捕获文本而不是逗号的方法。
我的CSV简化如下。 CSV中没有带引号的字符串 CSV中没有空条目
我已经检查了谷歌的csv匹配,但由于允许CSV内容中的特殊情况,大多数点击率都太过分了。
谢谢, 格特
答案 0 :(得分:2)
在regexp
命令中,-all
开关与匹配变量之间的交互是在最后一次匹配迭代中捕获的值用于填充变量。这意味着您可以通过使用一个捕获组并将其迭代匹配八次来填充八个变量。
你的正则表达式无论如何都不匹配,因为它在最后一个字段后需要逗号。
对于此特定示例,您可以使用调用
% regexp -all -inline {[^,]+} $line
{2017/08/21 16:06:20.0} { REALTIME} { late by 0.3} { EOS450D} { 1/640} { F/8.0} { ISO 100} { Partial 450D 0.0%}
这意味着要匹配不是逗号的所有字符组(请注意逗号并不特殊:您不需要将其转义)并将它们作为列表返回。
如您所述,这与使用
相同% split $line ,
(也快了约五倍)。
您不想使用split
,因为您想要进行一些验证:不清楚您想要做什么形式的验证,但您可以轻松验证找到的字段数量:
% set fields [split $line ,]
% if {[llength $fields] ne 8} {puts stderr "wrong number of fields"}
您可以将字段存储在变量中并单独验证它们,这比在提取变量时同时验证所有字段要容易得多:
lassign $fields date tm off cam exp fnum iso com
if {![regexp {ISO\s+\d+} $iso]} {puts stderr "in search of valid ISO"}
最好的方法仍然是使用csv
包拆分数据字符串。即使你现在只想使用这个简化的CSV,比你想象的更快,比如说允许带有逗号的字段。
package require csv
set fields [::csv::split $line]
文档: csv (package), if, lassign, llength, package, puts, regexp, set, split, Syntax of Tcl regular expressions
ETA:摆脱领先/尾随空格。这有点不寻常,因为CSV数据通常被安排为由分隔符分隔的严格重要文本字段。如果有任何要修剪的内容,通常在保存数据时完成。
一种好方法是将匹配的群组放在lmap
/ string trim
过滤器中:
lmap field [regexp -all -inline {[^,]+} $line] {string trim $field}
另一种方法是首先删除逗号周围的空格,然后拆分:
split [regsub -all {\s*,\s*} $line ,] ,
您可以使用以正则表达式分割的split
的Tcllib变体:
package require textutil
::textutil::splitx $line {\s*,\s*}
您还可以替换[^\s,][^,]*[^\s,]
的早期正则表达式(不匹配少于两个字符的字段)。这是一个正则表达式,即将变得过于复杂而无法发挥作用。