使用RegEx解析具有复杂分隔的字符串

时间:2013-06-10 06:08:09

标签: javascript regex

这是一个RegEx问题。

感谢您的帮助,请耐心等待,因为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# - are there libraries out there to do this?帖子是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>

5 个答案:

答案 0 :(得分:3)

^([a-z]+)(?:(-?\d*.?\d+)[^\d\n\r.-]*(-?\d*.?\d+)?)?

解释

^               # start of string
([a-z]+)        # any number of characters, match into group 1
(?:             # non-capturing group
  (-?\d*.?\d+)  #   first number (optional singn & decimal point, digits)
  [^\d\n\r.-]*  #   delimiting characters (anything but these)
  (-?\d*.?\d+)? #   second number
)?              # end non-capturing group, make optional

使用“不区分大小写”标记。

答案 1 :(得分:2)

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

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

答案 2 :(得分:2)

我必须执行非常类似的数据解析,以便在全国最大的赛道会议上报告实时结果。 http://ksathletics.com/2013/statetf/liveresults.js虽然涉及很多客户端和服务器端代码,但原则是相同的。事实上,这类数据实际上是相同的。

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

这是一个经过测试的解决方案,可用于您提供的输入。

<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

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

答案 3 :(得分: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" ] ]

答案 4 :(得分:1)

您可以尝试使用此模式:

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

(有点长,但似乎有效)

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