windows - platformspecifik Unicode semantik i Python 2.7

Indlæg af Hanne Mølgaard Plasc

Problem



Ubuntu 11.10:


$ python
Python 2.7.2+ (default, Oct  4 2011, 20:03:08)
[GCC 4.6.1] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> x = u'U0001f44d'
>>> len(x)
1
>>> ord(x[0])
128077


Windows 7:


Python 2.7.2 (default, Jun 12 2011, 15:08:59) [MSC v.1500 32 bit (Intel)] on win32
Type "help", "copyright", "credits" or "license" for more information.
>>> x = u'U0001f44d'
>>> len(x)
2
>>> ord(x[0])
55357


Min Ubuntu-oplevelse er med standardfortolkeren i distributionen. For Windows 7 downloadede og installerede jeg den anbefalede version linket fra python.org. Jeg sammensatte heller ikke dem selv.


Forskellen er tydelig for mig. (På Ubuntu er strengen en sekvens af kodepunkter, i Windows 7 en sekvens af UTF-16-kodeenheder.) Mine spørgsmål er:



  • Hvorfor ser jeg denne forskel i adfærd? Er det på grund af, hvordan tolken er bygget, eller en forskel i afhængige systembiblioteker?

  • Er der nogen måde at konfigurere opførelsen af ​​Windows 7-tolken for at være enig med Ubuntu-en, som jeg kan gøre indenfor Eclipse PyDev (mit mål)?

  • Hvis jeg skal ombygge, er der nogen forudbyggede Windows 7-tolke, der opfører sig som Ubuntu ovenfor fra en pålidelig kilde?

  • Er der nogen løsninger på dette problem udover manuelt at tælle surrogater i unicode strings på kun Windows (blech)?

  • Er det berettiget til en fejlrapport? Er der nogen chance for, at en sådan fejlrapport bliver behandlet i 2.7?


Bedste reference


På Ubuntu har du en 'bred' Python-konstruktion, hvor strenge er UTF-32/UCS-4. Desværre er dette ikke (endnu) tilgængeligt for Windows. [13]



  Windows bygger vil være snævre i et stykke tid baseret på det faktum at der
  Der har været få anmodninger om brede tegn, disse anmodninger er for det meste
  fra hard-core programmører med evnen til at købe deres egen Python
  og Windows selv er stærkt forudindtaget i retning af 16-bit tegn.



Python 3.3 har en fleksibel strengrepræsentation, hvor du ikke behøver at bekymre sig om, om Unicode-strenge bruger 16-bit eller 32-bit kodeenheder. [14]


Indtil da kan du få kodepunkterne fra en UTF-16-streng med


def code\_points(text):
    utf32 = text.encode('UTF-32LE')
    return struct.unpack('<{}I'.format(len(utf32) // 4), utf32)

Andre referencer 1


stort spørgsmål! jeg faldt for nylig i dette kaninhul.


@ dan04s svar inspirerede mig til at udvide det til en underklasse, der unicode giver konsistent indeksering, skæring og len() på både smalle og brede Python 2-bygger:


class WideUnicode(unicode):
  """String class with consistent indexing, slicing, len() on both narrow and wide Python."""
  def \_\_init\_\_(self, *args, **kwargs):
    super(WideUnicode, self).\_\_init\_\_(*args, **kwargs)
    # use UTF-32LE to avoid a byte order marker at the beginning of the string
    self.\_\_utf32le = unicode(self).encode('utf-32le')

  def \_\_len\_\_(self):
    return len(self.\_\_utf32le) / 4

  def \_\_getitem\_\_(self, key):
    length = len(self)

    if isinstance(key, int):
      if key >= length:
        raise IndexError()
      key = slice(key, key + 1)

    if key.stop is None:
      key.stop = length

    assert key.step is None

    return WideUnicode(self.\_\_utf32le[key.start * 4:key.stop * 4]
                       .decode('utf-32le'))

  def \_\_getslice\_\_(self, i, j):
    return self.\_\_getitem\_\_(slice(i, j))


åben hentet her, offentligt område. eksempel brug: [15]


text = WideUnicode(obj.text)
for tag in obj.tags:
  text = WideUnicode(text[:start] + tag.text + text[end:])


(forenklet fra denne brug.) [16]


tak @ dan04!

Andre referencer 2


Jeg havde primært brug for nøjagtigt at teste længden. Derfor er denne funktion, der korrekt returnerer kodepunktlængden for en unicode streng, uanset om tolken er smal eller bred bygget. Hvis dataene bruger to surrogatbogstaver i stedet for et enkelt U -stilet kodepunkt i en vidbygget tolk, vil den returnerede kodepointlængde tage højde for det, så længe surrogaterne anvendes 'korrekt', dvs. som en smal -bygget tolk ville bruge dem.


invoke = lambda f: f()  # trick borrowed from Node.js

@invoke
def ulen():
  testlength = len(u'U00010000')
  assert (testlength == 1) or (testlength == 2)
  if testlength == 1:  # "wide" interpreters
    def closure(data):
      u'returns the number of Unicode code points in a unicode string'
      return len(data.encode('UTF-16BE').decode('UTF-16BE'))
  else:  # "narrow" interpreters
    def filt(c):
      ordc = ord(c)
      return (ordc >= 55296) and (ordc < 56320)
    def closure(data):
      u'returns the number of Unicode code points in a unicode string'
      return len(data) - len(filter(filt, data))
  return closure  # ulen() body is therefore different on narrow vs wide builds


Testcase, passerer på smalle og brede konstruktioner:


class TestUlen(TestCase):

  def test\_ulen(self):
    self.assertEquals(ulen(u'ud83dudc4d'), 1)
    self.assertEquals(ulen(u'U0001F44D'), 1)