Bir metin dosyasına Unicode metin mi yazıyorsunuz?


225

Verileri bir Google dokümanından alıyorum, işliyorum ve bir dosyaya yazıyorum (sonunda bir Wordpress sayfasına yapıştıracağım).

Bazı ASCII olmayan sembolleri vardır. Bunları HTML kaynağında kullanılabilecek sembollere nasıl güvenli bir şekilde dönüştürebilirim?

Şu anda, her şeyi bir Python dizesinde bir araya getirerek, Unicode'a dönüştürüyorum, sonra yapıyorum:

import codecs
f = codecs.open('out.txt', mode="w", encoding="iso-8859-1")
f.write(all_html.encode("iso-8859-1", "replace"))

Son satırda bir kodlama hatası var:

UnicodeDecodeError: 'ascii' codec bileşeni 12286 konumundaki bayt 0xa0 kodunu çözemiyor: sıra değeri aralıkta değil (128)

Kısmi çözüm:

Bu Python hatasız çalışır:

row = [unicode(x.strip()) if x is not None else u'' for x in row]
all_html = row[0] + "<br/>" + row[1]
f = open('out.txt', 'w')
f.write(all_html.encode("utf-8"))

Ama sonra gerçek metin dosyasını açarsam, aşağıdaki gibi birçok sembol görürüm:

Qur’an 

Belki bir metin dosyasından başka bir şeye yazmam gerekiyor?


1
Açmak için kullandığınız program UTF-8 metnini doğru şekilde yorumlamıyor. Dosyayı UTF-8 olarak açma seçeneği olmalıdır.
Thomas K

Yanıtlar:


322

Bir şeyleri ilk aldığınız zaman unicode nesnelerine çözerek ve çıkışta gerektiğinde kodlayarak, mümkün olduğunca unicode nesnelerle başa çıkın.

Dizeniz aslında bir unicode nesnesiyse, bunu bir dosyaya yazmadan önce bir unicode kodlu dize nesnesine dönüştürmeniz gerekir:

foo = u'Δ, Й, ק, ‎ م, ๗, あ, 叶, 葉, and 말.'
f = open('test', 'w')
f.write(foo.encode('utf8'))
f.close()

Bu dosyayı tekrar okuduğunuzda, bir unicode nesnesinin kodunu çözebileceğiniz unicode kodlu bir dize alırsınız:

f = file('test', 'r')
print f.read().decode('utf8')

Teşekkürler. Bu hatasız çalışır, ancak daha sonra metin dosyasını açarsam, bir tuhaf semboller görüyorum :) Metni bir Wordpress sayfasına kopyalayıp yapıştırmam gerekiyor (sorma). Oradaki sembolleri basabilmemin bir yolu var mı? Sanırım bir txt dosyasına değil, ama başka bir şeye?
Simon

1
Metin dosyasını açmak için ne kullanıyorsunuz? Sanırım Windows'dasınız ve kodlamalarla çok akıllı olmayan Not Defteri'nde açıyorsunuz. Wordpad'de açtığınızda ne olur?
17'de kaasistoik

@quasistoic Dosya yöntemi nereden geliyor?
Omar Cusma Fait

İkili modu, yani f = open ('test', 'wb'), stackoverflow.com/a/5513856/6580199'da açıklandığı gibi açmam gerekiyordu - aksi takdirde "TypeError: write () argümanı str, bayt değil "
Benji

72

Python 2.6 ve sonraki sürümlerinde Python 3'te varsayılan ( yerleşik ) olanı kullanabilirsinizio.open() :open()

import io

with io.open(filename, 'w', encoding=character_encoding) as file:
    file.write(unicode_text)

Metni adım adım yazmanız gerekirse ( unicode_text.encode(character_encoding)birden çok kez aramanız gerekmez ) daha uygun olabilir . codecsModülün aksine , iomodül uygun bir evrensel satırsonu desteğine sahiptir.


1
Dostum, bunu bulmak için çok zaman harcadım! Teşekkür ederim!
Georgy Gobozov

2
Bu Python 3 için de geçerlidir (açık, ama yine de dikkat çekmeye değer).
Hippo

37

