将 ser.readline() 编码为 UTF-8

问题描述

我有一个 Neo 6M GPS 模块,我想从中打印坐标。它目前正在以字节形式打印 NMEA 句子,\r\n 粘在末尾。下面是一个例子:

b'$GPGGA,161812.371,4042.759,N,07400.317,W,1,12,1.0,0.0,M,*7B\r\n'

要将字符串解析为坐标,我需要去掉 \r\nb' '

为此,我正在尝试 .strip("b'rn\\")。原来你只能剥离字符串,而不是字节。 为了克服字节和条带的不兼容性,我尝试将字节解码为这样的字符串:(ser.readline().decode("utf-8")).strip("b'rn\\")

这不会运行,我收到此错误:

Traceback (most recent call last):
  File "gps2.py",line 10,in <module>
    newdata = (ser.readline().decode("utf-8")).strip("b'rn\\")
UnicodeDecodeError: 'utf-8' codec can't decode byte 0xfe in position 0: invalid start byte

下面是我的代码。有没有人能帮我解码和剥离它,或者以另一种方式摆脱 \r\nb' '

import serial
import time
import string
import pynmea2

while True:
    port = "/dev/ttyAMA0"
    ser = serial.Serial(port,baudrate=9600,timeout=0.5)
    dataout = pynmea2.NMEAStreamReader()
    newdata = (ser.readline().decode("utf-8")).strip("b'rn\\")

    if newdata[0:6] == "$GPRMC":
        newmsg = pynmea2.parse(newdata)
        lat = newmsg.latitude
        lng = newmsg.longitude
        gps = ("Latitude = " + str(lat) + " and Longitude = " +str(lng))
        print(gps)
    elif newdata[0:6] == "$GPGLL":
        print("Found GPGLL record: " + newdata)
    else:
        print(newdata)

解决方法

注意:当我的原始评论长于回复 OP 对原始问题的放大的评论时,我将其更改为答案。

您无法摆脱 b' '。它不在数据中。这是一个 Python 约定,它向您显示您的数据是字节字符串而不是常规字符串。调用 decode() 会将字节串转换为字符串。另一方面,\r\n 在数据中。它表明您的设备正在使用回车/换行对终止字符串。这两个都算作空格。开头的字符 0xfe 是字节顺序标记对 \xfe\xff 的第一部分,可以丢弃。所以你只需要ser.readline()[2:].decode("utf-8").strip()

至于您在问题中未提及的无法解释的数据,但仅在随后的评论中提及:

既没有设备也没有它的文档,我只能推测你想要的数据前缀的明显二进制数据。它当然不是我能识别的任何类型的字符数据:它不是 UTF-8,也不是有效的 UTF-16,我的预感是它也不是东亚 MBCS。它不太可能是浮点数或整数,因为没有一个零字节,而二进制数字数据(和 UTF-32)往往有很多。

但是,如果您想要的数据以 $GPGGA, 这样的已知常量开头,那么从您获得的数据流中挑选出您想要的数据应该不是很困难。例如,假设你得到

b'i\x9a\xcab\x82\xbab\x8a\xb2b\x92\xc2b\x92\xca\x9ab\x8a\xa2R\xba\xc2jR":A\x1dMY\xb1\xcd\xb1\xc9\xb1\xc5\xc1\xb1\xc5\xe1\xb1\xd1\xd9\xb1\xc5\xd5\xdd\xb1\xc9\xc1\xb1\xc9\xd5\xb1\xc9\xd5\xb1\xc5\xc5\xd9\xb1\xc5\xd1\xb1\xc9\xd9\xb1\xd9\xc5\xb1\xc9\xe5\xc9\xb1\xc5\xd1\xb1\xc9\xdd\xb1\xc1\xc9\xb1\xc9\xd1\xdd\xb1\xc1\xd9\xa9\xdd\x195)\x91\x1dA\x1dMY\xb1\xcd\xb1\xcd\xb1\xc5\xc1\xb1\xc9\xe5\xb1\xd5\xd9\xb1\xc1\xd9\xcd\xb1\xc9\xd1\xb1\xcd\xc5\xb1\xd1\xe5\xb1\xc9\xc1\xe5\xb1\xc5\xd5\xa9\xdd\xcd5)\x91\x1dA\x1d11\xb1\xd5\xc5\xc9\xd5\xb9\xe5\xe5\xc1\xc5\xe1\xb19\xb1\xc1\xc1\xc1\xc9\xd5\xb9\xd5\xe1\xd1\xc1\xcd\xb1]\xb1\xc9\xc1\xc1\xdd\xcd\xd9\xb9\xc1\xc1\xb1\x05\xb1\x05\xa9\xdd\r5)\xff\xfe\xff$GPGGA,161812.371,4042.759,N,07400.317,W,1,12,1.0,0.0,M,*7B\r\n'

(其中大部分是从您的 Pastebin 内容中复制的)并将其存储在 dataout 中。然后 dataout.partition(b'$GPGGA,')[-1].decode().strip() 会给你你期望的数字,不管 $GPGGA, 左边是否有无法解释的二进制数据。

在您看来,我仍然想知道二进制数据是什么。我认为它更有可能是由串行数据传输的复杂性而不是设备中的任何缺陷引起的。我的猜测是它是真实数据,但可能带有意外的数据位(pySerial 调用 bytesize)、停止位或奇偶校验。您对 serial.Serial() 的调用采用 8 个数据位、无奇偶校验、一个停止位的默认值。我不知道 serial 模块有多聪明,但它可能在看到一些数据后可以从错误的初始值中恢复。 25 年前,调制解调器可以通过查看(诚然,预先指定的)数据的前 2 个字节来做到这一点。

相关问答

错误1:Request method ‘DELETE‘ not supported 错误还原:...
错误1:启动docker镜像时报错:Error response from daemon:...
错误1:private field ‘xxx‘ is never assigned 按Alt...
报错如下,通过源不能下载,最后警告pip需升级版本 Requirem...