如何使用Python正确地给字符串添加引号?

4
我希望能够在Python字符串中添加一组(双引号)引号,如果它们缺失,但是该字符串也可以包含引号。
这样做的目的是引用所有未被引用的命令,因为使用_popen()执行进程时,Windows API要求引用整个命令行。
以下是一些应该被引用的字符串:
<empty string>
type
"type" /?
"type" "/?"
type "a a" b
type "" b

以下是一些不应该被引用的内容:

"type"
""type" /?"

请花些时间来测试所有的示例;很难判断字符串是否需要引号,因此请仔细检查。

''的预期输出是什么? - user395760
1
#6 也可以读作引号-空字符串-引号。 - John La Rooy
这个人改变了问题的很多方面 - 主要是为了表达他的真实目标。这对于澄清目标非常有帮助,但是测试数字的引用似乎不再起作用了。 - Ian
@bogdan 这些不是有库函数吗?此外请注意,在Python中有一个subprocess.call函数,它将其参数作为列表传递,而os.popen已经过时了。 - Kos
4个回答

8

您的问题是不一致的。

考虑两种情况

""a" b"

"a" "b"

前者被解释为具有“嵌套引号”的预引用字符串,但后者被解释为分别引用的字符串。以下是一些突出显示该问题的示例。

" "a" "b" "

" "a" b"

"a ""b"

应如何处理?


一直以来,我都感觉这个问题有些不对劲...现在我知道了。感谢你提出来。 - user395760
我刚才也在打同样的东西。不过我会给你的答案点赞。另一个输入示例:" a " foo " b "。小小澄清一下:我不确定这个问题是否“不一致”,更多的是“未明确说明”。 - FMc
1
@bogdan:去掉空格,只考虑引号,这两者是等价的。 - user395760
@FM:在假设所有非引号字符都被平等对待的情况下,这是不一致的。增加更多的规范会改变问题。 - Katriel
1
@bogdan:不幸的是,这并没有帮助:命令“a“foo”b”和“a”foo“b”看起来无法区分,但需要不同的规则。也许可以尝试其中一个并捕获结果错误? - Katriel
我犯了一个错误,试图过于简化问题。现在我用新的例子更新了问题。如果您想正确检测字符串是否已经被引用,看起来不能忽略空格。 - bogdan

4
我认为这是一个难以精确说明的问题,但也许这个策略可以近似达到您的目标。
基本思路是创建原始字符串的副本,并删除其中的内部引用项。在这里,内部引用项的定义是它必须包含至少一个非空格字符。
在删除了内部引用项之后,您需要检查整个字符串是否需要加上引号。
import re

tests = [
    # Test data in original question.
    ( '',                '""'                ),
    ( 'a',               '"a"'               ),
    ( '"a"',             '"a"'               ), # No change.
    ( '""a" b"',         '""a" b"'           ), # No change.
    ( '"a" b',           '""a" b"'           ),
    ( '"a" "b"',         '""a" "b""'         ),
    ( 'a "b" c',         '"a "b" c"'         ),

    # Test data in latest edits.
    ( 'type',            '"type"'         ),    # Quote these.
    ( '"type" /?',       '""type" /?"'    ),
    ( '"type" "/?"',     '""type" "/?""'  ),
    ( 'type "a a" b',    '"type "a a" b"' ),
    ( 'type "" b',       '"type "" b"'    ),
    ( '"type"',          '"type"'         ),    # Don't quote.
    ( '""type" /?"',     '""type" /?"'    ),

    # Some more tests.
    ( '"a b" "c d"',     '""a b" "c d""'     ),
    ( '" a " foo " b "', '"" a " foo " b ""' ),
]

Q = '"'
re_quoted_items = re.compile(r'" \s* [^"\s] [^"]* \"', re.VERBOSE)

for orig, expected in tests:
    # The orig string w/o the internally quoted items.
    woqi = re_quoted_items.sub('', orig)

    if len(orig) == 0:
        orig_quoted = Q + orig + Q
    elif len(woqi) > 0 and not (woqi[0] == Q and woqi[-1] == Q):
        orig_quoted = Q + orig + Q    
    else:
        orig_quoted = orig

    print orig_quoted == expected

3

我编写了一个简单的状态机来跟踪我们是否处于单词中。如果字符串中引号深度为零,则需要引号:

def quotify(s):
    if s == "":
        return '""'

    depth = 0
    in_word = False
    needs_quotes = False
    for c in s:
        if c == '"':
            if in_word:
                depth -= 1
            else:
                depth += 1
        else:
            if depth == 0:
                needs_quotes = True
                break
            in_word = not c.isspace()

    if needs_quotes:
        return '"' + s + '"'
    else:
        return s

assert quotify('') == '""'
assert quotify('''type''') == '''"type"'''
assert quotify('''"type" /?''') == '''""type" /?"'''
assert quotify('''"type" "/?"''') == '''""type" "/?""'''
assert quotify('''type "a a" b''') == '''"type "a a" b"'''
assert quotify('''type "" b''') == '''"type "" b"'''
assert quotify('''"type"''') == '''"type"'''
assert quotify('''""type" /?"''') == '''""type" /?"'''

我喜欢你的方法,因为它不需要 re,但是它在FM进行的最后一个测试中失败了 - 他的 re 解决方案通过了所有测试。 - bogdan

-1

你有三种情况:

  1. 字符串长度小于两个字符:添加引号
  2. 字符串在s[0]和s[1]处有引号:不添加引号
  3. 添加引号

而通过“添加引号”,我指的是简单地构造'"'+string+'"'并返回它。

将其转换为if语句,你就完成了。


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