【问题标题】:TCL multi capture group for simplified csv string parsing with regexpTCL 多捕获组,用于使用正则表达式简化 csv 字符串解析
【发布时间】:2017-09-04 01:11:54
【问题描述】:

我正在尝试使用 TCL 正则表达式解析简化的 CSV 格式。我选择 regexp 而不是 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 内容中允许特殊情况,大多数点击都被夸大了。

谢谢, 格特

【问题讨论】:

  • 我现在无法测试,但查看代码似乎很可能因为最后一个字段后没有逗号而窒息(这是正确的)。我强烈建议使用 Tcllib 中的 csv 包来处理甚至简化的 csv。

标签: regex tcl


【解决方案1】:

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,](不会匹配少于两个字符的字段)。这是一个正则表达式,即将变得过于复杂而无用。

【讨论】:

  • 我喜欢建议的正则表达式。作为正则表达式的一部分,有没有办法修剪前导/尾随空格? (是的,以后可以修剪,但是如果可以合并到正则表达式返回的项目中就更好了。)
  • @GertGottschalk:我在 ETA 中提出了一些建议。
猜你喜欢
  • 2019-11-06
  • 2022-01-25
  • 2014-10-29
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-12-15
  • 2019-12-10
相关资源
最近更新 更多