กำลังเขียนข้อความ Unicode ไปยังไฟล์ข้อความ?


225

ฉันดึงข้อมูลออกจาก Google doc ประมวลผลแล้วเขียนลงในไฟล์ (ในที่สุดฉันก็จะวางลงในหน้า Wordpress)

มันมีสัญลักษณ์ที่ไม่ใช่ ASCII ฉันจะแปลงสิ่งเหล่านี้อย่างปลอดภัยเป็นสัญลักษณ์ที่สามารถใช้ในซอร์ส HTML ได้อย่างไร

ขณะนี้ฉันกำลังแปลงทุกอย่างเป็น Unicode ระหว่างทางรวมเข้าด้วยกันในสตริง Python จากนั้นทำ:

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

มีข้อผิดพลาดในการเข้ารหัสในบรรทัดสุดท้าย:

UnicodeDecodeError: ตัวแปลงสัญญาณ 'ascii' ไม่สามารถถอดรหัสไบต์ 0xa0 ในตำแหน่ง 12286: ลำดับไม่อยู่ในช่วง (128)

โซลูชันบางส่วน:

Python นี้ทำงานโดยไม่มีข้อผิดพลาด:

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"))

แต่ถ้าฉันเปิดไฟล์ข้อความจริงฉันเห็นสัญลักษณ์มากมายเช่น:

Qur’an 

บางทีฉันต้องเขียนถึงสิ่งอื่นนอกเหนือจากไฟล์ข้อความ?


1
โปรแกรมที่คุณใช้เปิดไม่แปลข้อความ UTF-8 อย่างถูกต้อง มันควรจะมีตัวเลือกในการเปิดไฟล์เป็น UTF-8
โทมัส K

คำตอบ:


322

จัดการเฉพาะกับวัตถุ Unicode ให้มากที่สุดโดยการถอดรหัสสิ่งของเป็น Unicode วัตถุเมื่อคุณได้รับพวกเขาครั้งแรกและเข้ารหัสตามความจำเป็นในการออก

หากสตริงของคุณเป็นวัตถุ Unicode คุณจะต้องแปลงเป็นสตริงวัตถุที่เข้ารหัสแบบ Unicode ก่อนที่จะเขียนลงในไฟล์:

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

เมื่อคุณอ่านไฟล์นั้นอีกครั้งคุณจะได้รับสตริงที่เข้ารหัสแบบ Unicode ซึ่งคุณสามารถถอดรหัสเป็นวัตถุ Unicode ได้:

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

ขอบคุณ สิ่งนี้จะทำงานโดยไม่มีข้อผิดพลาด แต่ถ้าฉันเปิดไฟล์ข้อความฉันเห็นสัญลักษณ์แปลก ๆ มากมาย :) ฉันต้องคัดลอกและวางข้อความในหน้า Wordpress (ไม่ต้องถาม) มีวิธีใดบ้างที่ฉันจะพิมพ์สัญลักษณ์ที่มีอยู่จริง? ฉันเดาว่าไม่ใช่ไฟล์ txt ใช่ แต่อาจเป็นอย่างอื่นใช่ไหม
simon

1
คุณใช้อะไรเพื่อเปิดไฟล์ข้อความ ฉันเดาว่าคุณใช้ Windows แล้วคุณเปิดมันใน Notepad ซึ่งไม่ฉลาดเกินไปกับการเข้ารหัส จะเกิดอะไรขึ้นเมื่อคุณเปิดใน Wordpad
quasistoic

@quasistoic วิธีการที่ไฟล์มาในรูปแบบ?
Omar Cusma Fait

ฉันต้องการเปิดโหมดไบนารีเช่น f = open ('test', 'wb') ตามที่อธิบายไว้ในstackoverflow.com/a/5513856/6580199 - ไม่เช่นนั้นฉันจะได้รับ "TypeError: write () ต้องเป็น str, ไม่ใช่ไบต์ "
Benji

72

ใน Python 2.6+ คุณสามารถใช้io.open()สิ่งนั้นเป็นค่าเริ่มต้น ( บิวอินopen() ) บน Python 3:

import io

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

