تحويل البايتات إلى سلسلة؟
-
03-07-2019 - |
سؤال
أنا أستخدم هذا الرمز للحصول على الإخراج القياسي من برنامج خارجي:
>>> from subprocess import *
>>> command_stdout = Popen(['ls', '-l'], stdout=PIPE).communicate()[0]
يُرجع أسلوب التواصل () مصفوفة من البايتات:
>>> command_stdout
b'total 0\n-rw-rw-r-- 1 thomas thomas 0 Mar 3 07:03 file1\n-rw-rw-r-- 1 thomas thomas 0 Mar 3 07:03 file2\n'
ومع ذلك، أود العمل مع الإخراج كسلسلة بايثون عادية.حتى أتمكن من طباعتها هكذا:
>>> print(command_stdout)
-rw-rw-r-- 1 thomas thomas 0 Mar 3 07:03 file1
-rw-rw-r-- 1 thomas thomas 0 Mar 3 07:03 file2
اعتقدت أن هذا ما binascii.b2a_qp() الطريقة مخصصة، ولكن عندما جربتها، حصلت على نفس مصفوفة البايت مرة أخرى:
>>> binascii.b2a_qp(command_stdout)
b'total 0\n-rw-rw-r-- 1 thomas thomas 0 Mar 3 07:03 file1\n-rw-rw-r-- 1 thomas thomas 0 Mar 3 07:03 file2\n'
هل يعرف أحد كيفية تحويل قيمة البايتات مرة أخرى إلى السلسلة؟أعني استخدام "البطاريات" بدلاً من القيام بذلك يدويًا.وأود أن يكون الأمر على ما يرام مع Python 3.
المحلول
وتحتاج إلى فك الكائن بايت لإنتاج سلسلة:
>>> b"abcde"
b'abcde'
# utf-8 is used here because it is a very common encoding, but you
# need to use the encoding your data is actually in.
>>> b"abcde".decode("utf-8")
'abcde'
نصائح أخرى
وأعتقد أن هذه الطريقة سهلة:
bytes_data = [112, 52, 52]
"".join(map(chr, bytes_data))
>> p44
وتحتاج إلى فك شفرة سلسلة بايت وتحويله في ل(يونيكود) سلسلة أحرف.
في بايثون 2
encoding = 'utf-8'
b'hello'.decode(encoding)
في بيثون 3
encoding = 'utf-8'
str(b'hello', encoding)
إذا كنت لا تعرف التشفير، فاستخدم MS-DOS القديم لقراءة الإدخال الثنائي في السلسلة بطريقة متوافقة مع Python 3 وPython 2 cp437 التشفير:
PY3K = sys.version_info >= (3, 0)
lines = []
for line in stream:
if not PY3K:
lines.append(line)
else:
lines.append(line.decode('cp437'))
نظرًا لأن التشفير غير معروف، توقع ترجمة الرموز غير الإنجليزية إلى أحرف cp437
(لا تتم ترجمة الأحرف الإنجليزية، لأنها تتطابق في معظم ترميزات البايت الفردي وUTF-8).
يعد فك تشفير الإدخال الثنائي التعسفي إلى UTF-8 أمرًا غير آمن، لأنك قد تحصل على ما يلي:
>>> b'\x00\x01\xffsd'.decode('utf-8')
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
UnicodeDecodeError: 'utf-8' codec can't decode byte 0xff in position 2: invalid
start byte
الأمر نفسه ينطبق على latin-1
, ، والذي كان شائعًا (افتراضيًا؟) لـ Python 2.انظر النقاط المفقودة في تخطيط صفحة الرموز - هذا هو المكان الذي تختنق فيه بايثون بالسيء السمعة ordinal not in range
.
تحديث 20150604:هناك شائعات بأن بايثون 3 لديها surrogateescape
استراتيجية الخطأ لتشفير الأشياء إلى بيانات ثنائية دون فقدان البيانات وتعطلها، ولكنها تحتاج إلى اختبارات التحويل [binary] -> [str] -> [binary]
للتحقق من صحة كل من الأداء والموثوقية.
تحديث 20170116:بفضل تعليق Nearoo - هناك أيضًا إمكانية لخفض الهروب من جميع وحدات البايت غير المعروفة باستخدام backslashreplace
معالج الأخطاء.يعمل هذا فقط مع Python 3، لذلك حتى مع هذا الحل البديل، ستظل تحصل على مخرجات غير متناسقة من إصدارات Python المختلفة:
PY3K = sys.version_info >= (3, 0)
lines = []
for line in stream:
if not PY3K:
lines.append(line)
else:
lines.append(line.decode('utf-8', 'backslashreplace'))
يرى https://docs.python.org/3/howto/unicode.html#python-s-unicode-support للتفاصيل.
تحديث 20170119:قررت تنفيذ فك تشفير الهروب المائل الذي يعمل مع كل من Python 2 و Python 3.ينبغي أن يكون أبطأ ذلك cp437
الحل، ولكن ينبغي أن تنتج نتائج متطابقة في كل إصدار بايثون.
# --- preparation
import codecs
def slashescape(err):
""" codecs error handler. err is UnicodeDecode instance. return
a tuple with a replacement for the unencodable part of the input
and a position where encoding should continue"""
#print err, dir(err), err.start, err.end, err.object[:err.start]
thebyte = err.object[err.start:err.end]
repl = u'\\x'+hex(ord(thebyte))[2:]
return (repl, err.end)
codecs.register_error('slashescape', slashescape)
# --- processing
stream = [b'\x80abc']
lines = []
for line in stream:
lines.append(line.decode('utf-8', 'slashescape'))
في بايثون 3, ، الترميز الافتراضي هو "utf-8"
, ، لذلك يمكنك استخدام مباشرة:
b'hello'.decode()
وهو ما يعادل
b'hello'.decode(encoding="utf-8")
على الجانب الآخر، في بايثون 2, ، الترميز الافتراضي هو ترميز السلسلة الافتراضي.وبالتالي، يجب عليك استخدام:
b'hello'.decode(encoding)
أين encoding
هو الترميز الذي تريده
ملحوظة: تمت إضافة دعم لوسائط الكلمات الرئيسية في Python 2.7.
وأعتقد أن ما تريد في الواقع هو هذا:
>>> from subprocess import *
>>> command_stdout = Popen(['ls', '-l'], stdout=PIPE).communicate()[0]
>>> command_text = command_stdout.decode(encoding='windows-1252')
وكانت الجواب هارون الصحيحة، إلا أن عليك أن تعرف في اي ترميز للاستخدام. وأعتقد أن يستخدم ويندوز 'ويندوز 1252 ". وسوف يهم فقط إذا كان لديك بعض غير عادية (غير أسكي) الأحرف في المحتوى الخاص بك، ولكن بعد ذلك سوف تحدث فرقا.
وبالمناسبة، فإن حقيقة أن يهم هو السبب في أن بيثون انتقلت إلى استخدام نوعين مختلفين عن البيانات الثنائية والنص: أنه لا يمكن تحويل سحرية بينهما لأنه لا يعرف ترميز إلا إذا كنت أقول ذلك ! الطريقة الوحيدة التي من شأنه أن نعرف أن يقرأ وثائق Windows (أو قراءتها هنا).
وتعيين universal_newlines إلى True، أي بمعنى.
command_stdout = Popen(['ls', '-l'], stdout=PIPE, universal_newlines=True).communicate()[0]
وعلى الرغم الجواب Aaron Maenpaa في تعمل فقط، المستخدم <لأ href = "https://stackoverflow.com/ أسئلة / 33688837 / urllib مقابل بيثون 3/33688948 # comment55151210_33688948 "> طلب مؤخرا :
<اقتباس فقرة>هل هناك أي طريقة أكثر بساطة؟ "fhand.read (). فك (" ASCII ")" [...] انها طويلة جدا!
اقتباس فقرة>ويمكنك استخدام:
command_stdout.decode()
وdecode()
لديه مستوى حجة :
وcodecs.decode(obj, encoding='utf-8', errors='strict')
لتفسير تسلسل بايت كما نص، عليك أن تعرف ترميز الحرف المطابق:
unicode_text = bytestring.decode(character_encoding)
مثال:
>>> b'\xc2\xb5'.decode('utf-8')
'µ'
الأمر ls
قد تنتج الإخراج الذي لا يمكن أن تفسر على أنها نص. أسماء الملفات
على يونيكس قد يكون أي تسلسل بايت إلا b'/'
القطع والصفر
b'\0'
:
>>> open(bytes(range(0x100)).translate(None, b'\0/'), 'w').close()
ومحاولة فك شفرة هذا الحساء بايت باستخدام ترميز UTF-8 يثير UnicodeDecodeError
.
ويمكن أن يكون أسوأ من ذلك. قد تفشل فك بصمت وتنتج موجيباكي إذا كنت تستخدم الترميز يتعارض الخطأ:
>>> '—'.encode('utf-8').decode('cp1252')
'—'
وتلف البيانات ولكن يبقى البرنامج غير مدركين أن الفشل حدث.
في عام، ما ترميز الأحرف لاستخدام ليست جزءا لا يتجزأ من تسلسل بايت نفسها. لديك لإيصال هذه المعلومات خارج النطاق. هي بعض نتائج أكثر عرضة من غيرهم وحدة بالتالي chardet
موجودة يمكن أن <م> تخمين م> ترميز الأحرف. وهناك سيناريو بيثون واحد قد تستخدم المحارف متعددة في أماكن مختلفة.
والانتاج ls
يمكن تحويلها إلى os.fsdecode()
سلسلة بيثون باستخدام
الوظيفة التي نجحت حتى ل undecodable
أسماء (وأنه يستخدم
sys.getfilesystemencoding()
والخطأ surrogateescape
معالج على
يونكس):
import os
import subprocess
output = os.fsdecode(subprocess.check_output('ls'))
لتحصل على بايت الأصلي، هل يمكن استخدام os.fsencode()
.
إذا قمت بتمرير معلمة universal_newlines=True
ثم الاستخدامات subprocess
locale.getpreferredencoding(False)
لفك بايت على سبيل المثال، يمكن أن يكون
cp1252
على ويندوز.
لفك تيار بايت على ذبابة،
io.TextIOWrapper()
يمكن استخدامها: سبيل المثال
والأوامر المختلفة قد تستخدم المحارف المختلفة الخاصة بهم
الناتج مثلا، dir
أمر داخلي (cmd
) قد تستخدم cp437. لفك لها
الإخراج، هل يمكن تمرير ترميز بشكل واضح (بايثون 3.6 +):
output = subprocess.check_output('dir', shell=True, encoding='cp437')
وقد تختلف أسماء من os.listdir()
(الذي يستخدم ويندوز
يونيكود API) على سبيل المثال، يمكن أن تكون بديلا '\xb6'
مع '\x14'
-بايثون
cp437 خرائط الترميز b'\x14'
للسيطرة على الطابع U + 0014 بدلا من
U + 00B6 (¶). لدعم أسماء الملفات مع أحرف Unicode التعسفية، انظر فك poweshell الانتاج ربما تحتوي على أحرف يونيكود غير أسكي-إلى سلسلة الثعبان
ومنذ هذا السؤال يسأل فعلا عن انتاج subprocess
، لديك نهج أكثر مباشرة متاحة منذ Popen
يقبل <وأ href = "https://docs.python.org/3/library/subprocess.html#frequently-used -arguments "يختلط =" noreferrer "> <م> ترميز م> الكلمة (في بايثون 3.6 +):
>>> from subprocess import Popen, PIPE
>>> text = Popen(['ls', '-l'], stdout=PIPE, encoding='utf-8').communicate()[0]
>>> type(text)
str
>>> print(text)
total 0
-rw-r--r-- 1 wim badger 0 May 31 12:45 some_file.txt
والجواب العامة للمستخدمين الآخرين هو فك م> بايت إلى النص:
>>> b'abcde'.decode()
'abcde'
ومع عدم وجود حجة، sys.getdefaultencoding()
سيتم استخدامها. إذا لم يتم sys.getdefaultencoding()
البيانات الخاصة بك، ثم يجب عليك تحديد الترميز صراحة في decode
دعوة :
>>> b'caf\xe9'.decode('cp1250')
'café'
إذا يجب عليك الحصول على ما يلي من خلال محاولة decode()
:
وAttributeError: "شارع" كائن ليس له السمة 'فك'
اقتباس فقرة>ويمكنك أيضا تحديد نوع الترميز مباشرة في الجبس:
>>> my_byte_str
b'Hello World'
>>> str(my_byte_str, 'utf-8')
'Hello World'
عند العمل مع البيانات من أنظمة ويندوز (مع نهايات خط \r\n
)، جوابي هو
String = Bytes.decode("utf-8").replace("\r\n", "\n")
لماذا؟ حاول هذا مع Input.txt متعدد الأسطر:
Bytes = open("Input.txt", "rb").read()
String = Bytes.decode("utf-8")
open("Output.txt", "w").write(String)
وسوف تضاعف كافة النهايات خطك (ل\r\r\n
)، مما يؤدي إلى خطوط فارغة إضافية. عادة تطبيع ظائف قراءة النص بايثون نهايات الخط بحيث سلاسل استخدام \n
فقط. إذا كنت تتلقى البيانات الثنائية من نظام ويندوز، بيثون لا تكون هناك فرصة للقيام بذلك. وهكذا،
Bytes = open("Input.txt", "rb").read()
String = Bytes.decode("utf-8").replace("\r\n", "\n")
open("Output.txt", "w").write(String)
وسيتم نسخ الملف الأصلي.
ولقد تقدمت وظيفة لتنظيف قائمة
def cleanLists(self, lista):
lista = [x.strip() for x in lista]
lista = [x.replace('\n', '') for x in lista]
lista = [x.replace('\b', '') for x in lista]
lista = [x.encode('utf8') for x in lista]
lista = [x.decode('utf8') for x in lista]
return lista
لبيثون 3، وهذا هو أكثر أمانا و<م> Pythonic م> نهج لتحويل من byte
إلى string
:
def byte_to_str(bytes_or_str):
if isinstance(bytes_or_str, bytes): #check if its in bytes
print(bytes_or_str.decode('utf-8'))
else:
print("Object not of byte type")
byte_to_str(b'total 0\n-rw-rw-r-- 1 thomas thomas 0 Mar 3 07:03 file1\n-rw-rw-r-- 1 thomas thomas 0 Mar 3 07:03 file2\n')
وإخراج:
total 0
-rw-rw-r-- 1 thomas thomas 0 Mar 3 07:03 file1
-rw-rw-r-- 1 thomas thomas 0 Mar 3 07:03 file2
def toString(string):
try:
return v.decode("utf-8")
except ValueError:
return string
b = b'97.080.500'
s = '97.080.500'
print(toString(b))
print(toString(s))
إذا كنت تريد تحويل أي بايت، وليس مجرد سلسلة تحويلها إلى بايت:
with open("bytesfile", "rb") as infile:
str = base64.b85encode(imageFile.read())
with open("bytesfile", "rb") as infile:
str2 = json.dumps(list(infile.read()))
وهذه ليست فعالة جدا، ولكن. وسوف تتحول صورة 2 ميغابايت في 9 ميغابايت.
http://docs.python.org/3/library/sys. أتش تي أم أل و
لكتابة أو قراءة البيانات الثنائية من / إلى تيارات القياسية، واستخدام المخزن المؤقت ثنائي الأساسي. على سبيل المثال، لكتابة بايت إلى المعياري، استخدم sys.stdout.buffer.write(b'abc')
.