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

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

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

Rubyはプログラミング言語のひとつで、オープンソース、オブジェクト指向のプログラミング開発に対応しています。

正規表現

正規表現とは特定の文字列によるパターンマッチングを行う際に用いられる宣言型プログラミングです。

Python

Pythonは、コードの読みやすさが特徴的なプログラミング言語の1つです。 強い型付け、動的型付けに対応しており、後方互換性がないバージョン2系とバージョン3系が使用されています。 商用製品の開発にも無料で使用でき、OSだけでなく仮想環境にも対応。Unicodeによる文字列操作をサポートしているため、日本語処理も標準で可能です。

Q&A

解決済

1回答

1624閲覧

Unicodeエスケープされた文字列を変換する

k-ura

総合スコア23

Ruby

Rubyはプログラミング言語のひとつで、オープンソース、オブジェクト指向のプログラミング開発に対応しています。

正規表現

正規表現とは特定の文字列によるパターンマッチングを行う際に用いられる宣言型プログラミングです。

Python

Pythonは、コードの読みやすさが特徴的なプログラミング言語の1つです。 強い型付け、動的型付けに対応しており、後方互換性がないバージョン2系とバージョン3系が使用されています。 商用製品の開発にも無料で使用でき、OSだけでなく仮想環境にも対応。Unicodeによる文字列操作をサポートしているため、日本語処理も標準で可能です。

0グッド

1クリップ

投稿2022/06/16 00:18

前提・実現したいこと

Pythonと正規表現を用いて、Unicodeエスケープされた文字列を変換したいのですが、
下記に示す通り、対象文字列にUnicodeエスケープを示す "\u" のプレフィックスがありません。

内容

変換前文字列 = '\X2\3010914D7BA1301151B75A92914D7BA1\X0\ (b)15.9\X2\03C600D7\X0\9.5\X2\03C6\X0\'

このうち、"\X2" と "\X0" で囲まれたUnicodeエスケープされた(と思われる)文字列を変換する。

変換後文字列 = "【配管】冷媒配管 (b)15.9φ×9.5φ"

現在は、下記のように "\X2" と "\X0" で囲まれた文字列を見つけ、4文字ずつ切り出して、
"\u" のプレフィックスを付与した上で、変換しています。

Python

1# \X2\〜\X0\で囲まれた文字列 → UNICODE変換対象 2word_X2_X0 = '3010914D7BA1301151B75A92914D7BA1' 3unicode = '\\u' + '\\u'.join(str(n) for n in re.split('(....)', word_split_unicode)[1::2]) 4word_decode = unicode.encode().decode('unicode-escape')

stack overflowで、スマートに解決されている下記のコード例を見つけたのですが、
言語がRubyで、Pythonでの実装方法が分かりませんでした。
(下記、少し質問用に修正しています)

Ruby

1ESCAPE_SEQUENCE_EXPR = /\\X2\\(.*)\\X0\\/ 2 3def decode_ifc(str) 4 str.gsub(ESCAPE_SEQUENCE_EXPR) do 5 $1.gsub(/..../) { $&.to_i(16).chr(Encoding::UTF_8) } 6 end 7end 8 9str = '\X2\3010914D7BA1301151B75A92914D7BA1\X0\ (b)15.9\X2\03C600D7\X0\9.5\X2\03C6\X0\\' 10puts "Input:", str 11puts "Output:", decode_ifc(str)
【実行結果】 Input: \X2\3010914D7BA1301151B75A92914D7BA1\X0\ (b)15.9\X2\03C600D7\X0\9.5\X2\03C6\X0\ Output: 【配管】冷媒配管 (b)15.9φ×9.5φ

特に、to_i(16).chr(Encoding::UTF_8) あたりのPythonでの対応方法が分かりません。
ご教授頂ければ、幸いです。

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

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

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

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

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

guest

回答1

0

ベストアンサー

re.subはマッチした部分に対して任意の関数を呼ぶことができます。
この関数内で、抽出した部分をbyte列に変換し、それを'utf-16be'として解釈して文字列化します。
なお、以下コードにて文字列に[:-1]しているのは、今回の問題では本質ではないので説明は省きます。
Python raw strings and trailing backslash [duplicate]に記載されています)

追記

X2にくわえ顔文字などX4にも対応させました。
参考:
How to decoding IFC using Ruby
STEP-file, ISO 10303-21
6.4.3.3 Encoding ISO 10646 characters within a string

