在Lua中,将输入中的双反斜杠替换为单个反斜杠

3
假设我有一个名为str的变量,它的值被赋为test\\ttest(或者对于这种情况,它实际上只是\\)。我的目标是用单个反斜杠替换双反斜杠。
目的很明确:我想输出\t转义序列(水平制表符),而现在它只是以纯文本\t的形式输出。
显然,我不能使用:
str:gsub("\\","\")

因为这会导致语法错误,被识别为转义序列"。我已经尝试了所有可能的方法。我还尝试使用loadstring()(以及嵌套的loadstring()调用),但也失败了。
请不要建议执行以下操作:
str:gsub("\\t","\t")

当然,这样会起作用,但不是我需要的。我需要将双反斜杠替换为单反斜杠。

1个回答

5
我猜你被引号所困惑,因为string.gsub可以替换反斜杠字符:
C:...> lua
Lua 5.1.4  版权所有(C) 1994-2008 Lua.org, PUC-Rio
> a="test\\\\ttest"
> =a
test\\ttest
> =a:gsub([[\\]],[[\]])
test\ttest       1
>
在双引号和单引号字符串中,反斜杠用作字符转义符,但在使用[[...]]表示的长字符串中不会。在通常的字符串常量中,反斜杠消耗一个或多个后续字符,并将整个序列替换为内部字符串值中的单个字节。因此,"\\"是包含一个反斜杠的单字节字符串,"\"是语法错误,"\""是包含双引号的单字节字符串。
增加混乱的是,Lua模式(由string.gsub及其衍生函数理解)使用%字符进行引用和命名特殊模式。这是Lua模式与其他语言支持的正则表达式之间较为明显的差异之一。对于Lua模式而言,反斜杠只是一个普通的字符。
因此,在上述设置a的值时,我使用了额外的反斜杠,使得字符串值总共有两个。我也可以这样写:a=[[test\\ttest]]。调用gsub时,简单模式将双倍反斜杠替换为单个反斜杠。可以看到,它成功了,结果是字符串test\ttest(以及作为第二个返回值的匹配计数)。
简而言之,在问题中所要求的替换“只是起作用”的预期效果。
但是,读懂了你的意思,似乎并不完全是这样。你似乎试图将字符串test\\ttest转换为test<TAB>test。如果这是你想要的单一转换,那么就像这样写:a:gsub([[\\t]],"\t")。(请注意,我使用引号,以便字符串文字将解释\t为替换值中的ASCII字符。)
更通用的情况则更加困难,因为你不仅必须处理制表符、响铃符、退格符、回车符、换行符等常规单字母转义,还必须处理一到三位十进制码序列。
更新:编写一个处理所有反斜杠转义的函数,就像Lua编译器处理字符串字面量一样,太强烈的诱惑了。
function unbackslashed(s)
    local ch = {
        ["\\a"] = '\\007', --'\a' alarm             Ctrl+G BEL
        ["\\b"] = '\\008', --'\b' backspace         Ctrl+H BS
        ["\\f"] = '\\012', --'\f' formfeed          Ctrl+L FF
        ["\\n"] = '\\010', --'\n' newline           Ctrl+J LF
        ["\\r"] = '\\013', --'\r' carriage return   Ctrl+M CR
        ["\\t"] = '\\009', --'\t' horizontal tab    Ctrl+I HT
        ["\\v"] = '\\011', --'\v' vertical tab      Ctrl+K VT
        ["\\\n"] = '\\010',--     newline
        ["\\\\"] = '\\092',--     backslash
        ["\\'"] = '\\039', --     apostrophe
        ['\\"'] = '\\034', --     quote
    }
    return s:gsub("(\\.)", ch)
        :gsub("\\(%d%d?%d?)", function(n)
            return string.char(tonumber(n))
        end)
end

这样的函数在解析用户提供的文本并希望处理用户提供的文本中的反斜杠转义时非常有用。字符串文字应该已经由编译器处理了。
另一个需要注意的地方是,如果你发现自己有部分翻译不完整的字符串,那么实际上可能会遭受设计缺乏清晰性的困扰。实际上需要这样的函数而不是解析用户输入之外的一种指示,这表明你的设计可能存在更深层次的问题。
函数"unbackslashed"的工作原理是首先用相应的数字形式替换所有识别出的以反斜杠后跟单个字符的序列形式出现的序列。第二轮将所有数字形式转换为它们的文字字符形式。之所以需要两轮,是因为string.gsub所理解的字符串模式不支持全面的正则表达式解析器所支持的备选符号。否则,匹配模式可以写成类似于Perl的"/\\([0-9]{1-3})|\\(.)/",并在一次转换中执行替换。

在第一部分中,您说如何将“\\”替换为“\”。这不是我要求的,正如您在第二部分中提到的那样,实际上我不想输出\t,而是一个水平制表符。您的“unbackslashed”函数也不是我要求的。查看原帖,我明确表示我不想用“\t”替换“\t”,而是用“\”替换“\”。 然而,在该函数中使用ASCII代码和string.char()并没有必要,仅使用ASCII字符就像s:gsub("\\t","\t")一样可以工作。 这不是我的问题的答案。 - noize
1
如果您想输出实际的水平制表符,则字符串必须包含该字符,而不是两个字符“\t”。当它出现在使用单引号或双引号形成的字符串字面量中时,这个两个字符序列在编译时被理解。unbackslashed函数模拟了Lua编译器对字符串字面量的处理方式,并可用于将包含\t的字符串转换为包含实际水平制表符的字符串。 - RBerteig

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接