以符合现有代码期望的方式迭代可能空行

时间:2012-05-02 15:28:43

标签: string iterator lua pattern-matching

我曾经用简单的

迭代字符串中的行
for line in s:gmatch("[^\r\n]+") do

这完美无缺!然后现实生活发生了....

....现在我的要求发生了变化:我需要匹配空行太多。这就是问题所在:由于这种迭代形式在几个本身都是丛林的地方使用,我想避免尽可能地改变周围的代码。到目前为止,我对gmatch / find的尝试由于对上述功能无法匹配的微妙期望而无法为上述模式创建“替代”。

我寻求的构造(for line in some_matcher(s) do)符合以下要求,我认为这些要求涵盖了我担心的所有边缘案例:

   INPUT            EXPECTATIONS

1. ''            -- match once   ('')
2. '\r\n'        -- match twice  ('', '')
3. '\r\n\r\n'    -- match thrice ('', '', '')
4. 'aaa'         -- match once   ('aaa')
5. 'aaa\r\n'     -- match twice  ('aaa', '')
6. 'aaa\r\nbbb'  -- match twice  ('aaa', 'bbb')
7. '\r\nbbb'     -- match twice  ('', 'bbb')

我的旧解决方案假设仅发生\r\n行结束,如果新解决方案仅处理该情况,则完全正常。

然而,由于我的商店里有关于Linux支持计划的讨论,我们非常感谢能够解决更简单的\n行结束(以及将来的unix兼容性)的答案并保存重新访问这个问题几个月后来了。然而, catch 在很多情况下我需要原始字符串输入中的此匹配的起始列。如果上面的结构(for ... do)可以输出那个额外的好处,那将是特别棒的。

1 个答案:

答案 0 :(得分:2)

如果您需要准确地检测换行边界而不是跳过/丢弃它们,则无法使用包含\r\n的集合,因为它会匹配换行序列\r\n两次而不是一次(CRLF是DOS / Windows上的单个换行符)。您可以使用足够强大的正则表达式引擎(例如支持替换)处理此问题,但Lua的模式匹配库非常小。

最好的办法是在处理文本之前规范化换行符,如下所示:

function normalize_eols(s)
    return s
        :gsub('\r\n','\n')
        :gsub('\r', '\n')
end

至于你的input =>输出网格,如果我们将换行视为一行分隔符,那么我希望[^\n]*(零或多个非换行符)工作,但我们得到以下内容:

''         => ('')
'\n'       => ('', '')
'\n\n'     => ('', '', '')
'aaa'      => ('aaa', '')
'aaa\n'    => ('aaa', '', '')
'aaa\nbbb' => ('aaa', '', 'bbb', '')
'\nbbb'    => ('', 'bbb', '')
坦率地说,我不知道为什么。

但是,如果我们将换行视为一行终结符,那么我们可以通过在输入中附加换行符并使用模式[^\n]*\n(零或者)来获得所需的结果更多非换行字符后跟换行符):

'\n'         => ('')
'\n\n'       => ('', '')
'\n\n\n'     => ('', '', '')
'aaa\n'      => ('aaa')
'aaa\n\n'    => ('aaa', '')
'aaa\nbbb\n' => ('aaa', 'bbb')
'\nbbb\n'    => ('', 'bbb')

所以你的代码会改为:

s = normalize_eols(s) .. '\n'
for line in s:gmatch('([^\n]*)\n') do
    ...