มันอาจจะสะดวกกว่าถ้าคุณต้องการเขียนข้อความแบบเพิ่มหน่วย (คุณไม่จำเป็นต้องโทรunicode_text.encode(character_encoding)หลายครั้ง) แตกต่างจากcodecsโมดูลioโมดูลมีการสนับสนุนบรรทัดใหม่สากลที่เหมาะสม


1
ผู้ชายฉันใช้เวลามากมายในการค้นหาสิ่งนี้! ขอบคุณ!
Georgy Gobozov

2
สิ่งนี้ใช้ได้กับ Python 3 ด้วย (ชัดเจน แต่ก็คุ้มค่าที่จะชี้ให้เห็น)
Hippo

37

การจัดการสตริง Unicode ได้มาตรฐานแล้วใน Python 3

  1. char's ถูกเก็บไว้ใน Unicode (32 บิต) ในหน่วยความจำแล้ว
  2. คุณจะต้องเปิดไฟล์ใน utf-8
    (การแปลง Unicode แบบ 32 บิตเป็นความยาวผันแปรแบบ utf-8 จะดำเนินการโดยอัตโนมัติจากหน่วยความจำไปยังไฟล์)

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

แต่มันใช้ไม่ได้กับ Python 2 ใช่มั้ย (ฉันควรจะบอกว่าในรหัส Python 3 นี้มันดูกระชับและสมเหตุสมผล)
Liwen Zhao

มันไม่ควรทำงานกับ Python 2 เรายังคงใช้ Python 3 อยู่ดีขึ้น
david m lee

18

ไฟล์ที่เปิดโดยcodecs.openเป็นไฟล์ที่ใช้unicodeข้อมูลเข้ารหัสiso-8859-1และเขียนลงในไฟล์ แต่สิ่งที่คุณพยายามที่จะเขียนไม่ได้unicode; คุณใช้เวลาunicodeและการเข้ารหัสในตัวเองiso-8859-1 นั่นคือสิ่งที่unicode.encodeวิธีการทำและผลลัพธ์ของการเข้ารหัสสตริง Unicode เป็น bytestring ( strชนิด)

คุณควรใช้แบบปกติopen()และเข้ารหัสยูนิโค้ดด้วยตัวเองหรือใช้ความคิดที่ดีกว่าcodecs.open()และไม่เข้ารหัสข้อมูลด้วยตัวคุณเอง


17

คำนำ: ผู้ชมของคุณจะทำงานหรือไม่

ตรวจสอบให้แน่ใจว่าวิวเวอร์ / บรรณาธิการ / เทอร์มินัลของคุณ (อย่างไรก็ตามคุณกำลังโต้ตอบกับไฟล์ที่เข้ารหัส utf-8 ของคุณ) สามารถอ่านไฟล์ได้ นี่เป็นปัญหาที่พบบ่อยในWindowsเช่น Notepad

กำลังเขียนข้อความ Unicode ไปยังไฟล์ข้อความ?

ใน Python 2 ให้ใช้openจากioโมดูล (ซึ่งเหมือนกับใน builtin openใน Python 3):

import io

วิธีปฏิบัติที่ดีที่สุดโดยทั่วไปใช้UTF-8สำหรับเขียนไฟล์ (เราไม่ต้องกังวลกับการสั่งไบต์ด้วย utf-8)

encoding = 'utf-8'

utf-8 เป็นการเข้ารหัสที่ทันสมัยที่สุดและใช้งานได้ในระดับสากลใช้ได้กับทุกเว็บเบราว์เซอร์ตัวแก้ไขข้อความส่วนใหญ่ (ดูการตั้งค่าของคุณหากคุณมีปัญหา) และเทอร์มินัล / เชลล์ส่วนใหญ่

บน Windows คุณอาจลองใช้utf-16leหากคุณ จำกัด การรับชมใน Notepad (หรือตัวแสดงที่ จำกัด อื่น)

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

และเพียงแค่เปิดมันด้วยตัวจัดการบริบทและเขียนตัวอักษรยูนิโค้ดของคุณออกมา:

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

ตัวอย่างการใช้อักขระ Unicode จำนวนมาก

