4

I want to add a set of (double) quotes to a python string if they are missing but the string can also contain quotes.

The purpose of this is to quote all command that are not already quoted because Windows API requires you to quote the entire command line when you execute a process using _popen().

Here are some strings that should be quoted:

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

Here are some that should not be quoted:

"type"
""type" /?"

Please take the time to test all examples; it is not too easy to detect if the string needs the quotes or not.

4
  • What's the expected output for ''? Commented Aug 27, 2010 at 12:50
  • 1
    #6 can also be read as quote-a-quotedemptystring-b-quote Commented Aug 27, 2010 at 13:26
  • This guy changed the question around quite a bit -- mainly to tell what his real goal is. Good for clarifying the goal, but references to test numbers sort of don't work anymore. Commented Aug 28, 2010 at 6:03
  • @bogdan Isn't there a library function for these? Also note that in Python there's subprocess.call that takes its arguments as a list, os.popen is obsolete. Commented Nov 17, 2012 at 17:04

4 Answers 4

8

Your problem is inconsistent.

Consider the two cases

""a" b"

"a" "b"

The former is interpreted as a pre-quoted string with 'nested quotes', but the latter is interpreted as separately-quoted strings. Here are some examples that highlight the issue.

" "a" "b" "

" "a" b"

"a ""b"

How should they be treated?

Sign up to request clarification or add additional context in comments.

6 Comments

All the time, I felt like something was wrong with this question... now I know. +1 for bringing it up.
I was just typing the same thing. Instead I'll upvote your answer. Another input example: " a " foo " b ". Minor clarification: not sure that the question is inconsistent so much as under-specified.
@bogdan: removing the spaces, respectively considering only the quotes, the two are equivalent.
@FM: Well, it's inconsistent under the assumption that all non-quote characters are treated equally. Adding more specification changes the problem.
@bogdan: unfortunately that doesn't help: the commands "a "foo" b" and "a" foo "b" seem indistinguishable but require different rules. Maybe try one and catch the resulting error?
|
4

I think this is a difficult question to specify in a precise way, but perhaps this strategy will approximate your goal.

The basic idea is to create a copy of the original string, removing the internally quoted items. An internally quoted item is defined here so that it must contains at least one non-whitespace character.

After the internally quoted items have been removed, you then check whether the entire string needs surrounding quotes or not.

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

Comments

3

I wrote a simple state machine to track if we are in a word or not. If the quote depth is ever zero in the string, then we need quotes:

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

1 Comment

I like your approach because it does not require re, but it does fail on the last test made by FM - his re solution does pass all tests.
-1

You have three cases:

  1. String is less than two characters long: add quotes
  2. String has quotes at s[0] and at s[1]: don't add quotes
  3. Add quotes

And by "add quotes" I mean simply construct '"'+string+'"' and return it.

Translate to if-statements, and you're done.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.