如何在Python中正确地添加字符串引号?

4 投票
4 回答
18036 浏览
提问于 2025-04-16 03:23

我想给一个Python字符串加上一对(双)引号,如果它们缺失的话,但这个字符串里也可能包含引号。

这样做的目的是为了把所有没有被引号包围的命令都加上引号,因为Windows的API在用_popen()执行进程时,需要你把整个命令行用引号包起来。

以下是一些应该加引号的字符串:

<empty string>
type
"type" /?
"type" "/?"
type "a a" b
type "" b

以下是一些不应该加引号的字符串:

"type"
""type" /?"

请务必测试所有示例;判断字符串是否需要引号并不容易。

4 个回答

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" /?"'''
8

你的问题不一致。

考虑这两种情况:

""a" b"

"a" "b"

第一种情况被理解为一个带有“嵌套引号”的预先引用字符串,而第二种情况则被理解为两个单独的引用字符串。下面是一些例子,能更好地说明这个问题。

" "a" "b" "

" "a" b"

"a ""b"

这些应该如何处理呢?

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

撰写回答