2009-11-04 13 views
11

Używam małego skryptu Python do generowania binarnych danych, które będą używane w nagłówku C.Pobierz rep z podwójnym cudzysłowem Python

Dane te powinny być zadeklarowane jako char[] i byłoby miło, gdyby mogło być zakodowane jako ciąg (z odpowiednimi sekwencjami specjalnymi, gdy nie znajdują się w zakresie znaków ASCII), aby nagłówek był bardziej zwarty niż z dziesiętnym lub szesnastkowym kodowaniem macierzy.

Problem polega na tym, że podczas drukowania repr ciągu w języku Python jest on rozdzielany pojedynczymi cudzysłowami, a C nie podoba się to. Naiwny rozwiązaniem jest zrobić:

'"%s"'%repr(data)[1:-1] 

ale to nie działa, gdy jeden z bajtów danych dzieje się cudzysłów, tak że muszę im się uciec zbyt.

Myślę, że prosty replace('"', '\\"') może wykonać zadanie, ale może istnieje lepsze, bardziej pythonic rozwiązanie tam.

Extra Punkt:

byłoby zbyt wygodne podzielić dane na linii około 80 znaków, ale znowu proste podejście splitting the source string in chunks wielkości 80 nie będzie działać, ponieważ każdy znak zakaz druku trwa 2 lub 3 znaki w sekwencji specjalnej. Dzielenie listy w kawałkach 80 po otrzymaniu repr również nie pomoże, ponieważ może podzielić sekwencję ucieczki.

Wszelkie sugestie?

Odpowiedz

4

repr() nie jest tym, czego potrzebujesz.Istnieje podstawowy problem: repr() może używać dowolnej reprezentacji ciągu znaków, który może być oceniony jako Python w celu utworzenia łańcucha. Oznacza to teoretycznie, że może zdecydować o użyciu dowolnej liczby innych konstruktów, które nie byłyby prawidłowe w C, takich jak "" "długie łańcuchy" "".

Ten kod jest prawdopodobnie właściwym kierunkiem. Użyłem domyślnego zawijania w 140, co jest sensowną wartością na rok 2009, ale jeśli naprawdę chcesz zawinąć kod do 80 kolumn, po prostu go zmień.

Jeśli unicode = True, wyprowadza łańcuch L "szeroki", który może przechowywać znaczniki Unicode w znaczący sposób. Alternatywnie, można konwertować znaki Unicode UTF-8 i wyjście im uciekł, w zależności od programu, którego używasz ich.

def string_to_c(s, max_length = 140, unicode=False): 
    ret = [] 

    # Try to split on whitespace, not in the middle of a word. 
    split_at_space_pos = max_length - 10 
    if split_at_space_pos < 10: 
     split_at_space_pos = None 

    position = 0 
    if unicode: 
     position += 1 
     ret.append('L') 

    ret.append('"') 
    position += 1 
    for c in s: 
     newline = False 
     if c == "\n": 
      to_add = "\\\n" 
      newline = True 
     elif ord(c) < 32 or 0x80 <= ord(c) <= 0xff: 
      to_add = "\\x%02x" % ord(c) 
     elif ord(c) > 0xff: 
      if not unicode: 
       raise ValueError, "string contains unicode character but unicode=False" 
      to_add = "\\u%04x" % ord(c) 
     elif "\\\"".find(c) != -1: 
      to_add = "\\%c" % c 
     else: 
      to_add = c 

     ret.append(to_add) 
     position += len(to_add) 
     if newline: 
      position = 0 

     if split_at_space_pos is not None and position >= split_at_space_pos and " \t".find(c) != -1: 
      ret.append("\\\n") 
      position = 0 
     elif position >= max_length: 
      ret.append("\\\n") 
      position = 0 

    ret.append('"') 

    return "".join(ret) 

print string_to_c("testing testing testing testing testing testing testing testing testing testing testing testing testing testing testing testing testing", max_length = 20) 
print string_to_c("Escapes: \"quote\" \\backslash\\ \x00 \x1f testing \x80 \xff") 
print string_to_c(u"Unicode: \u1234", unicode=True) 
print string_to_c("""New 
lines""") 
+0

nie jest "elif" \\\ "". Find (c)! = -1 "taki sam jak" elif c in "\\\" ""? W każdym razie zgadzam się, repr() nie jest rozwiązaniem tutaj i musisz zrobić coś takiego. –

+0

80 zaleceń dla tekstu kolumny nie było oparte na szerokości monitora. Pochodzi z typografii. Spójrz na niektóre gazety: spójrz na tabloidowy wygląd w arkuszu. Jak szerokie są kolumny? –

6

Lepiej nie zhakuj kodu repr(), ale użyj właściwego kodowania od początku. Można uzyskać kodowanie Repr bezpośrednio z kodowaniem string_escape

>>> "naïveté".encode("string_escape") 
'na\\xc3\\xafvet\\xc3\\xa9' 
>>> print _ 
na\xc3\xafvet\xc3\xa9 

Do ucieczki „-quotes myślę za pomocą prostego zastąpienia po ewakuacji kodujący łańcuch jest całkowicie jednoznaczny procesu:

>>> '"%s"' % 'data:\x00\x01 "like this"'.encode("string_escape").replace('"', r'\"') 
'"data:\\x00\\x01 \\"like this\\""' 
>>> print _ 
"data:\x00\x01 \"like this\"" 
+0

że nie rozwiąże mój problem, to nadal pokazuje cudzysłowy Niecytowany ' 'quotehere' '. encode ("string_escape") 'daje'' quotehere '' ' – fortran

+0

W Pythonie 3 jest to [' unicode_escape'] (https://docs.python.org/3/library/codecs.html?highlight = unicode_escape # text-encodings). Zobacz także [odpowiedź na inne pytanie] (https://stackoverflow.com/a/6868026). –

6

Jeśli pytasz pytona str dla jego repr, nie sądzę, rodzaj cytat jest bardzo konfigurowalny z funkcji w drzewie źródłowym PyString_Repr python 2.6.4.

/* figure out which quote to use; single is preferred */ 
    quote = '\''; 
    if (smartquotes && 
     memchr(op->ob_sval, '\'', Py_SIZE(op)) && 
     !memchr(op->ob_sval, '"', Py_SIZE(op))) 
     quote = '"'; 

Sądzę więc, że używaj podwójnych cudzysłowów, jeśli w łańcuchu występuje pojedynczy cudzysłów, ale nawet wtedy, gdy w łańcuchu występuje podwójny cudzysłów.

Chciałbym wypróbować coś takiego, jak napisanie własnej klasy, aby zawrzeć ciąg danych, zamiast używać wbudowanego ciągu, aby to zrobić. Jedną z możliwości byłoby wyprowadzenie klasę z str i pisanie własnego repr:

class MyString(str): 
    __slots__ = [] 
    def __repr__(self): 
     return '"%s"' % self.replace('"', r'\"') 

print repr(MyString(r'foo"bar')) 

Albo, nie używaj repr w ogóle:

def ready_string(string): 
    return '"%s"' % string.replace('"', r'\"') 

print ready_string(r'foo"bar') 

To uproszczone cytowanie nie może zrobić „prawo” rzeczy jeśli w ciągu znaków znajduje się już cytowana wycena.

+0

+1 Rozszerzenie str to genialny pomysł! Dziękuję Ci! –

Powiązane problemy