Odczyt znaków z pliku w Pythonie

102

W pliku tekstowym znajduje się ciąg „Nie podoba mi się to”.

Jednak kiedy wczytuję go do ciągu, staje się „Nie tak \ xe2 \ x80 \ x98t”. Rozumiem, że \ u2018 to symbol „” ”w formacie Unicode. używam

f1 = open (file1, "r")
text = f1.read()

polecenie wykonania odczytu.

Czy jest możliwe odczytanie ciągu w taki sposób, że kiedy jest wczytywany do łańcucha, brzmi to „Nie podoba mi się to” zamiast „Nie tak \ xe2 \ x80 \ x98t w ten sposób”?

Druga edycja: widziałem, jak niektórzy ludzie używają mapowania do rozwiązania tego problemu, ale czy naprawdę nie ma wbudowanej konwersji, która dokonuje tego rodzaju konwersji ANSI na Unicode (i odwrotnie)?

Graviton
źródło
Kilka komentarzy: Widziałem, jak niektórzy ludzie używają mapowania do rozwiązania tego problemu, ale czy naprawdę nie ma wbudowanej konwersji, która dokonuje tego rodzaju konwersji ANSI na Unicode (i odwrotnie)? Dzięki!
Graviton,
Nie ma, ponieważ istnieją setki tysięcy punktów kodowych Unicode. Jak zdecydowałbyś, które powinny być mapowane na jakie znaki ASCII?
John Millikin,
2
btw, twój plik tekstowy jest uszkodzony! U + 2018 to „LEWY POJEDYNCZY ZNAK CYTATU”, a nie apostrof (najczęściej U + 0027).
John, twój komentarz jest błędny, przynajmniej w ogólnym sensie. biblioteka iconv może służyć do transliteracji znaków Unicode do ascii (nawet zależnych od ustawień regionalnych. $ python -c 'print u "\ u2018" .encode ("utf-8")' | iconv -t 'ascii // translit' | xxd 0000000: 270a
chodzi o to, że musisz przekonwertować UNICODE na ASCII (a nie odwrotnie).
hasen

Odpowiedzi:

157

Ref: http://docs.python.org/howto/unicode

Odczytywanie Unicode z pliku jest zatem proste:

import codecs
with codecs.open('unicode.rst', encoding='utf-8') as f:
    for line in f:
        print repr(line)

Możliwe jest również otwieranie plików w trybie aktualizacji, umożliwiając zarówno odczyt, jak i zapis:

with codecs.open('test', encoding='utf-8', mode='w+') as f:
    f.write(u'\u4500 blah blah blah\n')
    f.seek(0)
    print repr(f.readline()[:1])

EDYCJA : Zakładam, że twoim zamierzonym celem jest po prostu możliwość prawidłowego odczytania pliku do ciągu w Pythonie. Jeśli próbujesz przekonwertować na ciąg ASCII z Unicode, to naprawdę nie ma bezpośredniego sposobu, aby to zrobić, ponieważ znaki Unicode niekoniecznie muszą istnieć w ASCII.

Jeśli próbujesz przekonwertować na ciąg ASCII, wypróbuj jedną z następujących czynności:

  1. Zastąp określone znaki Unicode odpowiednikami ASCII, jeśli chcesz obsłużyć tylko kilka specjalnych przypadków, takich jak ten konkretny przykład

  2. Użyj unicodedatamodułu normalize()i string.encode()metody, aby przekonwertować najlepiej jak potrafisz na najbliższy odpowiednik ASCII (zob. Https://web.archive.org/web/20090228203858/http://techxplorer.com/2006/07/18/converting- unicode-to-ascii-using-python ):

    >>> teststr
    u'I don\xe2\x80\x98t like this'
    >>> unicodedata.normalize('NFKD', teststr).encode('ascii', 'ignore')
    'I donat like this'
Sójka
źródło
3
codecsmoduł nie obsługuje poprawnie trybu uniwersalnych znaków nowej linii. io.open()Zamiast tego użyj w Pythonie 2.7+ (jest wbudowany open()w Python 3).
jfs
15

Należy wziąć pod uwagę kilka kwestii.

Znak \ u2018 może pojawić się tylko jako fragment reprezentacji ciągu znaków Unicode w Pythonie, np. Jeśli napiszesz:

>>> text = u'‘'
>>> print repr(text)
u'\u2018'

Teraz, jeśli chcesz po prostu ładnie wydrukować ciąg znaków Unicode, po prostu użyj encodemetody Unicode :

>>> text = u'I don\u2018t like this'
>>> print text.encode('utf-8')
I dont like this

Aby upewnić się, że każda linia z dowolnego pliku zostanie odczytana jako Unicode, lepiej użyj codecs.openfunkcji zamiast po prostu open, która pozwala określić kodowanie pliku:

>>> import codecs
>>> f1 = codecs.open(file1, "r", "utf-8")
>>> text = f1.read()
>>> print type(text)
<type 'unicode'>
>>> print text.encode('utf-8')
I dont like this
DzinX
źródło
6

Ale tak naprawdę brzmi „Nie podoba mi się to”, a nie „Nie podoba mi się to”. Znak u '\ u2018' to zupełnie inny znak niż "'" (i wizualnie powinien bardziej odpowiadać' '').

Jeśli próbujesz przekonwertować zakodowany Unicode na zwykły ASCII, być może możesz zachować mapowanie znaków interpunkcyjnych Unicode, które chciałbyś przetłumaczyć na ASCII.

punctuation = {
  u'\u2018': "'",
  u'\u2019': "'",
}
for src, dest in punctuation.iteritems():
  text = text.replace(src, dest)

Jednak w Unicode jest bardzo dużo znaków interpunkcyjnych , ale przypuszczam, że możesz liczyć na to, że tylko kilka z nich jest faktycznie używanych przez dowolną aplikację, która tworzy czytane dokumenty.

Logan
źródło
1
właściwie, jeśli zmienisz dyktando na liczby porządkowe Unicode na liczby porządkowe w Unicode ({0x2018: 0x27, 0x2019: 0x27}), możesz po prostu przekazać cały dykt do text.translate (), aby wykonać wszystkie zamiany za jednym razem.
Thomas Wouters,
5

Możliwe jest również odczytanie zakodowanego pliku tekstowego za pomocą metody odczytu Pythona 3:

f = open (file.txt, 'r', encoding='utf-8')
text = f.read()
f.close()

W przypadku tej odmiany nie ma potrzeby importowania żadnych dodatkowych bibliotek

Stein
źródło
3

Pomijając fakt, że twój plik tekstowy jest uszkodzony (U + 2018 to lewy cudzysłów, a nie apostrof): iconv można używać do transliteracji znaków Unicode na ascii.

Będziesz musiał wyszukać w Google „iconvcodec”, ponieważ moduł wydaje się nie być już obsługiwany i nie mogę znaleźć dla niego kanonicznej strony głównej.

>>> import iconvcodec
>>> from locale import setlocale, LC_ALL
>>> setlocale(LC_ALL, '')
>>> u'\u2018'.encode('ascii//translit')
"'"

Alternatywnie możesz użyć iconvnarzędzia wiersza poleceń, aby wyczyścić plik:

$ xxd foo
0000000: e280 980a                                ....
$ iconv -t 'ascii//translit' foo | xxd
0000000: 270a                                     '.

źródło
2

Istnieje możliwość, że w jakiś sposób masz ciąg znaków inny niż Unicode ze znakami ucieczki Unicode, np .:

>>> print repr(text)
'I don\\u2018t like this'

To mi się kiedyś przytrafiło. Możesz użyć unicode_escapekodeka, aby zdekodować ciąg do Unicode, a następnie zakodować go w dowolnym formacie:

>>> uni = text.decode('unicode_escape')
>>> print type(uni)
<type 'unicode'>
>>> print uni.encode('utf-8')
I dont like this
DzinX
źródło
1

To jest sposób, w jaki Python pokazuje ciągi zakodowane w Unicode. Ale myślę, że powinieneś być w stanie wydrukować ciąg na ekranie lub zapisać go do nowego pliku bez żadnych problemów.

>>> test = u"I don\u2018t like this"
>>> test
u'I don\u2018t like this'
>>> print test
I dont like this
xardias
źródło
1

W rzeczywistości U + 2018 jest reprezentacją Unicode znaku specjalnego ''. Jeśli chcesz, możesz przekonwertować wystąpienia tego znaku na U + 0027 za pomocą tego kodu:

text = text.replace (u"\u2018", "'")

Ponadto, czego używasz do zapisywania pliku? f1.read()powinien zwrócić ciąg, który wygląda następująco:

'I don\xe2\x80\x98t like this'

Jeśli zwraca ten ciąg, plik jest zapisywany nieprawidłowo:

'I don\u2018t like this'
John Millikin
źródło
Przepraszam! Jak powiedziałeś, zwraca „Nie lubię tego \ xe2 \ x80 \ x98t”
Graviton,
„Nie podoba mi się to \ xe2 \ x80 \ x98t”, które widzisz, jest tym, co Python nazwałby str. Wydaje się, że jest to kodowanie utf-8 u „Nie podoba mi się to”, które jest instancją Unicode w Pythonie. Spróbuj wywołać .decode ('utf-8') na pierwszym lub .encode ('utf-8') na drugim.
Logan,
@hop: oops, zapomniałem ord () zwraca liczbę dziesiętną zamiast szesnastkową. Dziękuję za połów.
John Millikin