Python'daki dosyalara Unicode (UTF-8) okuma ve yazma


329

Bir dosyaya metin okumayı ve yazmayı anlamada beyin yetmezliği yaşıyorum (Python 2.4).

# The string, which has an a-acute in it.
ss = u'Capit\xe1n'
ss8 = ss.encode('utf8')
repr(ss), repr(ss8)

("u'Capit \ xe1n '", "' Capit \ xc3 \ xa1n '")

print ss, ss8
print >> open('f1','w'), ss8

>>> file('f1').read()
'Capit\xc3\xa1n\n'

Bu yüzden Capit\xc3\xa1nfavori düzenleyicime f2 dosyasında yazıyorum.

Sonra:

>>> open('f1').read()
'Capit\xc3\xa1n\n'
>>> open('f2').read()
'Capit\\xc3\\xa1n\n'
>>> open('f1').read().decode('utf8')
u'Capit\xe1n\n'
>>> open('f2').read().decode('utf8')
u'Capit\\xc3\\xa1n\n'

Burada ne anlamıyorum? Açıkça, eksik olduğum bazı hayati büyü (ya da sağduyu) var. Doğru dönüşümleri elde etmek için metin dosyalarına ne yazılır?

Buraya gerçekten bakamadığım şey, dışarıdan geldiğinde Python'u gerçekten tanıyamazsanız, UTF-8 temsilinin amacı nedir. Belki de sadece JSON dizeyi dökmeli ve bunun yerine kullanmalıyım, çünkü bu ascii bir temsile sahip! Daha da önemlisi, bu Unicode nesnesinin Python'un bir dosyadan gelirken tanıyacağı ve kodunu çözeceği bir ASCII temsili var mı? Eğer öyleyse, nasıl alabilirim?

>>> print simplejson.dumps(ss)
'"Capit\u00e1n"'
>>> print >> file('f3','w'), simplejson.dumps(ss)
>>> simplejson.load(open('f3'))
u'Capit\xe1n'

Yanıtlar:


110

Gösterimde

u'Capit\xe1n\n'

"\ xe1" yalnızca bir baytı temsil eder. "\ x" size "e1" in onaltılık olduğunu belirtir. Yazdığın zaman

Capit\xc3\xa1n

içine "\ xc3" eklenir. Bunlar 4 bayt ve kodunuzda hepsini okudunuz. Bunları görüntülerken görebilirsiniz:

>>> open('f2').read()
'Capit\\xc3\\xa1n\n'

Ters eğik çizginin ters eğik çizgiden kaçtığını görebilirsiniz. Dizenizde dört bayt var: "\", "x", "c" ve "3".

Düzenle:

Diğerlerinin cevaplarında belirttiği gibi, sadece editöre karakterleri girmelisiniz ve editörünüz UTF-8'e dönüştürmeyi yapmalı ve kaydetmelidir.

Aslında bu biçimde bir dizeniz varsa, string_escapekod çözücüyü normal bir dizeye çözmek için kullanabilirsiniz :

In [15]: print 'Capit\\xc3\\xa1n\n'.decode('string_escape')
Capitán

Sonuç, aksanlı karakterin \\xc3\\xa1orijinal dizede yazılan iki baytla temsil edildiği UTF-8'de kodlanan bir dizedir. Unicode dizeye sahip olmak istiyorsanız, UTF-8 ile tekrar kod çözmeniz gerekir.

Düzenlemeniz için: dosyanızda UTF-8 bulunmuyor. Aslında nasıl görüneceğini görmek için:

s = u'Capit\xe1n\n'
sutf8 = s.encode('UTF-8')
open('utf-8.out', 'w').write(sutf8)

Dosyanın utf-8.outiçeriğini düzenleyicinizle kaydettiğiniz dosyanın içeriğiyle karşılaştırın .


