MacRoman, CP1252, Latin1, UTF-8 및 ASCII 간의 인코딩을 안정적으로 추측하는 방법
직장에서 인코딩 관련 연결, 재난 또는 재앙 없이는 일주일이 지나지 않는 것처럼 보입니다. 문제는 일반적으로 인코딩을 지정하지 않고도 "텍스트"파일을 안정적으로 처리 할 수 있다고 생각하는 프로그래머에게서 발생합니다. 그러나 당신은 할 수 없습니다.
따라서 이후에는 파일 이름이 *.txt
또는로 끝나는 것을 금지하기로 결정되었습니다 *.text
. 이러한 확장은 평범한 프로그래머를 인코딩과 관련하여 지루한 안주로 오도하여 부적절한 처리로 이어진다는 생각입니다. 연장이 전혀없는 것이 거의 낫습니다. 적어도 당신이 가지고있는 것을 모른다는 것을 알고 있기 때문 입니다.
그러나 우리는 그렇게 멀리 갈 수 없습니다. 대신 인코딩으로 끝나는 파일 이름을 사용해야합니다. 텍스트 파일 그래서, 예를 들어, 다음은 같은 것 README.ascii
, README.latin1
, README.utf8
, 등
특정 확장자가 필요한 파일의 경우 Perl 또는 Python과 같이 파일 자체 내부에 인코딩을 지정할 수 있다면 그렇게해야합니다. 파일 내부에 이러한 기능이없는 Java 소스와 같은 파일의 경우 확장자 앞에 인코딩을 넣습니다 (예 : SomeClass-utf8.java
.
출력의 경우 UTF-8이 강력하게 선호됩니다.
그러나 입력을 위해 코드베이스에있는 수천 개의 파일을 처리하는 방법을 알아 내야합니다 *.txt
. 우리는 새로운 표준에 맞게 모든 이름을 변경하려고합니다. 그러나 우리는 그들 모두를 주시 할 수는 없습니다. 그래서 실제로 작동하는 라이브러리 나 프로그램이 필요합니다.
이들은 ASCII, ISO-8859-1, UTF-8, Microsoft CP1252 또는 Apple MacRoman으로 다양합니다. 우리는 어떤 것이 ASCII인지 알 수 있고 어떤 것이 아마도 UTF-8인지 알 수 있다는 것을 알고 있지만 우리는 8 비트 인코딩에 대해 난처합니다. 대부분의 데스크톱이 Mac 인 혼합 Unix 환경 (Solaris, Linux, Darwin)에서 실행 중이기 때문에 성가신 MacRoman 파일이 꽤 많습니다. 그리고 이것들은 특히 문제입니다.
한동안 저는 프로그래밍 방식으로 다음 중 어떤 것을 결정할 수있는 방법을 찾고있었습니다.
- ASCII
- ISO-8859-1
- CP1252
- MacRoman
- UTF-8
파일이 있고 세 가지 8 비트 인코딩을 확실하게 구분할 수있는 프로그램이나 라이브러리를 찾지 못했습니다. 우리는 아마도 천 개가 넘는 MacRoman 파일을 가지고있을 것입니다. 그래서 우리가 사용하는 문자셋 탐지기가 무엇이든간에 그것들을 알아낼 수 있어야합니다. 내가 본 어떤 것도 트릭을 관리 할 수 없습니다. ICU charset detector library에 대한 큰 희망이 있었지만 MacRoman을 처리 할 수 없습니다. 또한 Perl과 Python 모두에서 동일한 종류의 작업을 수행하는 모듈을 살펴 보았습니다.하지만 계속해서 동일한 이야기입니다. MacRoman 감지를 지원하지 않습니다.
따라서 내가 찾고있는 것은 파일이 포함 된 5 가지 인코딩 중 어떤 인코딩이 포함되어 있는지, 그리고 바람직하게는 그보다 더 많은 인코딩을 안정적으로 결정하는 기존 라이브러리 또는 프로그램입니다. 특히 내가 인용 한 3 비트 인코딩, 특히 MacRoman 을 구별해야합니다 . 파일은 99 % 이상의 영어 텍스트입니다. 다른 언어로는 적지 만 많지는 않습니다.
라이브러리 코드 인 경우 언어 기본 설정은 Perl, C, Java 또는 Python이고 그 순서입니다. 그것이 단지 프로그램이라면, 우리는 그것이 완전한 소스로 나오고, 유닉스에서 실행되고, 완전히 방해받지 않는 한 그것이 어떤 언어인지는 정말로 신경 쓰지 않습니다.
무작위로 인코딩 된 수많은 레거시 텍스트 파일의 문제가있는 사람이 있습니까? 그렇다면 어떻게 해결하려고했으며 얼마나 성공적 이었습니까? 이것이 내 질문의 가장 중요한 측면이지만, 프로그래머가 파일이있는 실제 인코딩으로 파일의 이름을 지정 (또는 이름 변경)하도록 권장하는 것이 향후 문제를 피하는 데 도움이 될지 여부에 대해서도 관심이 있습니다. 누구도 제도적으로이를 시행했는데, 그렇다면이었다했습니다 그 이유는 성공 여부, 그리고?
그리고 예, 저는 문제의 본질을 고려할 때 명확한 답변을 보장 할 수없는 이유를 완전히 이해합니다. 데이터가 충분하지 않은 작은 파일의 경우 특히 그렇습니다. 다행히도 파일은 거의 작지 않습니다. 임의 README
파일을 제외하고 대부분은 50k에서 250k 사이의 크기 범위에 있으며 대부분은 더 큽니다. 몇 K 이상의 크기는 영어로 보장됩니다.
문제 도메인은 생물 의학 텍스트 마이닝이므로 PubMedCentral의 모든 Open Access 저장소와 같이 광범위하고 매우 큰 말뭉치를 처리하는 경우가 있습니다. 다소 큰 파일은 5.7GB의 BioThesaurus 6.0입니다. 이 파일은 거의 모든 UTF-8 이기 때문에 특히 성가시다 . 그러나 일부 numbskull은 8 비트 인코딩 인 Microsoft CP1252로 된 몇 줄을 삽입했습니다. 당신이 그것을 여행하기까지 꽤 시간이 걸립니다. :(
첫째, 쉬운 경우 :
ASCII
데이터에 0x7F보다 큰 바이트가 없으면 ASCII입니다. (또는 7 비트 ISO646 인코딩이지만 매우 구식입니다.)
UTF-8
데이터의 유효성을 검사가 UTF-8로, 당신은 안전하게 가정 할 경우가 있다 UTF-8. UTF-8의 엄격한 유효성 검사 규칙으로 인해 오탐은 극히 드뭅니다.
ISO-8859-1 대 windows-1252
이 두 인코딩의 유일한 차이점은 ISO-8859-1에는 C1 제어 문자가 있고 windows-1252에는 인쇄 가능한 문자 € ‚ƒ „… † ‡ ˆ ‰ Š‹ŒŽ ''“”• –—˜ ™ š›가 있습니다. œžŸ. 곱슬 따옴표 또는 대시를 사용하는 파일을 많이 보았지만 C1 제어 문자를 사용하는 파일은 없습니다. 따라서 그들 또는 ISO-8859-1에 신경 쓰지 말고 대신 windows-1252를 감지하십시오.
이제 한 가지 질문 만 남았습니다.
MacRoman과 cp1252를 어떻게 구별합니까?
이것은 훨씬 더 까다 롭습니다.
정의되지 않은 문자
0x81, 0x8D, 0x8F, 0x90, 0x9D 바이트는 windows-1252에서 사용되지 않습니다. 발생하면 데이터가 MacRoman이라고 가정합니다.
동일한 문자
바이트 0xA2 (¢), 0xA3 (£), 0xA9 (©), 0xB1 (±), 0xB5 (µ)는 두 인코딩 모두에서 동일합니다. 이것이 유일한 비 ASCII 바이트라면 MacRoman을 선택하든 cp1252를 선택하든 상관 없습니다.
Statistical approach
Count character (NOT byte!) frequencies in the data you know to be UTF-8. Determine the most frequent characters. Then use this data to determine whether the cp1252 or MacRoman characters are more common.
For example, in a search I just performed on 100 random English Wikipedia articles, the most common non-ASCII characters are ·•–é°®’èö—
. Based on this fact,
- The bytes 0x92, 0x95, 0x96, 0x97, 0xAE, 0xB0, 0xB7, 0xE8, 0xE9, or 0xF6 suggest windows-1252.
- The bytes 0x8E, 0x8F, 0x9A, 0xA1, 0xA5, 0xA8, 0xD0, 0xD1, 0xD5, or 0xE1 suggest MacRoman.
Count up the cp1252-suggesting bytes and the MacRoman-suggesting bytes, and go with whichever is greatest.
Mozilla nsUniversalDetector (Perl bindings: Encode::Detect/Encode::Detect::Detector) is millionfold proven.
My attempt at such a heuristic (assuming that you've ruled out ASCII and UTF-8):
- If 0x7f to 0x9f don't appear at all, it's probably ISO-8859-1, because those are very rarely used control codes.
- If 0x91 through 0x94 appear at lot, it's probably Windows-1252, because those are the "smart quotes", by far the most likely characters in that range to be used in English text. To be more certain, you could look for pairs.
- Otherwise, it's MacRoman, especially if you see a lot of 0xd2 through 0xd5 (that's where the typographic quotes are in MacRoman).
Side note:
For files like Java source where no such facility exists internal to the file, you will put the encoding before the extension, such as SomeClass-utf8.java
Do not do this!!
The Java compiler expects file names to match class names, so renaming the files will render the source code uncompilable. The correct thing would be to guess the encoding, then use the native2ascii
tool to convert all non-ASCII characters to Unicode escape sequences.
"Perl, C, Java, or Python, and in that order": interesting attitude :-)
"we stand a good change of knowing if something is probably UTF-8": Actually the chance that a file containing meaningful text encoded in some other charset that uses high-bit-set bytes will decode successfully as UTF-8 is vanishingly small.
UTF-8 strategies (in least preferred language):
# 100% Unicode-standard-compliant UTF-8
def utf8_strict(text):
try:
text.decode('utf8')
return True
except UnicodeDecodeError:
return False
# looking for almost all UTF-8 with some junk
def utf8_replace(text):
utext = text.decode('utf8', 'replace')
dodgy_count = utext.count(u'\uFFFD')
return dodgy_count, utext
# further action depends on how large dodgy_count / float(len(utext)) is
# checking for UTF-8 structure but non-compliant
# e.g. encoded surrogates, not minimal length, more than 4 bytes:
# Can be done with a regex, if you need it
Once you've decided that it's neither ASCII nor UTF-8:
The Mozilla-origin charset detectors that I'm aware of don't support MacRoman and in any case don't do a good job on 8-bit charsets especially with English because AFAICT they depend on checking whether the decoding makes sense in the given language, ignoring the punctuation characters, and based on a wide selection of documents in that language.
As others have remarked, you really only have the high-bit-set punctuation characters available to distinguish between cp1252 and macroman. I'd suggest training a Mozilla-type model on your own documents, not Shakespeare or Hansard or the KJV Bible, and taking all 256 bytes into account. I presume that your files have no markup (HTML, XML, etc) in them -- that would distort the probabilities something shocking.
You've mentioned files that are mostly UTF-8 but fail to decode. You should also be very suspicious of:
(1) files that are allegedly encoded in ISO-8859-1 but contain "control characters" in the range 0x80 to 0x9F inclusive ... this is so prevalent that the draft HTML5 standard says to decode ALL HTML streams declared as ISO-8859-1 using cp1252.
(2) files that decode OK as UTF-8 but the resultant Unicode contains "control characters" in the range U+0080 to U+009F inclusive ... this can result from transcoding cp1252 / cp850 (seen it happen!) / etc files from "ISO-8859-1" to UTF-8.
Background: I have a wet-Sunday-afternoon project to create a Python-based charset detector that's file-oriented (instead of web-oriented) and works well with 8-bit character sets including legacy ** n
ones like cp850 and cp437. It's nowhere near prime time yet. I'm interested in training files; are your ISO-8859-1 / cp1252 / MacRoman files as equally "unencumbered" as you expect anyone's code solution to be?
As you have discovered, there is no perfect way to solve this problem, because without the implicit knowledge about which encoding a file uses, all 8-bit encodings are exactly the same: A collection of bytes. All bytes are valid for all 8-bit encodings.
The best you can hope for, is some sort of algorithm that analyzes the bytes, and based on probabilities of a certain byte being used in a certain language with a certain encoding will guess at what encoding the files uses. But that has to know which language the file uses, and becomes completely useless when you have files with mixed encodings.
On the upside, if you know that the text in a file is written in English, then the you're unlikely to notice any difference whichever encoding you decide to use for that file, as the differences between all the mentioned encodings are all localized in the parts of the encodings that specify characters not normally used in the English language. You might have some troubles where the text uses special formatting, or special versions of punctuation (CP1252 has several versions of the quote characters for instance), but for the gist of the text there will probably be no problems.
If you can detect every encoding EXCEPT for macroman, than it would be logical to assume that the ones that can't be deciphered are in macroman. In other words, just make a list of files that couldn't be processed and handle those as if they were macroman.
Another way to sort these files would be to make a server based program that allows users to decide which encoding isn't garbled. Of course, it would be within the company, but with 100 employees doing a few each day, you'll have thousands of files done in no time.
Finally, wouldn't it be better to just convert all existing files to a single format, and require that new files be in that format.
Has anyone else had this problem of a zillion legacy text files randomly encoded? If so, how did you attempt to solve it, and how successful were you?
I am currently writing a program that translates files into XML. It has to autodetect the type of each file, which is a superset of the problem of determining the encoding of a text file. For determining the encoding I am using a Bayesian approach. That is, my classification code computes a probability (likelihood) that a text file has a particular encoding for all the encodings it understands. The program then selects the most probable decoder. The Bayesian approach works like this for each encoding.
- Set the initial (prior) probability that the file is in the encoding, based on the frequencies of each encoding.
- Examine each byte in turn in the file. Look-up the byte value to determine the correlation between that byte value being present and a file actually being in that encoding. Use that correlation to compute a new (posterior) probability that the file is in the encoding. If you have more bytes to examine, use the posterior probability of that byte as the prior probability when you examine the next byte.
- When you get to the end of the file (I actually look at only the first 1024 bytes), the proability you have is the probability that the file is in the encoding.
It transpires that Bayes' theorem becomes very easy to do if instead of computing probabilities, you compute information content, which is the logarithm of the odds: info = log(p / (1.0 - p))
.
You will have to compute the initail priori probability, and the correlations, by examining a corpus of files that you have manually classified.
'programing' 카테고리의 다른 글
Zend Framework 2에서 경로, 게시, 가져 오기 등에 액세스하는 방법 (0) | 2020.08.23 |
---|---|
오픈 소스 프로젝트에 대한 코드 서명 인증서? (0) | 2020.08.22 |
Ruby on Rails 대 ASP.NET MVC 3 for .NET Guy? (0) | 2020.08.22 |
20 개의 질문 AI 알고리즘은 어떻게 작동합니까? (0) | 2020.08.22 |
SQL Server-클러스터형 인덱스와 비 클러스터형 인덱스는 언제 사용합니까? (0) | 2020.08.22 |