显然,Python字符串并不是“天生平等的”

2024-05-23 15:47:24 发布

您现在位置:Python中文网/ 问答频道 /正文

我想把我的大脑围绕在“文本编码标准”上。当把一堆字节解释为“text”时,必须知道哪个“encoding sheme”适用。据我所知,可能的候选人:

  • ASCII:非常基本的编码方案,支持128个字符。在
  • CP-1252:Windows拉丁字母编码方案。也称为“ANSI”。在
  • UTF-8:Unicode表的编码方案(1.114.112个字符)。如果可能,用一个字节表示每个字符,如果需要,用更多字节表示(最多4个字节)。在
  • UTF-16:Unicode表的另一种编码方案(1.114.112个字符)。表示每个字符,最少2个字节,最多4个字节。在
  • UTF-32:Unicode表的另一种编码方案。用4个字节表示每个字符。在
  • 。在

现在我希望Python对其内置字符串类型始终使用一种编码方案。我做了下面的测试,结果让我发抖。我开始相信Python并不是一直坚持一种编码方案来在内部存储字符串。换句话说:Python字符串似乎“生来就不平等”。。在

编辑:

我忘了提到我使用的是python3.x。对不起:-)

1。测试

我在一个文件夹中有两个简单的文本文件:myAnsi.txt和{}。正如您所猜到的,第一个是用CP-1252编码方案编码的,也称为ANSI。后者用utf-8编码。在我的测试中,我打开每个文件并读出其内容。我将内容分配给一个本机Python字符串变量。然后我关闭文件。之后,我创建一个新文件并将字符串变量的内容写入该文件。下面是实现这些功能的代码:

    ##############################
    #    TEST ON THE ANSI-coded  #
    #    FILE                    #
    ##############################
    import os
    file = open(os.getcwd() + '\\myAnsi.txt', 'r')
    fileText = file.read()
    file.close()

    file = open(os.getcwd() + '\\outputAnsi.txt', 'w')
    file.write(fileText)
    file.close()

    # A print statement here like:
    #    >> print(fileText)
    # will raise an exception.
    # But if you're typing this code in a python terminal,
    # you can just write:
    #    >> fileText
    # and get the content printed. In my case, it is the exact
    # content of the file.
    # PS: I use the native windows cmd.exe as my Python terminal ;-)

    ##############################
    #    TEST ON THE Utf-coded   #
    #    FILE                    #
    ##############################
    import os
    file = open(os.getcwd() + '\\myUtf.txt', 'r')
    fileText = file.read()
    file.close()

    file = open(os.getcwd() + '\\outputUtf.txt', 'w')
    file.write(fileText)
    file.close()

    # A print statement here like:
    #    >> print(fileText)
    # will just work fine (at least for me).

    ############# END OF TEST #############

2。我期望的结果

让我们假设Python对它的所有字符串始终坚持一种内部编码方案,例如utf-8。将其他内容分配给字符串会导致某种类型的隐式转换。在这些假设下,我希望两个输出文件都是utf-8类型:

^{pr2}$

3。我得到的结果

我得到的结果是:

    outputAnsi.txt   ->   CP-1252 encoded (ANSI)
    outputUtf.txt    ->   utf-8 encoded

从这些结果中,我必须得出结论:字符串变量fileText以某种方式存储了它所遵循的编码方案。在

很多人在他们的回答中告诉我:

When no encoding is passed explicitly, open() uses the preferred system encoding both for reading and for writing.

