2

这是一个正则表达式问题。

感谢您的帮助,请耐心等待,因为 RegEx 绝对不是我的强项!

完全作为背景...我问的原因是我想使用 RegEx 来解析类似于 SVG 路径数据段的字符串。我一直在寻找解析段及其段属性的先前答案,但没有发现任何可以正确执行后者的答案。

以下是一些示例字符串,例如我需要解析的字符串:

M-11.11,-22
L.33-44  
ac55         66 
h77  
M88 .99  
Z 

我需要将字符串解析成这样的数组:

["M", -11.11, -22]
["L", .33, -44]
["ac", 55, 66]
["h", 77]
["M", 88, .99]
["Z"]

到目前为止,我在这个答案上找到了这段代码: Parsing SVG "path" elements with C# - 是否有库可以做到这一点? 帖子是 C#,但正则表达式在 javascript 中很有用:

var argsRX = /[\s,]|(?=-)/; 
var args = segment.split(argsRX);

这是我得到的:

 [ "M", -11.11, -22, <empty element>  ]
 [ "L.33", -44, <empty>, <empty> ]
 [ "ac55", <empty>, <empty>, <empty>, 66 <empty>  ]
 [ "h77", <empty>, <empty>  
 [ "M88", .99, <empty>, <empty> ]
 [ "Z", <empty> ]

使用此正则表达式时的问题:

  • 一个不需要的空数组元素被放在每个字符串数组的末尾。
  • 如果多个空格是分隔符,则会为每个额外的空格创建一个不需要的空数组元素。
  • 如果数字紧跟在开头字母之后,则该数字将附加到字母上,但应成为单独的数组元素。

以下是传入字符串的更完整定义:

  • 每个字符串以 1 个或多个字母开头(大小写混合)。
  • 接下来是零个或多个数字。
  • 数字可能有减号(总是在前面)。
  • 数字可能在数字中的任何位置都有小数点(结尾除外)。
  • 可能的分隔符有:逗号、空格、空格、减号。
  • 前面或后面带有空格的逗号也是可能的分隔符。
  • 即使减号是分隔符,它们也必须与它们的数字保持一致。
  • 一个数字可能会紧跟在开头字母之后(没有空格),并且该数字应该是分开的。

这是我一直在使用的测试代码:

<!doctype html>
<html>
<head>
<link rel="stylesheet" type="text/css" media="all" href="css/reset.css" /> <!-- reset css -->
<script type="text/javascript" src="http://code.jquery.com/jquery.min.js"></script>

<style>
    body{ background-color: ivory; }
</style>

<script>
    $(function(){


var pathData = "M-11.11,-22 L.33-44  ac55    66 h77  M88 .99  Z" 

// separate pathData into segments
var segmentRX = /[a-z]+[^a-z]*/ig;
var segments = pathData.match(segmentRX);

for(var i=0;i<segments.length;i++){
    var segment=segments[i];
    //console.log(segment);

    var argsRX = /[\s,]|(?=-)/; 
    var args = segment.split(argsRX);
    for(var j=0;j<args.length;j++){
        var arg=args[j];
        console.log(arg.length+": "+arg);
    }

}

    }); // end $(function(){});
</script>

</head>

<body>
</body>
</html>
4

5 回答 5

3
^([az]+)(?:(-?\d*.?\d+)[^\d\n\r.-]*(-?\d*.?\d+)?)?

解释

^ # 字符串开头
([az]+) # 任意数量的字符,匹配到第 1 组
(?: # 非捕获组
  (-?\d*.?\d+) # 第一个数字(可选单数和小数点,数字)
  [^\d\n\r.-]* # 分隔字符(除了这些)
  (-?\d*.?\d+)? #第二个数字
)? # 结束非捕获组,设为可选

与“不区分大小写”标志一起使用。

于 2013-06-10T06:28:02.000 回答
2

您的“模式”由一个或多个字母组成,后跟一个十进制数字,然后是另一个以逗号或空格分隔的数字。

正则表达式:/([a-z]+)(-?(?:\d*\.)?\d+)(?:[,\s]+|(?=-))(-?(?:\d*\.)?\d+)/i

于 2013-06-10T06:12:42.053 回答
2

我必须对数据进行非常相似的解析,才能在全国最大的田径比赛中报告实时结果。http://ksathletics.com/2013/statetf/liveresults.js 虽然涉及到很多客户端和服务器端代码,但原理是一样的。事实上,这种数据实际上是相同的。

我建议您不要使用一个“巨型”正则表达式,而是使用一个分隔数据片段的表达式和另一个将每个数据片段分解为其主要标识符和以下值的表达式。这通过允许二级正则表达式匹配数据值的定义而不必区分分隔符,从而解决了各种分隔符的问题。(这也比将所有逻辑放入单个正则表达式更有效。)

这是一个经过测试可以处理您提供的输入的解决方案。

<script>
var pathData = "M-11.11,-22 L.33-44  ac55    66 h77  M88 .99  Z" 

function parseData(pathData) {
    var pieces = pathData.match(/([a-z]+[-.,\d ]*)/gi), i;
    /* now parse each piece into its own array */
    for (i=0; i<pieces.length; i++)
        pieces[i] = pieces[i].match(/([a-z]+|-?[.\d]*\d)/gi);
    return pieces;
}

pathPieces = parseData(pathData);
document.write(pathPieces.join('<br />'));
console.log(pathPieces);
</script>

http://dropoff.us/private/1370846040-1-test-path-data.html

更新:结果完全等同于您想要的指定输出。然而,我想到的一个想法是,您是否还想要或需要从字符串到数字的类型转换。你也需要那个吗?我只是在考虑解析数据之外的下一步。

于 2013-06-10T06:28:59.553 回答
2
function parsePathData(pathData)
{
    var tokenizer = /([a-z]+)|([+-]?(?:\d+\.?\d*|\.\d+))/gi,
        match,
        current,
        commands = [];

    tokenizer.lastIndex = 0;
    while (match = tokenizer.exec(pathData))
    {
        if (match[1])
        {
            if (current) commands.push(current);
            current = [ match[1] ];
        }
        else
        {
            if (!current) current = [];
            current.push(match[2]);
        }
    }
    if (current) commands.push(current);
    return commands;
}

var pathData = "M-11.11,-22 L.33-44  ac55    66 h77  M88 .99  Z";
var commands = parsePathData(pathData);
console.log(commands);

输出:

[ [ "M", "-11.11", "-22" ],
  [ "L", ".33", "-44" ],
  [ "ac", "55", "66" ],
  [ "h", "77" ],
  [ "M", "88", ".99" ],
  [ "Z" ] ]
于 2013-06-10T06:31:00.030 回答
1

你可以试试这个模式:

/([a-z]+)(-?(?:\d*\.)?\d+)?(?:\s+|,|(-(?:\d*\.)?\d+))?(-?(?:\d*\.)?\d+)?/

(有点长,但它似乎工作)

请注意,最后一个数字可以在捕获组中 \3 或 \4

于 2013-06-10T06:52:50.270 回答