2009-09-25 27 views
18

Chciałbym przetestować obsługę kodu Unicode za pomocą kodu Unicode. Czy jest coś, co mogę umieścić w random.choice(), aby wybrać cały zakres Unicode, najlepiej nie zewnętrzny moduł? Ani Google, ani StackOverflow nie mają odpowiedzi.Generowanie losowego ciągu znaków UTF-8 w języku Python

Edycja: Wygląda na to, że jest to bardziej złożone niż się spodziewano, więc przeformułowuję pytanie - czy poniższy kod jest wystarczający do wygenerowania wszystkich ważnych non-control characters in Unicode?

unicode_glyphs = ''.join(
    unichr(char) 
    for char in xrange(1114112) # 0x10ffff + 1 
    if unicodedata.category(unichr(char))[0] in ('LMNPSZ') 
    ) 
+0

Może ci pomóc, jeśli chcesz podać więcej szczegółów na temat "testowania kodu Unicode w moim kodzie" i wyjaśnij, co jest częścią, która generuje losowe ciągi UTF-8 w tym teście i co traktujesz to jako "cały zakres Unicode" (16 bitów? 21 bitów? nie są to surrogate punkty kodowe? prawidłowe znaki (np. nie U + FFFF)?). Czy ufasz kodekowi Python UTF-8, czy też musisz to przetestować? Python 2.X lub 3.X lub oba? –

+1

Celem jest zaakceptowanie wszelkich możliwych do wydrukowania, poprawnych znaków kodowych Unicode (znaków) jako danych wejściowych dla interfejsu WWW w Pythonie 2.6. – l0b0

Odpowiedz

7
+0

Byłoby użyteczne, aby upewnić się, że program nie zostanie złamany, gdy zostanie podany niepoprawny tekst, ale nie pomoże to jako test zgodności. – voyager

+0

+1. l0b0: nie martw się o generowanie losowego unicode. Pożyczanie cudzego koła> wymyślanie go na nowo. –

+3

Dobra odpowiedź, ale w rzeczywistości nie odpowiada na pytanie zadane. – Kylotan

0

Od Unicode jest tylko zakres - oraz - kody, co na temat korzystania unichr(), aby uzyskać ciąg Unicode odpowiadającą losową liczbę między 0 a 0xFFFF?
(Oczywiście, że dałoby to tylko jeden punkt kodowy, a więc iterować zgodnie z wymaganiami)

+3

Niestety, nie jest to takie proste. Unicode zawiera znacznie więcej niż 0x100000 znaków, a zasięg nie jest połączony. Na przykład wartości zastępcze nie mogą nigdy pojawiać się jako pojedyncze punkty kodowe. Tak więc pytanie, co stanowi prawidłowy ciąg znaków UTF-8, jest wysoce nietrywialne. Szczegóły opisano w definicji D92 rozdziału 3 standardu Unicode. Istnieje również tabela (3-7)), która zawiera listę wszystkich poprawnych możliwości dla sekwencji bajtowych UTF-8. – Philipp

+0

Rozumiem, dziękuję :) – Joril

+0

Unicode działa od U + 0000 do U + 10FFFF; istnieją również liczne punkty kodowe, które nie są prawidłowe, w tym (jak to się dzieje) U + FFFF. Standard Unicode mówi o tym, że " - wartość FFFF nie jest wcale znakiem Unicode". –

0

Można pobrać stronę internetową napisaną w języku greckim lub niemieckim, która używa Unicode i podaje ją do kodu.

3

To zależy od tego, jak dokładnie chcesz wykonać test i jak dokładnie chcesz wykonać generację. W całości, Unicode to 21-bitowy zestaw kodowy (U + 0000 .. U + 10FFFF). Jednak niektóre spore fragmenty tego zakresu są zarezerwowane dla niestandardowych znaków. Czy chcesz się martwić o generowanie kombinacji znaków na początku łańcucha (ponieważ powinny one pojawiać się tylko po innej postaci)?

Podstawowym podejściem, jakie przyjmuję, jest losowe generowanie kodu Unicode (np. U + 2397 lub U + 31232), sprawdzanie poprawności w kontekście (czy jest to uzasadniony znak, może pojawić się tutaj w łańcuchu) i kodowanie prawidłowe punkty kodowe w UTF-8.

Jeśli chcesz tylko sprawdzić, czy twój kod poprawnie obsługuje nieprawidłowe sformatowanie UTF-8, możesz użyć znacznie prostszych schematów generacji.

Pamiętaj, że musisz wiedzieć, czego się spodziewać po wprowadzeniu - w przeciwnym razie nie testujesz; eksperymentujesz.

7