Python

1import re 2 3def func2(x): 4 b = bytes.fromhex(x.group(1)) 5 return b.decode('utf-16be') 6 7def func4(x): 8 # 7文字毎の先頭に"0"を付加 9 # 10 # https://www.steptools.com/stds/step/IS_final_p21e3.html 11 # 6.4.3.3 Encoding ISO 10646 characters within a string 12 #----- 13 # NOTE This use of eight hexadecimal characters in the "\X4\" encoding predates the restriction of the UCS codespace to a maximum value of 10FFFF. 14 # The first two characters in each eight character group will always be digit zero. 15 #----- 16 s = x.group(1) 17 s = ''.join(['0'+s[i:i+7] for i in range(0,len(s),7)]) 18 19 b = bytes.fromhex(s) 20 return b.decode('utf-32be') 21 22def decode_ifc_str(s): 23 EXPR2 = r'\\X2\\(.*?)\\X0\\='[:-1] 24 EXPR4 = r'\\X4\\(.*?)\\X0\\='[:-1] 25 for expr, func in [(EXPR2, func2), (EXPR4, func4)]: 26 s = re.sub(expr, func, s) 27 return s 28 29lst = [ 30 r'abc=', 31 r'\X2\3010914D7BA1301151B75A92914D7BA1\X0\ (b)15.9\X2\03C600D7\X0\9.5\X2\03C6\X0\=', 32 r'\X2\03B103B203B3\X0\=', # ギリシャ文字アルファ、ベータ、ガンマ(αβγ) 33 r'\X4\001F600\X0\=', # ニヤリと笑う顔(絵文字、😀) 34 r'\X4\001F600001F638\X0\=' #ニヤリと笑う猫の顔(2つの絵文字、😀😸) 35] 36 37with open('ret.txt', 'w', encoding='utf-8') as f: 38 for src in lst: 39 dst = decode_ifc_str(src) 40 line = f'[{src}]->[{dst}]' 41 print(line) # 環境によっては顔文字が正しく表示できない 42 f.write(line+'\n')

投稿2022/06/16 01:26

編集2022/06/16 02:41
can110

総合スコア38266

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

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

k-ura

2022/06/16 02:11 編集

早々のご回答ありがとうございます! [:-1]の件は、"\"による末尾の ' をエスケープしてしまうので、ダミーの文字列 "=" をつけて、-1していると理解しました。 re.subを使ってマッチした回数分だけ、to_str 関数で変換処理して、置換するということですね。 鮮やかな回答、お見事です。 また、デコードについても decode('unicode-escape') をどうやって使うかで頭がいっぱいだったところ、 decode('utf-16be') という方法があることをご教授いただけた点も勉強になりました。 下記2つほど、質問させてください。 ①2バイトずつ変換処理を行うのは、文頭の "\X2\" にヒントがあるのでしょうか? これは推測なのですが、文頭の "\X4" なら 4バイトずつみたいな、ルールがあるのでしょうか? (今回のケースに限った独自ルールなのか、一般ルールなのかがわからなかったので) ②ビッグエンディアンかリトルエンディアンかは、どのように判断したのでしょうか? ユニコードの文字列を見て、どっちのエンディアンか分かるものなのでしょうか? お手数をおかけしますが、よろしくお願いします。
can110

2022/06/16 02:48

① このような変換についての問題を解決する場合、まずはその変換仕様を正しく知ることが大事です。 今回はIFC→STEP→ISOと辿り、仕様を見つけました。 あとはその仕様を理解することでコードを書くことができます。 ② BEかLEの2択なので実際試してみました。 ただ、参考にあげたページ中か「unicode-escape endian」で検索すると仕様が見つかると思います。
k-ura

2022/06/16 04:36

X4の対応コードならびに①②のご回答ありがとうございました。 参考にさせていただきます! ① 最初の参考サイト(How to decoding IFC using Ruby)まではたどり着いたのですが、それ以降は調べておりませんでした。 今後は、もう少し深掘りしてみたいと思います。 ② 検索してみたところ、「ユニコードエスケープ形式の場合はUTF-16のビッグエンディアン」との情報がありました。 ネットワークバイトオーダ(SPARC/Intel)の方は理解していたつもりですが、文字エンコーディングの方は勉強不足でした。 いろいろとご教授頂き、大変勉強になりました。 どうもありがとうございました。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.48%

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

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

質問する

関連した質問