質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.48%
Python 3.x

Python 3はPythonプログラミング言語の最新バージョンであり、2008年12月3日にリリースされました。

Raspberry Pi

Raspberry Piは、ラズベリーパイ財団が開発した、名刺サイズのLinuxコンピュータです。 学校で基本的なコンピュータ科学の教育を促進することを意図しています。

Q&A

解決済

5回答

6757閲覧

USBから受けたbyte型文字を変換できない

KOKSAmati

総合スコア15

Python 3.x

Python 3はPythonプログラミング言語の最新バージョンであり、2008年12月3日にリリースされました。

Raspberry Pi

Raspberry Piは、ラズベリーパイ財団が開発した、名刺サイズのLinuxコンピュータです。 学校で基本的なコンピュータ科学の教育を促進することを意図しています。

0グッド

0クリップ

投稿2019/03/05 04:11

編集2019/03/05 04:14

前提・実現したいこと

計測器からRS-232Cで出力された信号を、RATOCのDsub-USB変換ケーブル経由でRaspberry Pi3に読み込ませています。
RATOC社のサイトには受信した信号をUTF-8に変換させる方法が載っていたのですが、計測器のヘッダが悪さをしているのか、うまく文字コードの変換ができません。

発生している問題・エラーメッセージ

Traceback (most recent call last): File "USB.py", line 7, in <module> line=line.decode('utf-8') UnicodeDecodeError: 'utf-8' codec can't decode byte 0xb7 in position 11: invalid start byte

該当のソースコード

python3

1#-*- coding: utf-8 -*- 2import serial 3ser = serial.Serial('/dev/ttyUSB0') 4line = ser.readline() #改行コード取得 5line = ser.readline() #ヘッダから改行コードまで読み込み 6ser.close() 7line=line.decode('utf-8') #文字コード変換 8print(line) 9

試したこと

line = ser.readline()

で読んだ値が(一例として)

b'S\xd4\xac+000036.6\xa0\xa0\xe7\x8d\n'

と「S\」などのヘッダを含んでいるので、replaceで余計な文字列を削除しようとしました。

#-*- coding: utf-8 -*- import serial import re ser = serial.Serial('/dev/ttyUSB0') line = ser.readline() line = ser.readline() ser.close() print(line) line=line.replace(b'S\xd4\xac+',b'') #文字列削除 print(line) line=line.decode('utf-8') print(line)

が、同様のエラーが出てしまいました。

b'S\xd4\xac+000036.6\xa0\xa0\xe7\x8d\n' #読み込んだbyteデータ b'000036.6\xa0\xa0\xe7\x8d\n' #削除後のbyteデータ Traceback (most recent call last): File "/home/pi/USB.py", line 11, in <module> line=line.decode('utf-8') UnicodeDecodeError: 'utf-8' codec can't decode byte 0xa0 in position 8: invalid start byte

また、replace対象の先頭か最後が''だとエスケープしてしまうようで、文字のリプレイスができません。