Oto przykład funkcja prawdopodobnie tworzy losową dobrze uformowane UTF-8 sekwencji, jak zdefiniowano w Tabeli 3-7 Unicode 5.0.0:

#!/usr/bin/env python3.1 

# From Table 3–7 of the Unicode Standard 5.0.0 

import random 

def byte_range(first, last): 
    return list(range(first, last+1)) 

first_values = byte_range(0x00, 0x7F) + byte_range(0xC2, 0xF4) 
trailing_values = byte_range(0x80, 0xBF) 

def random_utf8_seq(): 
    first = random.choice(first_values) 
    if first <= 0x7F: 
     return bytes([first]) 
    elif first <= 0xDF: 
     return bytes([first, random.choice(trailing_values)]) 
    elif first == 0xE0: 
     return bytes([first, random.choice(byte_range(0xA0, 0xBF)), random.choice(trailing_values)]) 
    elif first == 0xED: 
     return bytes([first, random.choice(byte_range(0x80, 0x9F)), random.choice(trailing_values)]) 
    elif first <= 0xEF: 
     return bytes([first, random.choice(trailing_values), random.choice(trailing_values)]) 
    elif first == 0xF0: 
     return bytes([first, random.choice(byte_range(0x90, 0xBF)), random.choice(trailing_values), random.choice(trailing_values)]) 
    elif first <= 0xF3: 
     return bytes([first, random.choice(trailing_values), random.choice(trailing_values), random.choice(trailing_values)]) 
    elif first == 0xF4: 
     return bytes([first, random.choice(byte_range(0x80, 0x8F)), random.choice(trailing_values), random.choice(trailing_values)]) 

print("".join(str(random_utf8_seq(), "utf8") for i in range(10))) 

względu na szeroki zakres normy Unicode Nie mogę tego dokładnie przetestować. Zauważ również, że znaki nie są równomiernie rozmieszczone (ale każdy bajt w sekwencji jest).

0

Odpowiadając zrewidowane pytanie:

Tak, na ścisłej definicji „znaków sterujących” - zauważ, że nie będzie to CR, LF i TAB; czy tego chcesz?

Zastanów się, czy odpowiedzieć na moje wcześniejsze zaproszenie, aby powiedzieć nam, co tak naprawdę chcesz zrobić.

10

Ludzie mogą tu znaleźć drogę głównie na podstawie tytułu pytania, więc tutaj jest sposób na wygenerowanie losowego łańcucha zawierającego różne znaki Unicode. Aby uwzględnić więcej (lub mniej) możliwych znaków, wystarczy rozszerzyć tę część przykładu o pożądane zakresy punktów kodowych.

import random 

def get_random_unicode(length): 

    try: 
     get_char = unichr 
    except NameError: 
     get_char = chr 

    # Update this to include code point ranges to be sampled 
    include_ranges = [ 
     (0x0021, 0x0021), 
     (0x0023, 0x0026), 
     (0x0028, 0x007E), 
     (0x00A1, 0x00AC), 
     (0x00AE, 0x00FF), 
     (0x0100, 0x017F), 
     (0x0180, 0x024F), 
     (0x2C60, 0x2C7F), 
     (0x16A0, 0x16F0), 
     (0x0370, 0x0377), 
     (0x037A, 0x037E), 
     (0x0384, 0x038A), 
     (0x038C, 0x038C), 
    ] 

    alphabet = [ 
     get_char(code_point) for current_range in include_ranges 
      for code_point in range(current_range[0], current_range[1] + 1) 
    ] 
    return ''.join(random.choice(alphabet) for i in range(length)) 

if __name__ == '__main__': 
    print('A random string: ' + get_random_unicode(10)) 
+0

Dziękuję, Jacob. Czy wystąpią problemy z uruchomieniem tego kodu w Pythonie 2.7? – morfys

+1

@morfys Nie, ale ja to właśnie edytowałem. Dzięki, że pytasz. –

+0

Wielkie dzięki, Jacob! – morfys

2

Follows kod, który wydrukować dowolny wydruku charakter UTF-8:

print(''.join(tuple(chr(l) for l in range(1, 0x10ffff) 
        if chr(l).isprintable()))) 

Wszystkie znaki są obecne, nawet te, które nie są obsługiwane przez używane czcionki. and not chr(l).isspace() można dodać w celu odfiltrowania wszystkich znaków spacji. (w tym zakładka)

+0

To nie da ci losowego ciągu znaków, chociaż możesz zamiast tego użyć po prostu ['random.sample'] (https://docs.python.org/2/library/random.html#random.sample) z 'print'. – l0b0

+0

'random.sample' nie zastępuje elementów, które już zostały narysowane. –

+0

Zamiast tego użyj "random.choices". – gimboland

Powiązane problemy