นี่คือตัวอย่างที่พยายามแมปอักขระที่เป็นไปได้ทุกตัวที่มีความกว้างสูงสุดสามบิต (4 คือสูงสุด แต่นั่นอาจจะไกลไปหน่อย) จากการแสดงแบบดิจิทัล (เป็นจำนวนเต็ม) ไปยังเอาต์พุตที่เข้ารหัสได้พร้อมชื่อ เป็นไปได้ (ใส่ลงในไฟล์ที่เรียกว่า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))

สิ่งนี้จะทำงานในเวลาประมาณหนึ่งนาทีและคุณสามารถดูไฟล์ข้อมูลและหากโปรแกรมดูไฟล์ของคุณสามารถแสดงยูนิโค้ดคุณจะเห็นมัน ข้อมูลเกี่ยวกับประเภทที่สามารถพบได้ที่นี่ จากการนับเราอาจปรับปรุงผลลัพธ์ของเราได้โดยไม่รวมหมวดหมู่ Cn และ Co ซึ่งไม่มีสัญลักษณ์ที่เกี่ยวข้อง

$ python uni.py

มันจะแสดงการทำแผนที่เลขฐานสิบหก, หมวดหมู่ , สัญลักษณ์ (เว้นแต่จะไม่สามารถรับชื่อ, ดังนั้นอาจเป็นตัวควบคุม) และชื่อของสัญลักษณ์ เช่น

ฉันแนะนำlessบน Unix หรือ Cygwin (อย่าพิมพ์ / cat ไฟล์ทั้งหมดไปยังผลลัพธ์ของคุณ):

$ less unidata

เช่นจะแสดงคล้ายกับบรรทัดต่อไปนี้ซึ่งฉันสุ่มตัวอย่างจากมันโดยใช้ Python 2 (unicode 5.2):

     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

Python 3.5 ของฉันจาก Anaconda มียูนิโค้ด 8.0 ฉันจะทึกทักถึง 3 อย่างที่สุด


3

วิธีพิมพ์อักขระ Unicode ลงในไฟล์:

บันทึกลงในไฟล์: 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: é')

เรียกใช้และส่งออกไพพ์ไปยังไฟล์:

python foo.py > tmp.txt

เปิด tmp.txt แล้วมองเข้าไปข้างในคุณจะเห็นสิ่งนี้:

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

ดังนั้นคุณบันทึกยูนิโคด e ที่มีเครื่องหมาย obfuscation ไว้ในไฟล์


2
ฉันค่อนข้างตื่นเต้นกับคำตอบนี้ แต่มันมีข้อผิดพลาดบนเครื่องของฉัน เมื่อฉันคัดลอก / วางรหัสของคุณฉันได้รับข้อผิดพลาด: "TypeError: ต้องเป็น STR ไม่ใช่ไบต์"
Richard Rast

1

ข้อผิดพลาดนั้นเกิดขึ้นเมื่อคุณพยายามเข้ารหัสสตริงที่ไม่ใช่ยูนิโค้ด: มันพยายามถอดรหัสมันโดยสมมติว่าเป็น ASCII ธรรมดา มีความเป็นไปได้สองอย่าง:

  1. คุณกำลังเข้ารหัสเพื่อ bytestring แต่เนื่องจากคุณใช้ตัวแปลงสัญญาณเปิดวิธีการเขียนจึงคาดว่าจะเป็นวัตถุ Unicode ดังนั้นคุณเข้ารหัสมันและมันพยายามที่จะถอดรหัสอีกครั้ง ลอง: f.write(all_html)แทน
  2. all_html ไม่ใช่วัตถุ unicode เมื่อคุณทำอย่าง.encode(...)แรกมันจะพยายามถอดรหัสมัน

0

ในกรณีที่เขียนใน python3

>>> 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:

>>> 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)

เพื่อหลีกเลี่ยงข้อผิดพลาดนี้คุณจะต้องเข้ารหัสเป็นไบต์โดยใช้ตัวแปลงสัญญาณ "utf-8" ดังนี้:

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

และถอดรหัสข้อมูลในขณะที่อ่านโดยใช้ตัวแปลงสัญญาณ "utf-8":

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

และถ้าคุณพยายามพิมพ์บนสายนี้มันจะถอดรหัสโดยอัตโนมัติโดยใช้ตัวแปลงสัญญาณ "utf-8" เช่นนี้

>>> print a
batsà
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.