Peki, python bunu kullanarak dosyalarda okuyabilirse utf-8 kodlu formatın anlamı nedir? Başka bir deyişle, python \ \ c3 içinde 1 bayt olarak okuyacak herhangi bir ascii temsili var mı?
Gregg Lind

4
"Peki, mesele nedir?" Sorunuzun cevabı "Mu" dir. (çünkü Python UTF-8 ile kodlanmış dosyaları okuyabilir). İkinci sorunuz için: \ xc3 ASCII setinin bir parçası değildir. Belki de bunun yerine "8 bit kodlama" demek istediniz. Unicode ve kodlamalar konusunda kafanız karıştı; tamam, birçoğu.
tzot

8
Bunu bir astar olarak okumayı deneyin: joelonsoftware.com/articles/Unicode.html
tzot

not: karakter kodlamasına bağlı olarak 1 veya daha fazla bayt kullanılarak temsil edilebilen u'\xe1'bir Unicode kod noktasıdır (utf-8'de 2 bayttır). (Bir sayı 225), bu temsil edebilir varsa hangi harf karakterine bağlıdır bir byte mesela bunu çözmek için kullanılan kodlama, öyle ( ) CP1251 içinde, ( ) CP866 yılında, vbU+00e1b'\xe1'бU+0431сU+0441
jfs

11
Kaç İngiliz kodlayıcı "sadece ascii kullanın" demek şaşırtıcı ve sonra £ işareti olmadığını fark edemiyorum şaşırtıcı. Çoğu farkında değil ascii! = Yerel kod sayfası (yani latin1).
Danny Staple

712

Kodlama ve kod çözme yöntemlerini karıştırmak yerine dosyayı açarken kodlamayı belirtmeyi daha kolay buluyorum. ioModül (Python 2.6 ilave edildi), bir sağlar io.openbir kodlama parametresi vardır fonksiyonu.

ioModüldeki open yöntemini kullanın .

>>>import io
>>>f = io.open("test", mode="r", encoding="utf-8")

Daha sonra f's read () işlevini çağırdıktan sonra, kodlanmış bir Unicode nesnesi döndürülür.

>>>f.read()
u'Capit\xe1l\n\n'

Python 3'te, io.openişlevin yerleşik işlev için bir diğer ad olduğunu unutmayın open. Yerleşik açık işlevi Python 2'de değil, yalnızca Python 3'te kodlama bağımsız değişkenini destekler.

Düzenleme: Daha önce bu cevap codec bileşenleri modülünü önerdi . Codec modülü karıştırma sorunlara neden olabilir read()vereadline() bu cevap artık önerir yüzden, io yerine modülü.

Codec modülünden open yöntemini kullanın.

>>>import codecs
>>>f = codecs.open("test", "r", "utf-8")

Daha sonra f's read () işlevini çağırdıktan sonra, kodlanmış bir Unicode nesnesi döndürülür.

>>>f.read()
u'Capit\xe1l\n\n'

Bir dosyanın kodlamasını biliyorsanız, codec paketini kullanmak çok daha az kafa karıştırıcı olacaktır.

Bkz. Http://docs.python.org/library/codecs.html#codecs.open


74
Yerine de dosya yazmak için mükemmel çalışıyor open(file,'w')do codecs.open(file,'w','utf-8')çözüldü
Matt Connolly

1
Aradığım cevap
Justin

6
Does codecs.open(...)yöntem de tam uygun with open(...):bir tarzda, withsonuçta dosyayı kapatılması hakkında umurunda yapılır? Yine de çalışıyor gibi görünüyor.
try-catch-nihayet

2
@ try-catch-nihayet Evet. Her with codecs.open(...) as f:zaman kullanıyorum.
Tim Swast

6
Keşke bunu yüz kere tekrarlayabilseydim. Birçok karışık verinin neden olduğu kodlama sorunları ve kodlama hakkında şaşılacak okumalar yüzünden birkaç gün boyunca acı çektikten sonra, bu cevap bir çölde su gibidir. Keşke daha önce görseydim.
Mike Girard

45

Şimdi Python3'te ihtiyacınız olan tek şey open(Filename, 'r', encoding='utf-8')

[İstenen açıklama için 2016-02-10 tarihinde düzenleyin]

Python3, kodlama parametresini açık işlevine ekledi . Open işlevi hakkında aşağıdaki bilgiler buradan toplanır: https://docs.python.org/3/library/functions.html#open

open(file, mode='r', buffering=-1, 
      encoding=None, errors=None, newline=None, 
      closefd=True, opener=None)

Kodlama, dosyanın kodunu çözmek veya kodlamak için kullanılan kodlamanın adıdır. Bu yalnızca metin modunda kullanılmalıdır. Varsayılan kodlama platforma bağlıdır (hangi locale.getpreferredencoding () döndürürse), ancak Python tarafından desteklenen herhangi bir metin kodlaması kullanılabilir. Desteklenen kodlamaların listesi için codec bileşenleri modülüne bakın .

Bu nedenle encoding='utf-8', open fonksiyonuna bir parametre olarak eklenerek , dosya okuma ve yazma işlemlerinin tümü utf8 olarak yapılır (bu şimdi Python'da yapılan her şeyin varsayılan kodlamasıdır).


Verdiğiniz çözüm hakkında biraz daha açıklama ekleyerek cevabınızı biraz daha açıklayabilir misiniz?
abarisone

2
Bu, python 2'de codecs modülü kullanılarak kullanılabilir - codecs.open('somefile', encoding='utf-8') stackoverflow.com/a/147756/149428
Taylor Edmiston

18

Yani, aradığım şey için bir çözüm buldum, yani:

print open('f2').read().decode('string-escape').decode("utf-8")

Burada yararlı olan bazı olağandışı kodekler var. Bu özel okuma, Python içinden UTF-8 temsillerinin alınmasına, bir ASCII dosyasına kopyalanmasına ve Unicode'a okunmasına izin verir. "String-escape" kod çözme altında, eğik çizgiler iki katına çıkmaz.

Bu, hayal ettiğim bir çeşit gidiş dönüşe izin veriyor.


1
İyi yanıt, her iki çözümü de test ettim (codecs.open(file,"r","utf-8")ve basitçe open(file,"r").read().decode("utf-8")ve her ikisi de mükemmel çalıştı.
Kartal

Neden herhangi bir fikir değil "TypeError: beklenen str, bayt veya os.PathLike nesnesi, alıyorum."
JinSnow

Bence, upvotes sayısı göz önüne alındığında, ikinci cevabı kabul etmek harika bir fikir olurdu :)
Jacquot

14
# -*- encoding: utf-8 -*-

# converting a unknown formatting file in utf-8

import codecs
import commands

file_location = "jumper.sub"
file_encoding = commands.getoutput('file -b --mime-encoding %s' % file_location)

file_stream = codecs.open(file_location, 'r', file_encoding)
file_output = codecs.open(file_location+"b", 'w', 'utf-8')

for l in file_stream:
    file_output.write(l)

file_stream.close()
file_output.close()

14

Aslında, bu benim için Python 3.2'de UTF-8 kodlaması olan bir dosyayı okumak için çalıştı:

import codecs
f = codecs.open('file_name.txt', 'r', 'UTF-8')
for line in f:
    print(line)

6

Unicode dizesinde okumak ve HTML'ye göndermek için bunu yaptım:

fileline.decode("utf-8").encode('ascii', 'xmlcharrefreplace')

Python destekli http sunucuları için kullanışlıdır.


6

Kodlamalar ile ilgili genel sorun üzerinde tökezlediniz: Bir dosyanın hangi kodlamasında olduğunu nasıl anlayabilirim?

Yanıt: Dosya biçimi bunu sağlamadığı sürece yapamazsınız . Örneğin, XML aşağıdakilerle başlar:

<?xml encoding="utf-8"?>

Bu başlık, kodlama ne olursa olsun okunabilmesi için dikkatle seçilmiştir. Sizin durumunuzda böyle bir ipucu yok, bu yüzden ne editörünüz ne de Python'un neler olduğu hakkında hiçbir fikri yok. Bu nedenle, codecsmodülü kullanmanız vecodecs.open(path,mode,encoding) ve Python'da eksik olan biti .

Düzenleyicinize gelince, dosyanın kodlamasını ayarlamak için bir yol sunup sunmadığını kontrol etmelisiniz.

UTF-8'in amacı 21 bit karakterleri (Unicode) 8 bit veri akışı olarak kodlayabilmektir (çünkü dünyadaki tüm bilgisayarların işleyebileceği tek şey budur). Ancak çoğu işletim sistemi Unicode döneminden önce geldiğinden, kodlama bilgilerini sabit diskteki dosyalara eklemek için uygun araçlara sahip değildir.

Bir sonraki konu Python'daki temsilidir. Bu yorum heikogerlach tarafından mükemmel açıklanmıştır . Konsolunuzun yalnızca ASCII görüntüleyebileceğini anlamalısınız. Unicode veya herhangi bir şey = = charcode 128'i görüntülemek için, bazı çıkış yöntemleri kullanmalıdır. Düzenleyicinizde, çıkış karakter dizisini değil, dizenin ne anlama geldiğini yazmalısınız (bu durumda umlaut'a girmeli ve dosyayı kaydetmelisiniz).

Bununla birlikte, kaçan bir dizeyi bir dizeye dönüştürmek için Python işlevi eval () kullanabilirsiniz:

>>> x = eval("'Capit\\xc3\\xa1n\\n'")
>>> x
'Capit\xc3\xa1n\n'
>>> x[5]
'\xc3'
>>> len(x[5])
1

Gördüğünüz gibi, "\ xc3" dizesi tek bir karaktere dönüştürüldü. Bu artık 8 bitlik bir dize, UTF-8 kodlu. Unicode'u almak için:

>>> x.decode('utf-8')
u'Capit\xe1n\n'

Gregg Lind sordu: Bence burada eksik bazı parçalar var: f2 dosyası içerir: hex:

0000000: 4361 7069 745c 7863 335c 7861 316e  Capit\xc3\xa1n

codecs.open('f2','rb', 'utf-8'), örneğin, hepsini ayrı bir karakterde okur (beklenen) ASCII'de çalışacak bir dosyaya yazmanın herhangi bir yolu var mı?

Cevap: Bu ne demek istediğine bağlı. ASCII,> 127 karakterlerini temsil edemez. Bu nedenle, "sonraki birkaç karakter özel bir şey ifade ediyor" demenin bir yolu olması gerekir, bu da "\ x" dizisinin yaptığıdır. Diyor ki: Sonraki iki karakter tek bir karakterin kodudur. "\ u" aynı şeyi 0xFFFF (65535) 'e kadar Unicode kodlamak için dört karakter kullanarak yapar.

Dolayısıyla, doğrudan Unicode'u ASCII'ye yazamazsınız (çünkü ASCII aynı karakterleri içermez). Dize kaçışları olarak yazabilirsiniz (f2'de olduğu gibi); bu durumda, dosya ASCII olarak temsil edilebilir. Veya UTF-8 olarak yazabilirsiniz, bu durumda 8 bit güvenli bir akışa ihtiyacınız vardır.

Kullanmakta decode('string-escape')olduğunuz çözüm işe yarıyor, ancak ne kadar bellek kullandığınızı bilmelisiniz:codecs.open() .

Bir dosyanın sadece 8 bitlik bir bayt dizisi olduğunu unutmayın. Ne bitlerin ne de baytların bir anlamı yoktur. "65" A "anlamına gelen sizsiniz. Yana \xc3\xa1"à" olması gerektiğini ancak bilgisayar bilmek imkanı bulunmadığını, dosyayı yazarken kullanılan kodlamayı belirterek söylemek gerekir.


Burada eksik bazı parçalar olduğunu düşünüyorum: f2 dosyası içerir: hex: 0000000: 4361 7069 745c 7863 335c 7861 316e 0a Capit \ xc3 \ xa1n. Örneğin, codecs.open ('f2', 'rb', 'utf-8'), hepsini ayrı bir karakterde okur (beklenen) ASCII'de çalışacak bir dosyaya yazmanın herhangi bir yolu var mı?
Gregg Lind

6

dışında codecs.open(), io.open()unicode dosyasını okumak / yazmak için Python2 veya Python3 ile çalışmak

misal

import io

text = u'á'
encoding = 'utf8'

with io.open('data.txt', 'w', encoding=encoding, newline='\n') as fout:
    fout.write(text)

with io.open('data.txt', 'r', encoding=encoding, newline='\n') as fin:
    text2 = fin.read()

assert text == text2


Evet, io kullanmak daha iyidir; Ama ben böyle deyimi ile yazdığı with io.open('data.txt', 'w', 'utf-8') as file:ve bir hata var: TypeError: an integer is required. Sonra değişti with io.open('data.txt', 'w', encoding='utf-8') as file:ve işe yaradı.
Evan Hu

5

En sevdiğiniz metin düzenleyiciniz, \xc3\xa1karakter değişmezleri olması gerektiğini fark etmez , ancak bunları metin olarak yorumlar. Bu yüzden son satırdaki çift ters eğik çizgiyi elde edersiniz - artık xc3dosyanızda gerçek bir ters eğik çizgi + vb.

Python'da kodlanmış dosyaları okumak ve yazmak istiyorsanız, en iyi codec bileşenlerini kullanın modülünü .

Terminal ve uygulamalar arasında metin yapıştırmak zordur, çünkü hangi programın metninizi hangi kodlamayı kullanarak yorumlayacağını bilmiyorsunuz. Aşağıdakileri deneyebilirsiniz:

>>> s = file("f1").read()
>>> print unicode(s, "Latin-1")
Capitán

Ardından bu dizeyi düzenleyicinize yapıştırın ve Latin-1 kullanarak dizesini sakladığından emin olun. Panonun dizgeyi bozmadığı varsayımı altında, gidiş-dönüş çalışması gerekir.


4

\ X .. dizisi Python'a özgü bir şeydir. Evrensel bir bayt kaçış dizisi değildir.

UTF-8 kodlu ASCII olmayan gerçekte nasıl gireceğiniz, işletim sisteminize ve / veya düzenleyicinize bağlıdır. Windows'ta bunu nasıl yapacağınız aşağıda açıklanmıştır . OS X'in akut aksanlı bir a girmesi içinoption + Etuşuna basmanız yeterlidir Ave OS X'teki neredeyse tüm metin editörleri UTF-8'i destekler.


3

Orijinal open()işlevi, yerine kullanarak, partialyerine kullanarak Unicode dosyalarıyla çalışmak üzere iyileştirebilirsiniz . Bu çözümün güzelliği, eski kodları değiştirmenize gerek olmamasıdır. Şeffaf.

import codecs
import functools
open = functools.partial(codecs.open, encoding='utf-8')

1

Python 2.7.9 kullanarak iCal ayrıştırma çalışıyordu :

icalendar ithalat Takviminden

Ama alıyordum:

 Traceback (most recent call last):
 File "ical.py", line 92, in parse
    print "{}".format(e[attr])
UnicodeEncodeError: 'ascii' codec can't encode character u'\xe1' in position 7: ordinal not in range(128)

ve sadece şu şekilde düzeltildi:

print "{}".format(e[attr].encode("utf-8"))

(Artık böss gibi yazdırabilir.)


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.