Unicode dize işleme zaten Python 3'te standartlaştırılmıştır.

  1. char'lar zaten Unicode'da (32 bit) bellekte saklanıyor
  2. Yalnızca utf-8'de dosyayı açmanız gerekir
    (32 bit Unicode'dan değişken bayt uzunluğuna utf-8 dönüşümü otomatik olarak bellekten dosyaya gerçekleştirilir.)

    out1 = "(嘉南大圳 ㄐㄧㄚ ㄋㄢˊ ㄉㄚˋ ㄗㄨㄣˋ )"
    fobj = open("t1.txt", "w", encoding="utf-8")
    fobj.write(out1)
    fobj.close()

Ama bu Python 2'de işe yaramıyor, değil mi? (Bu Python 3 kodunda çok özlü ve makul görünüyor) söylemeliyim
Liwen Zhao

Python 2 üzerinde çalışmamalı. Python 3'te kalıyoruz. 3 çok daha iyi.
david m lee

18

Tarafından açılan codecs.opendosya unicodeveri alan, kodlayan iso-8859-1ve dosyaya yazan bir dosyadır. Ancak, yazmaya çalıştığınız şey değil unicode; Çektiğiniz unicodeve bunu kodlamak iso-8859-1 kendine . Yani ne unicode.encodeyöntem yapar ve bir unicode dize kodlama sonucu bir bytestring (a isestr türü.)

Ya normale kullanmalıdır open()ve Unicode kendini, ya da (genellikle daha iyi bir fikir) kullanımını kodlamak codecs.open()ve olmayan verileri kendiniz kodlamak.


17

Önsöz: İzleyiciniz çalışır mı?

Görüntüleyicinizin / düzenleyicinizin / terminalinizin (utf-8 kodlu dosyanızla etkileşimde bulunuyorsanız) dosyayı okuyabildiğinden emin olun. Bu, Windows'ta sıklıkla bir sorundur, örneğin Not Defteri.

Bir metin dosyasına Unicode metin mi yazıyorsunuz?

Python 2, kullanım openile ilgili iomodül (bu yerleşiği aynıdır openPython 3):

import io

Genel olarak en iyi uygulama UTF-8dosyalara yazmak için kullanılır (utf-8 ile bayt sırası konusunda endişelenmemiz bile gerekmez).

encoding = 'utf-8'

utf-8 en modern ve evrensel olarak kullanılabilir kodlamadır - tüm web tarayıcılarında, çoğu metin editöründe (sorunlarınız varsa ayarlarınıza bakın) ve çoğu terminalde / kabukta çalışır.

Windows'ta, utf-16leçıktıyı Not Defteri'nde (veya başka bir sınırlı görüntüleyicide) görüntülemekle sınırlı olup olmadığınızı deneyebilirsiniz .

encoding = 'utf-16le' # sorry, Windows users... :(

Ve sadece içerik yöneticisi ile açın ve unicode karakterlerinizi yazın:

with io.open(filename, 'w', encoding=encoding) as f:
    f.write(unicode_object)

Çok sayıda Unicode karakter kullanma örneği

Aşağıda, olası tüm karakterleri 3 bit genişliğe kadar (4 maks. mümkün (bunu şu adlı bir dosyaya koyun uni.py):

from __future__ import print_function
import io
from unicodedata import name, category
from curses.ascii import controlnames
from collections import Counter

try: # use these if Python 2
    unicode_chr, range = unichr, xrange
except NameError: # Python 3
    unicode_chr = chr

exclude_categories = set(('Co', 'Cn'))
counts = Counter()
control_names = dict(enumerate(controlnames))
with io.open('unidata', 'w', encoding='utf-8') as f:
    for x in range((2**8)**3): 
        try:
            char = unicode_chr(x)
        except ValueError:
            continue # can't map to unicode, try next x
        cat = category(char)
        counts.update((cat,))
        if cat in exclude_categories:
            continue # get rid of noise & greatly shorten result file
        try:
            uname = name(char)
        except ValueError: # probably control character, don't use actual
            uname = control_names.get(x, '')
            f.write(u'{0:>6x} {1}    {2}\n'.format(x, cat, uname))
        else:
            f.write(u'{0:>6x} {1}  {2}  {3}\n'.format(x, cat, char, uname))
# may as well describe the types we logged.
for cat, count in counts.items():
    print('{0} chars of category, {1}'.format(count, cat))

Bu işlem yaklaşık bir dakika sürer ve veri dosyasını görüntüleyebilirsiniz; dosya görüntüleyiciniz unicode görüntüleyebiliyorsa, dosyayı görürsünüz. Kategoriler hakkında bilgi burada bulunabilir . Sayımlara dayanarak, muhtemelen kendileriyle ilişkili hiçbir sembol içermeyen Cn ve Co kategorilerini hariç tutarak sonuçlarımızı iyileştirebiliriz.

$ python uni.py

Onaltılık eşleme, kategori gösterilecek , sembol (adı alamadığı sürece, muhtemelen bir kontrol karakteri) ve sembolün adını görüntüler. Örneğin

lessUnix veya Cygwin'de öneriyorum (tüm dosyayı çıktılarınıza yazdırmayın / sıkıştırmayın):

$ less unidata

örneğin Python 2 (unicode 5.2) kullanarak örneklediğim aşağıdaki satırlara benzer şekilde görüntülenecektir:

     0 Cc NUL
    20 Zs     SPACE
    21 Po  !  EXCLAMATION MARK
    b6 So    PILCROW SIGN
    d0 Lu  Ð  LATIN CAPITAL LETTER ETH
   e59 Nd    THAI DIGIT NINE
  2887 So    BRAILLE PATTERN DOTS-1238
  bc13 Lo    HANGUL SYLLABLE MIH
  ffeb Sm    HALFWIDTH RIGHTWARDS ARROW

Anaconda'dan Python 3.5'in unicode 8.0'ı var, çoğu 3'ün olacağını tahmin ediyorum.


3

Unicode karakterler bir dosyaya nasıl yazdırılır:

Bunu dosyaya kaydedin: foo.py:

#!/usr/bin/python -tt
# -*- coding: utf-8 -*-
import codecs
import sys 
UTF8Writer = codecs.getwriter('utf8')
sys.stdout = UTF8Writer(sys.stdout)
print(u'e with obfuscation: é')

Çalıştırın ve çıktıyı dosyaya aktarın:

python foo.py > tmp.txt

Tmp.txt dosyasını açın ve içine bakın, şunu görüyorsunuz:

el@apollo:~$ cat tmp.txt 
e with obfuscation: é

Böylece unicode e'yi üzerinde bir işaret bulunan bir dosyaya kaydettiniz.


2
Bu cevap için çok heyecanlandım, ancak makinemde bir hata veriyor. Kodunuzu kopyaladığımda / yapıştırdığımda bir hata alıyorum: "TypeError: bayt değil, str olmalı"
Richard Rast

1

Bu hata, unicode olmayan bir dizeyi kodlamaya çalıştığınızda ortaya çıkar: düz ASCII'de olduğu varsayılarak kod çözülmeye çalışır. İki olasılık vardır:

  1. Bunu bir sınamaya kodluyorsunuz, ancak codecs.open kullandığınız için, write yöntemi bir unicode nesnesi bekliyor. Böylece onu kodlarsınız ve tekrar kodunu çözmeye çalışır. Deneyin:f.write(all_html) yerine.
  2. all_html aslında bir unicode nesnesi değildir. Bunu yaptığınızda .encode(...), önce kod çözmeye çalışır.

0

Python3 ile yazma durumunda

>>> a = u'bats\u00E0'
>>> print a
batsà
>>> f = open("/tmp/test", "w")
>>> f.write(a)
>>> f.close()
>>> data = open("/tmp/test").read()
>>> data
'batsà'

Python2 ile yazma durumunda:

>>> a = u'bats\u00E0'
>>> f = open("/tmp/test", "w")
>>> f.write(a)

Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
UnicodeEncodeError: 'ascii' codec can't encode character u'\xe0' in position 4: ordinal not in range(128)

Bu hatayı önlemek için, "utf-8" kodeklerini kullanarak baytlara kodlamanız gerekir:

>>> f.write(a.encode("utf-8"))
>>> f.close()

ve "utf-8" kodeklerini kullanarak verileri okurken kodu çözer:

>>> data = open("/tmp/test").read()
>>> data.decode("utf-8")
u'bats\xe0'

Ve ayrıca bu dizede baskı yürütmeye çalışırsanız, bunun gibi "utf-8" kodeklerini kullanarak otomatik olarak kod çözülür

>>> print a
batsà
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.