[ptyhon] 부득이한 경우utf-8의 문자 집합으로 구성된 일본어를 sjis 인코딩할 때의 작업 목표
제품 코드를 바탕으로commiit를 했지만 실제로 투입하고 싶지 않아요...
배경.
현재 나는 계산서와 관련된 시스템을 구축하고 있다.업무에 따라 다행수를 요구하는 유형이 있어 csv 양식을 첨부하여 고객에게 제공해야 한다.
상세한 원본 데이터 정보가 본사의 CRM에 들어왔다.CRM의 문자 코드는utf-8입니다. 물론 (utf-8로 표시할 수 있음) 임의의 일본어 문자를 포함할 수 있습니다.예를 들면 고객의 회사명 같은 거.
다른 한편, 제공하고자 하는 csv는 윈도/Mac의 모든 환경에서 열릴 가능성이 있기 때문에 대부분의 사람들이 Excel을 관찰자로 사용할 것으로 예상된다.
이 때 csv 형식의 인코딩은 Windows/Mac의 Excel로 각각 sjis입니다.따라서 csv 형식의 인코딩은 sjis로 진행되는 것은 나무랄 데가 없다.
OS와 Office 버전을 엄격하게 지정해 정확한 검증은 아니지만 참고하기 위해 다음과 같은 조합을 시도했다.(참조용)
(1) windows
utf-8... 디스카운트
sjis … OK
문자화
문자화
부호가 깨지지 않았지만 쉼표는 표시줄로 식별되지 않았다
(2) mac
utf-8 … OK
sjis … OK
문자화
문자화
부호가 깨지지 않았지만 쉼표는 표시줄로 식별되지 않았다
※ 참고로 Mac의 표 계산 응용 프로그램인 Numbers는 대체로 모든 인코딩이 되어 문제가 없음을 나타냅니다.훌륭하다.Mac 환경의 대상이라면, 먼저 Numbers에서 열어볼 수 있습니다
※ 프리의 청구 서비스'빌원'에서는 csv 출력에도 sjis가 적용된 곳이 있습니다.freee 도움말 센터 CSV 파일 디코딩 시 대응 방법
무슨 문제가 생겼어요?
CRM(utf-8)에 존재하며 sjis로 변환할 수 없는 문자가 있습니다.
예를 들어, "①"은 sjis에서 사용할 수 없습니다.상담의 관리명, 명세품 명칭에 나오는 글입니다.
㈱
또는®
도.Python의 문자열 인코딩
str.encode()
과 codecs.encode()
이라면, 파일 출력과 함께 처리 open()
한다면, encoding을 지정하고 싶습니다.상기 문자를 포함하는 문자가 입력이라면 sjis 인코딩을 지정할 때 인코딩 실패
UnicodeEncodeError
는 예외가 발생합니다.하지만 색다른 일들 때문에'그곳에서 방법을 생각해 보자'고 말하고 싶었어요.
어떻게 처리합니까
지침 자체는 간단하지만, 인코딩할 수 없는 문자가 발견되면 다른 (목표의 문자 코드로 인코딩할 수 있는) 문자로 바꿔 계속 처리하면 OK다.
이거 파이톤으로 어떻게 해야 돼요?즉
str.encode()
와 codecs.encode()
에 errors
의 매개 변수가 있어 그것을 지정할 수 있다는 것이다.아래와 같이 참고하다.
보시다시피 파라미터
errors
가 있고 기본값은strict입니다.발송
UnicodeEncodeError
은 errros='strict'
시의 행위다.이 매개 변수를 다른 매개 변수로 바꾸어 인코딩할 수 없는 문자의 처리를 사용자 정의할 수 있습니다.errors 매개 변수의 표준 옵션
취할 수 있는 값 목록은 아래 문서에 기재되어 있습니다.
기본'strict '이외에 인코딩할 수 없는 문자를 다른 문자로 바꾸어 인코딩 처리를 계속합니다.
ignore
와replace
면(인간의 관점에서 볼 때 정보의 결손이 발생한다) 혼란을 피하는 동시에 나무랄 데가 없을 것 같다.REPR로 간단히 검증할 수 있어 스니 펫을 미리 붙일 수 있기 때문이다.
# 文字 "①" は sjis エンコーディング不可能
>>> s = '取引先①, test'
>>> s.encode('sjis', errors='ignore').decode('sjis')
'取引先, test'
>>> s.encode('sjis', errors='replace').decode('sjis')
'取引先?, test'
>>> s.encode('sjis', errors='backslashreplace').decode('sjis')
'取引先\\u2460, test'
여기서 요점은'교체 규칙은 고정적'이다.예를 들어
ほげほげ案件①
와 같은 문자열을 입력한 경우.이런 문자열을 가상하는 상황에서 가능하다면'첫 번째 사건'이라는 정보량을 사람도 쉽게 읽을 수 있는 텍스트에 유지하고 싶지 않겠는가.예를 들어 그때의 나는
①
였으면 (1)
로 바꿨으면 좋겠다고 생각했다.안타깝게도 표준 erross에서 얻을 수 있는 값 중 csv 형식에 맞는 선택이 없습니다.
※ 물론 수출지의 요건이 다르면 이 제한을 받지 않습니다.필요에 따라 옵션을 확인하십시오.
그럼 어떡하지?만약 스스로 반사 정보를 가지고 그 반사에 따라 바꿀 수 있다면 나는 매우 기쁠 것이다.
errors
매개 변수에 사용할 수 있는 값도 스스로 확장할 수 있는데 그 실현 방법은 다음과 같다.codecs.register_error에서 인코딩할 때 사용자 정의 오류 처리 프로그램
다음과 같은 상황을 실현하려면 코드cs
register_error
에 사용자 정의 오류 처리 프로그램을 등록하면 요구를 달성할 수 있습니다.①
면(1)
문서는 여기 있습니다.자신의 오류 처리 프로그램을 설치하고 이름 뒤에 register를 설치하면 이 이름
errors
을 사용할 수 있습니다.codecs.encode
또는 str.encode
를 호출할 때, 그 이름을 errors 매개 변수로 지정합니다.어쨌든 같은 다른 숫자도 같은 규칙으로 변환하는 김에 변환
㈱
(株)
해 봅시다.설치 예는 여기에 있다.# main.py
_error_handler_registered = False
_str_sjis_mapping = {
'①': '(1)',
'②': '(2)',
'③': '(3)',
'④': '(4)',
'⑤': '(5)',
'⑥': '(6)',
'⑦': '(7)',
'⑧': '(8)',
'⑨': '(9)',
'㈱': '(株)',
}
def _error_handler(e: UnicodeError):
# print(e.args)
(encoding, text, i, j, msg) = e.args
return (
_str_sjis_mapping.get(text[i], ''),
j
)
def str_to_sjis(s: str) -> bytes:
"""Unicode(str) を sjis に変換する
see also: https://docs.python.org/ja/3.8/library/codecs.html#codecs.register_error
"""
global _error_handler_registered
if not _error_handler_registered:
codecs.register_error('my_custom_handler', _error_handler)
_error_handler_registered = True
return s.encode(encoding='sjis', errors='my_custom_handler')
# unitest
from unittest import TestCase
from main import str_to_sjis
class TestCustomErrorHandler(TestCase):
def test_str_to_sjis(self):
testcases = [
('取引先①', '取引先(1)'),
('取引先㈱', '取引先(株)'),
]
for case in testcases:
with self.subTest(input=case[0], expect=case[1]):
self.assertEqual(str_to_sjis(case[0]).decode('sjis'), case[1])
오류 처리 프로그램 등록 함수_error_handler
가 인코딩 오류가 발생하면 호출됩니다.여기서 자신의 행동거지를 정의함으로써 억지로 sjis로 바꿀 수 있다.이 프로세서는 유니코드 error를 매개 변수로 받아들인다.처리 프로그램 함수에 있어야 할 규격은 엄격한 문서화는 아니지만
register_error
의 문서에 문장을 바탕으로 한 설명이 있기 때문에 이 실시를 참고했습니다.For encoding, error_handler will be called with a UnicodeEncodeError instance, which contains information about the location of the error. The error handler must either raise this or a different exception, or return a tuple with a replacement for the unencodable part of the input and a position where encoding should continue. The replacement may be either str or bytes. If the replacement is bytes, the encoder will simply copy them into the output buffer. If the replacement is a string, the encoder will encode the replacement. Encoding continues on original input at the specified position. Negative position values will be treated as being relative to the end of the input string. If the resulting position is out of bound an IndexError will be raised.
대개
그런 말.
오류 처리 프로그램의 매개 변수 사용 방법을 조사하기 위해 print를 준비하면서 조사를 실시한 결과 상술한 실시에 이르렀다.텍스트 맵을 유지하는 dict가 있습니다. 변환할 수 있으면 이 맵에서 빼십시오.반환하지 않으면 Fall back이 기본값으로 설정됩니다(위의 예에서는 빈 문자).
되돌아오는 값의 형식입니다. 대상의 문자를str와byte로 바꿀 수 있습니다.이것은 매우 일반적인 곡예자입니다.str로 되돌아오면 바뀐 텍스트에 다시 인코딩을 적용합니다.따라서 대상의 문자를 잘못 바꾸면 무한 순환이 발생한다.만약 상대방의 문자 코드가 이미 결정되었다면byte표현은 역시 회답하는 것이 좋다.
총결산
어떻게든 사람의 가독성을 유지하면서utf-8->sjis를 인코딩하고 싶다면 인코딩할 때의
error
파라미터를 바꾸거나 (정말 안 된다면)codecs.register_error
로 인코딩을 만드는 사용자 정의 오류 처리 프로그램을 사용하세요.말할 것도 없이 결국 그 녀석은 빚진 냄새가 난다.불평할 때마다 지도의 정의를 지키는데 상상도 못하겠어...
무엇보다 이를 묘수로만 파악해 실제 시행하지 않고도 조정할 수 있다.
Reference
이 문제에 관하여([ptyhon] 부득이한 경우utf-8의 문자 집합으로 구성된 일본어를 sjis 인코딩할 때의 작업 목표), 우리는 이곳에서 더 많은 자료를 발견하고 링크를 클릭하여 보았다 https://zenn.dev/hassaku63/articles/f7ca587b86398c텍스트를 자유롭게 공유하거나 복사할 수 있습니다.하지만 이 문서의 URL은 참조 URL로 남겨 두십시오.
우수한 개발자 콘텐츠 발견에 전념 (Collection and Share based on the CC Protocol.)