Python 熊猫:如何从CSV读取字节和非字节列并解码字节列?
我有一个.csv文件,有4列:2个整数列、1个字节数组列和一个日期列。这个字节数组列有一个二进制文本,我需要将其解码为一个普通的utf-8字符串 下面是my.csv的外观:Python 熊猫:如何从CSV读取字节和非字节列并解码字节列?,python,python-3.x,pandas,csv,binary-data,Python,Python 3.x,Pandas,Csv,Binary Data,我有一个.csv文件,有4列:2个整数列、1个字节数组列和一个日期列。这个字节数组列有一个二进制文本,我需要将其解码为一个普通的utf-8字符串 下面是my.csv的外观: id1 id2 text date 1 2 0x202020312045584D4F2841292E205 2020-01-01 3 4 0x20312020455843454C454E
id1 id2 text date
1 2 0x202020312045584D4F2841292E205 2020-01-01
3 4 0x20312020455843454C454E 2020-05-01
当我简单地使用pd.read_csv()时:
输出:
id1 id2 text date
24228 35649098 0x202020312045584D4F2841292E2 2020-05-04
24298 97780137 0x20312020455843454C454E54C38 2020-05-04
df.info():
但是,我需要普通的字符串,所以我尝试只解码这个列,但我无法使它工作。以下是我已经尝试过的:
试验1:
df.loc[:,'transformedText'] = df.text.str.decode('utf-8')
输出:
transformedText列全部为NaN
试验2:
df.loc[:,'transformedText'] = df.text.str.encode('utf-8').str.decode('utf-8')
输出:
transformedText列保留字节数组字符串
试验3:
df.loc[:,'transformedText'] = df.text.str.encode('ascii').str.decode('utf-8')
输出:
transformedText列保留字节数组字符串
为了进一步调查这个问题,我检查了刚编码字符串时发生的情况:
df.loc[:,'transformedText']=df.text.str.encode('ascii')
输出:
它所做的只是在我的字符串上添加一个b'(例如b'0x20202031204584D4F2841292E2')
我相信解码不起作用的原因是因为read_csv没有将我的列识别为字节数组列,而是将我的列识别为字符串列。尽管如此,我对此并不确定
我需要的输出是:
id1 id2 text date
24228 35649098 A normal string that a human can read 1 2020-05-04
24298 97780137 A normal string that a human can read 2 2020-05-04
另外,我对二进制文件有点陌生,所以任何东西都有帮助
我已经查看了下面的链接,但找不到答案:确保您实际拥有正确的十六进制字符串(您的一些示例中有奇数个十六进制数字,这将导致下面的代码失败) 然后:
CSV中的数据与
pandas
读取的数据之间似乎存在一些不一致。这是您从文件中共享的第一行:
1 2 0x202020312045584D4F2841292E205 2020-01-01
请注意,字符串被编码为十六进制(以0x
开头),因此,它需要偶数位数才能正确解码。上面的示例有29位数字(不包括0x
),这意味着无法正确解码
然而,我注意到在加载代码时可能会出现问题(或者只是输入错误)。两行二进制字符串在读取之前和之后都以相同的数字开头,但它们的最终数字不同。见第一条:
0x202020312045584D4F2841292E205 # Before
0x202020312045584D4F2841292E2 # After
第二个:
0x20312020455843454C454E # Before
0x20312020455843454C454E54C38 # After
此外,在加载到pandas之前和之后,日期和id
列也不同。检查pandas是否正确加载了您的数据会很有趣
无论如何,如果您有十六进制数据字符串,您可以通过执行以下操作对其进行解码:
df['Decoded'] = df['text'].str[2:].apply(lambda s: bytes.fromhex(s).decode('utf-8')
如果将此列改为数字,则可以执行以下操作:
df['Decoded'] = df['text'].apply(lambda s: bytes.fromhex(hex(s)[2:]).decode('utf-8')
它已成功解码文本中的以下二进制字符串:
0x202020312045584D4F2841292E20 # First row without the last digit
0x20312020455843454C454E # Second row
返回:
0 1 EXMO(A).
1 1 EXCELEN
这两个词在葡萄牙语中都是“几乎”的词:EXMO(A)
是Excelentíssimo(A)的缩写,而EXCELEN
是一个不完整的词,可以是Excelência、Excelente或类似的词(我也是巴西人,所以很高兴看到一些非英语单词被解码)
正如你所看到的,你的数据有一些问题,但我们可以通过某种方式解码其中的一部分。如果您有任何进一步的问题,请告诉我们您是否成功解码了您的文本。此答案的目的是(希望)填补潜在的理解空白,即为什么您的尝试和明显的实际努力没有导致解决方案 目前,@PierreD和@Ralubrusto的优秀答案很好地解释了这个解决方案
在丛林中寻找鲨鱼: 这个问题表明你正在研究如何解码“二进制”字符串。问题是,两个字符串都不是二进制字符串,而是十六进制字符串。这就是标题——不幸的是,你找错了地方 有什么区别?十六进制字符串是表示二进制数据的一种方法
- 二进制:由两个符号组成的数字系统,
和0
1
- 十六进制:由16个符号(十六进制(6)+十进制(10))、
和0-9
组成的数字系统。可以“计数”为A-F
0、1、…、8、9、A、B、…、E、F
01001001
)。而等效的8位十六进制代码(49
)是两个字符。您提供的十六进制字符串之一是28个字符。如果将其转换为二进制,它将是112个字符!因此,十六进制系统是一种“缩短”二进制字符串的方法,以便于表示
提供两种数据类型的简单比较
十六进制到ASCII: 本示例的目的是说明为什么
.encode()
和.decode()
函数只执行部分作业
使用以下步骤,手动将十六进制字符串转换为ASCII字符串很容易:
- 将十六进制转换为二进制(编码函数在此停止)
- 将二进制转换为十进制
- 通过
0x49 (hex) --> 0100 1001 (binary) --> 73 (decimal) --> I (ascii)
# .encode() stops here ^^^
总结: 二进制字符串
b'mystring'
或b'49'
和十六进制字符串'0x49'
是不同的东西;其中十六进制字符串可以是二进制值的表示形式
我希望这有助于解释为什么你的尝试不幸没有成功——尽管付出了巨大的努力。Hey@Ralubrusto!谢谢你的回答!这种不一致性实际上是我的错,文本比这大得多,是一个角色文档转换为这种类型的编码。所以为了简化,我只复制了文本的开头,否则它将是一个巨大的字符串,我认为这会使我对问题的解释复杂化。我不知道,回到过去,这将是一个问题:/
df['Decoded'] = df['text'].apply(lambda s: bytes.fromhex(hex(s)[2:]).decode('utf-8')
0x202020312045584D4F2841292E20 # First row without the last digit
0x20312020455843454C454E # Second row
0 1 EXMO(A).
1 1 EXCELEN
0x49 (hex) --> 0100 1001 (binary) --> 73 (decimal) --> I (ascii)
# .encode() stops here ^^^