我只是不能把我的大脑围绕着那句话。{t{t>如果这两个输出都是编码的,那么这两个输出都应该用cd9来编码?在

4。问题..

我的测试向我提出了几个问题:

(1)当我打开一个文件来读取其内容时,Python如何知道该文件的编码方案?我没有指定打开文件的时间。在

(2)显然,Python字符串可以遵循Python支持的任何编码方案。因此,并非所有Python字符串生来都是相等的。如何找出特定字符串的编码方案,以及如何转换它?或者如何确保新创建的Python字符串是预期的类型?在

(3)当我创建一个文件时,Python如何决定将以何种编码方案创建该文件?在我的测试中创建这些文件时,我没有指定编码方案。不过,Python做出了不同的(!)每种情况下的决定。在

5。额外信息(基于对该问题的评论):

  • Python版本:python3.x(从Anaconda安装)
  • 操作系统:Windows 10
  • 终端:标准Windows命令提示符cmd.exe
  • 关于临时变量fileText的一些问题。显然,指令print(fileText)不适用于ANSI情况。引发异常。但是在python终端窗口中,我可以简单地输入变量名fileText并打印出文件内容。在
  • 安可丁g文件检测:记事本右下角第一次检查,在线工具复查:https://nlp.fi.muni.cz/projects/chared/
  • 输出文件outputAnsi.txt和{}在测试开始时不存在。它们是在我使用'w'选项发出open(..)命令时创建的。在

6。实际文件(为了完整性):

我得到了一些建议,鼓励我分享我正在做这个测试的实际文件。那些文件相当大,所以我把它们删减了,重新做了测试。结果相似。以下是这些文件(注:当然,我的文件包含源代码,还有什么?)公司名称:

我的ANSI.txt

/*
******************************************************************************
**
**  File        : LinkerScript.ld
**
**  Author      : Auto-generated by Ac6 System Workbench
**
**  Abstract    : Linker script for STM32F746NGHx Device from STM32F7 series
**
**  Target      : STMicroelectronics STM32
**
**  Distribution: The file is distributed “as is,” without any warranty
**                of any kind.
**
*****************************************************************************
** @attention
**
** <h2><center>&copy; COPYRIGHT(c) 2014 Ac6</center></h2>
**
*****************************************************************************
*/

/* Entry Point */
/*ENTRY(Reset_Handler)*/
ENTRY(Default_Handler)

/* Highest address of the user mode stack */
_estack = 0x20050000;    /* end of RAM */

_Min_Heap_Size = 0;      /* required amount of heap  */
_Min_Stack_Size = 0x400; /* required amount of stack */

/* Memories definition */
MEMORY
{
  RAM (xrw)     : ORIGIN = 0x20000000, LENGTH = 320K
  ROM (rx)      : ORIGIN = 0x8000000, LENGTH = 1024K
}

fileText变量的print语句导致以下异常:

>>> print(fileText)
    Traceback (most recent call last):
      File "<stdin>", line 1, in <module>
      File "C:\Anaconda3\lib\encodings\cp850.py", line 19, in encode
        return codecs.charmap_encode(input,self.errors,encoding_map)[0]
    UnicodeEncodeError: 'charmap' codec can't encode character '\u201c' in position 357: character maps to <undefined>

但只需键入变量的名称就可以毫无问题地打印出内容:

>>> fileText
    ### contents of the file are printed out :-) ###

myUtf.txt文件

/*--------------------------------------------------------------------------------------------------------------------*/
/*           _ _ _                                                                                                    */
/*          / -,- \                   __  _            _                                                              */
/*         //  |  \\                 / __\ | ___   ___| | __                   _            _                         */
/*         |   0--,|                / /  | |/ _ \ / __| |/ /    __ ___ _ _  __| |_ __ _ _ _| |_ ___                   */
/*         \\     //               / /___| | (_) | (__|   <    / _/ _ \ ' \(_-<  _/ _` | ' \  _(_-<                   */
/*          \_-_-_/                \____/|_|\___/ \___|_|\_\   \__\___/_||_/__/\__\__,_|_||_\__/__/                   */
/*--------------------------------------------------------------------------------------------------------------------*/


#include "clock_constants.h"
#include "../CMSIS/stm32f7xx.h"
#include "stm32f7xx_hal_rcc.h"


/*--------------------------------------------------------------------------------------------------*/
/*          S y s t e m C o r e C l o c k       i n i t i a l        v a l u e                      */
/*--------------------------------------------------------------------------------------------------*/
/*                                                                                                  */
/* This variable is updated in three ways:                                                          */
/*      1) by calling CMSIS function SystemCoreClockUpdate()                                        */
/*      2) by calling HAL API function HAL_RCC_GetHCLKFreq()                                        */
/*      3) each time HAL_RCC_ClockConfig() is called to configure the system clock frequency        */
/*          Note: If you use this function to configure the system clock; then there                */
/*                is no need to call the 2 first functions listed above, since SystemCoreClock      */
/*                variable is updated automatically.                                                */
/*                                                                                                  */
uint32_t SystemCoreClock = 16000000;
const uint8_t AHBPrescTable[16] = {0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 6, 7, 8, 9};


/*--------------------------------------------------------------------------------------------------*/
/*          S y s t e m C o r e C l o c k       v a l u e      u p d a t e                          */
/*--------------------------------------------------------------------------------------------------*/
/*                                                                                                  */
/* @brief  Update SystemCoreClock variable according to Clock Register Values.                      */
/*         The SystemCoreClock variable contains the core clock (HCLK), it can                      */
/*         be used by the user application to setup the SysTick timer or configure                  */
/*         other parameters.                                                                        */
/*--------------------------------------------------------------------------------------------------*/

Tags: 文件ofthe字符串txt内容字节is
3条回答

为了完全理解答案,我们需要看一下文档。在

让我们从open()函数开始。根据Python文档

open() returns a file object, and is most commonly used with two arguments: open(filename, mode). 1

这意味着我们处理的是一个file对象,它可能意味着原始二进制文件、缓冲二进制文件或在本例中是文本文件2。但是这个文本文件对象怎么知道它在编码呢?好吧,根据文件

A file object able to read and write str objects. Often, a text file actually accesses a byte-oriented datastream and handles the text encoding automatically.3

我们有了它,它是自动控制的。因为这两种格式都属于支持的编解码器。Python知道如何在给定file对象的情况下对文件进行编码。在

当没有显式传递编码时,^{} uses the preferred system encoding同时用于读和写(不确定如何在Windows上检测到首选的编码)。在

所以,当你写下:

file = open(os.getcwd() + '\\myAnsi.txt', 'r')
file = open(os.getcwd() + '\\outputAnsi.txt', 'w')
file = open(os.getcwd() + '\\myUtf.txt', 'r')
file = open(os.getcwd() + '\\outputUtf.txt', 'w')

所有四个文件都使用相同的编码打开,无论是读还是写。在

如果要确保使用以下编码打开文件,则必须传递encoding='cp1252'或{}:

^{pr2}$

(顺便说一句,我不是Windows专家,但我认为您可以用'myAnsi.txt'代替{}。)


除此之外,您还必须考虑到某些字符以相同的方式用不同的编码表示。例如,字符串hello在ASCII、CP-1252或UTF-8中具有相同的表示形式。通常,您必须使用一些非ASCII字符才能看到一些差异:

>>> 'hello'.encode('cp1252')
b'hello'
>>> 'hello'.encode('utf-8')
b'hello'  # different encoding, same byte representation

不仅如此,一些字节字符串在两种不同的编码中都是完全有效的,即使它们可能有不同的含义,因此当你试图用错误的编码解码一个文件时,你不会得到一个错误,而是一个奇怪的字符串:

>>> b'\xe2\x82\xac'.decode('utf-8')
'€'
>>> b'\xe2\x82\xac'.decode('cp1252')
'€'  # same byte representation, different string

对于记录,Python uses UTF-8, UTF-16 or UTF-32在内部表示字符串。Python尝试使用“最短”表示,即使使用UTF-8和UTF-16时没有连续字节,因此查找总是O(1)。在


简而言之,您已经使用系统编码读取了两个文件,并使用相同的编码编写了两个文件(因此没有任何转换)。您所读文件的内容与CP-1252和UTF-8兼容。在

CP-1252基本上是一个字节对字节的编解码器;它可以解码任意字节,包括来自UTF-8编码的字节。因此,有效地,假设您在使用西方语言环境的Windows上,open提供的默认编码是cp-1252,如果您从不使用Python中的字符串,只需读写它,那么您也可以只以二进制模式读写。只有在尝试以暴露问题的方式使用字符串时,才会看到问题。在

例如,考虑以下测试文件,其中包含一个UTF-8编码字符:

with open('utf8file.txt', 'w', encoding='utf-8') as f:
    f.write('é')

该文件中的实际字节是C3 A9。在

如果您在cp-1252中读取该文件,它会很乐意这样做,因为每个字节都是合法的cp-1252字节:

^{pr2}$

但这不是字符串'é',而是这两个字节恰好在cp-1252:"é"中表示的(您可以打印它们或检查长度,假设您的控制台编码处理的是非ASCII)

但是,如果您只是写回它,而不使用它,您将永远看不到这一点;输出步骤是(默认)将"é"编码为C9 A9,这将恢复您期望的原始字节。在

您的问题是,大多数文件都是合法的cp-1252文本文件(Python可能会将未分配的字节作为等效的Unicode序号进行静默读取;我知道对于latin-1这样的未分配字节,\x8d也是如此),当它们合法时,以相同的编码进行读取和写回是不可变的。在

相关问题 更多 >