line=line.replace(b'S\xd4\',b'') #ダイアログエラー「EOL While scanning string literal」

補足情報(FW/ツールのバージョンなど)

python3.5.2、Raspberry Pi3、REX-USB60F
http://www.ratoc-e2estore.com/products/detail.php?product_id=35#subsub_title2)

よろしくお願いいたします。

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答5

0

冒頭 b'S\xd4\xac' はどういうデータなのか? という点が分かっていないように見えます。
これは長さ3の(つまり3バイトの)データを表しています。

1バイト目が 16進数で 53、10進数で 83 という値
2バイト目が 16進数で d4、10進数で 212 という値
3バイト目が 16進数で ac、10進数で 172 という値

となっている3バイトのデータです。
「「S\」などのヘッダを含んでいる」などというのは大きな勘違いです。

対話環境で以下のような操作を実行してみてください。

>>> ord(b'S') 83 >>> '{:02x}'.format(ord(b'S')) '53' >>> b'\x53' b'S' >>> len(b'S\xd4\xac') 3

問題のデータですが、2バイト目から3バイト目はかろうじてUTF-8のシーケンスとして妥当で、

>>> b'\xd4\xac'.decode('utf-8') 'Ԭ'

Ԭ という文字を表しますが、13バイト目から17バイト目のb'\xa0\xa0\xe7\x8d'はUTF-8の範囲ではありません。
https://ja.wikipedia.org/wiki/UTF-8#%E3%82%A8%E3%83%B3%E3%82%B3%E3%83%BC%E3%83%89%E4%BD%93%E7%B3%BB
を見てください。

  1. a0 で始まるシーケンスはありません
  2. e7 で始まるシーケンスは3バイト必要ですが、その3バイト目が\nつまり16進数で0a、10進数で10 というデータが来ることははありません

ということが確認できるはずです。(できないならUTF-8の意味がわかってないということです)

印象としてその計測器から来ているというデータが、UTF-8の文字列である可能性は低く、UTF-8として解釈させるプログラムを書くのはおそらくは間違いだろう、と推測します。
仕様を確認もせずにUTF-8として読めばいいだろう、と勝手に考えていないでしょうか?

投稿2019/03/05 05:57

編集2019/03/05 06:17
quickquip

総合スコア11038

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

KOKSAmati

2019/03/05 06:21

おお…!ありがとうございます、上記、確認したしました! 大きな勘違いをしていたようです。 RS-232C・USBコネクタの仕様から「UTF-8に変換すれば良い」と考えていましたが、その先にある計測器の文字コードは違うようです(考えてみれば当たり前ですね…) 計測器の仕様書を確認してみたところ、文字コードの明記がありませんでした。ここからはメーカ問い合わせになりそうですね…ご助言ありがとうございます!
quickquip

2019/03/05 06:28

どっちかというとバイナリデータ(数値データ)が混じっているのでは、という気がします(なんとなく)。 あと、エラー検出符号とか。
KOKSAmati

2019/03/05 07:43

自己解決できました! データに以下の処理を実行 line=str(line) line=line.replace('S\xd4\xac+00','') line=line.replace('\','') line=line.replace('xb','') ここからre.searchで小数点を探してその前後の数字を固定長で抜き出し・格納することで、希望するデータ「016.0」を取り出せました。ありがとうございました!
guest

0

その計測器に、RS232Cの通信フォーマットの説明はないんでしょうか。
どう見てもバイナリ通信ですが、もしそうなら、readlineで読んではだめです

通信フォーマットに則り、決められた区切りデータ(デリミタデータ)で区切って読み込み、フォーマットに則ってデータを読まないとダメですぜ

投稿2019/03/05 07:42

y_waiwai

総合スコア87774

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

KOKSAmati

2019/03/05 07:52

ありがとうございます! 取扱説明書には ■インターフェース仕様 入出力規格 EIA RS-232C 伝送形式 調歩同期式(非同期)、双方向、半二重伝送 信号形式  ボーレート ~  データビット ~  使用コード ASCII と書いてありました(無理やり動かしたあとになって、ASCIIに気付きました…) すみません、この場合、readlineで読み込むことに問題があるということでしょうか…!?
y_waiwai

2019/03/05 07:57

その実際に読み込んだデータというのはASCIIになってませんが、 なにか勘違いしてるか、根本的に間違ってませんか?
KOKSAmati

2019/03/05 08:18

根本的な間違いについては、本当に自信がありません… 上記の取扱説明書は、計測器のものなのです。 【①計測器】-【②RS232C・USB変換ケーブル】-【③Raspberry Pi】 という構成で接続しています。 ①取扱説明書によれば、使用コードはASCII ②メーカサイトによれば、「バイナリデータなのでUTF-8に変換してください」 という事だけは分かっているのですが… 根本的な間違いがあるとすれば、シリアルーUSB変換が最も分かっていない部分です…
y_waiwai

2019/03/05 09:26

シリアルーUSB変換ケーブルはシリアル信号をUSBを介してデータに変換してくれるだけのものです ボーレートやデータビット、パリティを合わせていればシリアル通信のデータが取り出せます それにしても、使用コードはASCII、というのとバイナリデータなので、というのは矛盾してます。 読み方がまずいか、どちらかが嘘ですね
KOKSAmati

2019/03/05 10:16

ありがとうございます…そうですよね…ボーレートは確認済、データビットはモード固定で、一応それらしい数値は読めているので困ってしまいました…… とりあえず希望する情報を取得することはできたので解決済にはしましたが、調査は続けようと思います。 本当にありがとうございます……
guest

0

\xa0\xa0\xe7\x8dも変換できないので、ここも同様にreplaceで消せばとりあえず動きます。

ヘッダが固定長なら、スライスで取り出すのも手です。

replace対象の先頭か最後が''だとエスケープしてしまう

そもそも誤解がありそうですが、\xd4で1byteを表します。\xは16進表記であることを表すエスケープで、実際に\xという文字が入っている訳ではありません。データとしては十六進のd4です。

投稿2019/03/05 05:52

hayataka2049

総合スコア30933

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

KOKSAmati

2019/03/05 06:00

ご助言ありがとうございます! 上記の質問では固定長で数値部分が抜き出せそうなのですが、実は b'S\xd4\xac+000\xb15\xb7.\xb2\xa0\xa0\xe7\x8d\n' という結果が出ることもありまして…(この場合、取り出したいのはstr型の「157.2」) また、バイト文字の読み方についてもご教示ありがとうございます。byte型からそのまま数字を取り出すことはできない(やはりUTF-8への変換が必要)という理解でよろしいでしょうか? 固定長のヘッダを削除しても、質問文「試したこと」の通りにエラーが出るので困ってしまっております……
hayataka2049

2019/03/05 06:06

quiquiさんの回答とかぶりますが、機器の仕様に則って取り扱わないと駄目でしょうね。まったく無意味なデータが送られてくる訳もないだろうし、ほしいものを取り出すための方法も仕様で決まると思うのですが・・・
KOKSAmati

2019/03/05 06:23

ありがとうございます!quiqui様のご回答通り、大きな勘違いをしていたようです。 RS-232C・USBコネクタの仕様から「UTF-8に変換すれば良い」と考えていましたが、その先にある計測器の仕様についてもごっちゃになっていました! 取扱説明書に仕様の明記がなかったので、ここからはメーカ問い合わせになりそうです。 ご助言ありがとうございます!
guest

0

こんにちは。
欲しいのは「000036.6」の部分だけでしょうか。
そうであれば「先頭から+まで」と「実数部分」と「\以降」を分けるといいでしょう。

なおバックスラッシュは特殊な文字なので\とエスケープする必要があります。
参考:Pythonでバックスラッシュを使ってエスケープする方法

投稿2019/03/05 05:17

firedfly

総合スコア1131

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

KOKSAmati

2019/03/05 05:47

ご回答ありがとうございます! 上記の出力値であれば「先頭から+」「数字部分」「\以降」という分け方もできるのですが、 b'S\xd4\xac+000\xb15\xb7.\xb2\xa0\xa0\xe7\x8d\n' という値になる場合もあり、単純な切り分けが難しくなっている次第です(この場合、欲しいのは「157.2」) また、エスケープシーケンスについてもご教示いただき、ありがとうございます! replace処理を以下のように書いてみたところ line=line.replace(b'S\xd4\xac+00',b'') line=line.replace(b'\',b'') line=line.replace(b'xb',b'') リプレイスができていないような結果が出てしまいました……↓ b'0\xb15\xb7.\xb2\xa0\xa0\xe7\x8d\n' お手数おかけします、なにかお心あたりあればご教示お願いいたします。
guest

0

自己解決

出力値

b'S\xd4\xac+0000\xb16.0\xa0\xa0\xe7\x8d\n'

に以下の処理を実行

line=str(line) line=line.replace('S\xd4\xac+00','') line=line.replace('\','') line=line.replace('xb','')

以下のstr型のデータに。

b'Sxd4xac+000016.0xa0xa0xe7x8dn'

ここからre.searchで小数点を探してその前後の数字を固定長で抜き出し・格納することで、希望するデータ「016.0」を取り出せました

投稿2019/03/05 07:44

KOKSAmati

総合スコア15

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.48%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問