Ruby
 Computer >> コンピューター >  >> プログラミング >> Ruby

Rubyistsによる文字エンコード、Unicode、UTF-8の紹介

UndefinedConversionErrorのようなRuby例外が発生した可能性が非常に高いです。 またはIncompatibleCharacterEncodings 。例外の意味を理解している可能性は低くなります。この記事が役に立ちます。文字エンコードがどのように機能し、Rubyでどのように実装されるかを学びます。最終的には、これらのエラーをはるかに簡単に理解して修正できるようになります。

では、とにかく「文字エンコード」とは何ですか?

すべてのプログラミング言語で、文字列を操作します。それらを入力として処理する場合もあれば、出力として表示する場合もあります。しかし、あなたのコンピュータは「文字列」を理解していません。 1と0のビットのみを理解します。文字列をビットに変換するプロセスは、文字エンコードと呼ばれます。

しかし、文字エンコードはコンピュータの時代に属するだけではありません。コンピューターを使用する前のより単純なプロセスであるモールス信号から学ぶことができます。

モールス信号

モールス信号の定義は非常に単純です。信号を生成するための2つのシンボルまたは方法(短いものと長いもの)があります。これらの2つの記号を使用して、単純な英語のアルファベットを表します。例:

  • Aは.-(1つの短いマークと1つの長いマーク)
  • Eは。 (1つの短いマーク)
  • Oは---(3つの長いマーク)

このシステムは1837年頃に発明され、2つの記号または信号だけで、アルファベット全体をエンコードできるようになりました。

ここでオンラインで1人の翻訳者と遊ぶことができます。

Rubyistsによる文字エンコード、Unicode、UTF-8の紹介

この画像には、メッセージのエンコードとデコードを担当する「エンコーダー」が表示されています。これは、コンピューターの登場とともにまもなく変更されます。

手動から自動エンコードへ

メッセージをエンコードするには、モールス信号のアルゴリズムに従って文字を手動で記号に変換する人が必要です。

モールス信号と同様に、コンピューターは1と0の2つの「記号」のみを使用します。これらのシーケンスのみをコンピューターに格納できます。これらの記号を読み取るときは、ユーザーにとって意味のある方法で解釈する必要があります。

どちらの場合も、プロセスは次のように機能します。

Message -> Encoding -> Store/Send -> Decoding -> Message

モールス信号のSOSは次のようになります:

SOS -> Encode('SOS') -> ...---... -> Decode('...---...') -> SOS
-----------------------              --------------------------
       Sender                                 Receiver

コンピューターやその他のテクノロジーの大きな変化は、エンコードとデコードのプロセスが自動化されたため、情報を翻訳する人が不要になったということです。

コンピューターが発明されたとき、文字を自動的に1と0に変換するために作成された初期の標準の1つは(最初ではありませんが)ASCIIでした。

ASCIIは、情報交換のためのAmericanStandardCodeの略です。 「アメリカ人」の部分は、コンピューターがしばらくの間情報をどのように扱うかにおいて重要な役割を果たしました。理由は次のセクションで説明します。

ASCII(1963)

モールス信号や非常に初期のコンピューターなどの電信コードの知識に基づいて、コンピューターでの文字のエンコードとデコードの標準が1963年頃に作成されました。このシステムは、最初は127文字、英語のアルファベットと追加の記号しかカバーしていなかったため、比較的単純でした。

ASCIIは、各文字をバイナリコードに変換できる10進数に関連付けることによって機能しました。例を見てみましょう:

「A」はASCIIで65であるため、65をバイナリコードに変換する必要があります。

それがどのように機能するかわからない場合は、ここに簡単な方法があります :65を2で除算し始め、0になるまで続けます。除算が正確でない場合は、余りとして1を加算します:

65 / 2 = 32 + 1
32 / 2 = 16 + 0
16 / 2 = 8 + 0
8 / 2  = 4 + 0
4 / 2  = 2 + 0
2 / 2  = 1 + 0
1 / 2  = 0 + 1

ここで、残りを取り出して逆の順序で配置します。

1000001

したがって、「A」を「1000001」として、現在はUS-ASCIIとして知られている元のASCIIエンコーディングで保存します。現在、8ビットコンピュータが一般的である場合、それは01000001(8ビット=1バイト)になります。

各文字について同じプロセスに従うため、7ビットを使用すると、最大2^7文字=127を格納できます。

完全な表は次のとおりです:

Rubyistsによる文字エンコード、Unicode、UTF-8の紹介 (https://www.plcdev.com/ascii_chartから)

ASCIIの問題

フランス語のçや日本語の文字大など、別の文字を追加したい場合はどうなりますか?

はい、問題があります。

ASCIIの後、人々は独自のエンコーディングシステムを作成することによってこの問題を解決しようとしました。彼らはより多くのビットを使用しましたが、これは最終的に別の問題を引き起こしました。

主な問題は、ファイルを読み取るときに、特定のエンコーディングシステムがあるかどうかわからないということでした。間違ったエンコーディングでそれを解釈しようとすると、「���」や「Ã、ÂÂÂÂ」のようなぎこちない結果になりました。

これらのエンコーディングシステムの進化は大きく、幅広いものでした。言語に応じて、異なるシステムがありました。中国語のように文字数が多い言語では、アルファベットをエンコードするためのより複雑なシステムを開発する必要がありました。

これに長年苦労した後、新しい標準が作成されました:Unicode。この規格は、現代のコンピューターが情報をエンコードおよびデコードする方法を定義しました。

Unicode(1988)

Unicodeの目標は非常に単純です。公式サイトによると、「プラットフォーム、プログラム、言語に関係なく、すべてのキャラクターに一意の番号を提供するため」

そのため、言語の各文字には、コードポイントとも呼ばれる固有のコードが割り当てられています。現在、137,000を超える文字があります。

Unicode標準の一部として、これらの値またはコードポイントをエンコードするさまざまな方法がありますが、UTF-8が最も広範囲です。

Goプログラミング言語を作成したのと同じ人々であるRobPikeとKenThompsonもUTF-8を作成しました。それらの数字をどのようにエンコードするかが効率的で巧妙であるため、成功しました。理由を正確に見てみましょう。

UTF-8:Unicode変換形式(1993)

UTF-8は現在、Webサイトの事実上のエンコーディングです(Webサイトの94%以上がそのエンコーディングを使用しています)。これは、多くのプログラミング言語およびファイルのデフォルトのエンコーディングでもあります。では、なぜこれほど成功したのでしょうか。また、どのように機能するのでしょうか。

UTF-8は、他のエンコーディングシステムと同様に、Unicodeで定義された数値を2進数に変換して、コンピュータに保存します。

UTF-8には2つの非常に重要な側面があります。-文字は1〜4バイトかかる可能性があるため、ビットを格納するときに効率的です。-Unicodeと動的なバイト数を使用することにより、最初の127はASCIIエンコーディングと互換性があります。文字は1バイトかかります。これは、ASCIIファイルをUTF-8として開くことができることを意味します。

UTF-8がどのように機能するかを分析してみましょう。

UTF-8と1バイト

Unicodeテーブルの値に応じて、UTF-8は異なる文字数を使用します。

最初の127では、次のテンプレートを使用します:Rust1 0_______

したがって、0が常に存在し、その後にUnicodeの値を表す2進数が続きます(これもASCIIになります)。例:A =65=1000001。

Stringのunpackメソッドを使用して、Rubyでこれを確認しましょう:

'A'.unpack('B*').first

# 01000001

Bは、最上位ビットを最初に持つバイナリ形式が必要であることを意味します。このコンテキストでは、これは最も高い値のビットを意味します。アスタリスクは、ビットがなくなるまで続行するようにRubyに指示します。代わりに数値を使用した場合、その数値までのビットのみを取得します:

'A'.unpack('B4').first

# 01000

2バイトのUTF-8

Unicodeの値またはコードポイントが127を超え、2047までの文字がある場合、次のテンプレートで2バイトを使用します。

110_____ 10______

したがって、Unicodeの値には11個の空のビットがあります。例を見てみましょう:

ÀはUnicodeでは192であるため、バイナリでは11000000であり、8ビットを使用します。最初のテンプレートに収まらないため、2番目のテンプレートを使用します:

110_____ 10______

右から左にスペースを埋め始めます:

110___11 10000000

そこの空のビットはどうなりますか? 0を入力しただけなので、最終結果は1100001110000000になります。

ここでパターンが見え始めます。左から右に読み始めると、8ビットの最初のグループの最初に2つの1があります。これは、文字が2バイトかかることを意味します:

11000011 10000000
--       

繰り返しますが、これはRubyで確認できます:

'À'.unpack('B*').first

# 1100001110000000

ここでのちょっとしたヒントは、次のように出力をより適切にフォーマットできることです。

'À'.unpack('B8 B8').join(' ')

# 11000011 10000000

'À'.unpack('B8 B8')から配列を取得します 次に、要素をスペースで結合して文字列を取得します。 unpackパラメーターの8は、Rubyに2つのグループで8ビットを取得するように指示します。

3バイトのUTF-8

文字のUnicodeの値が、前のテンプレートで使用可能な11ビットに収まらない場合は、追加のバイトが必要です。

1110____  10______  10______

繰り返しになりますが、テンプレートの先頭にある3つの1は、3バイトの文字を読み取ろうとしていることを示しています。

同じプロセスがこのテンプレートに適用されます。 Unicode値をバイナリに変換し、スロットを右から左に埋め始めます。その後に空きスペースがある場合は、0で埋めます。

4バイトのUTF-8

一部の値は、前のテンプレートにあった11個の空のビットよりもさらに多くを取ります。絵文字の例を見てみましょう🙂これはUnicodeの場合、「a」や「大」のような文字としても表示されます。

Unicodeでの「🙂」の値またはコードポイントは128578です。バイナリでのその数は11111011001000010、17ビットです。これは、空のスロットが16個しかないため、3バイトのテンプレートに収まらないことを意味します。したがって、メモリに4バイトを使用する新しいテンプレートを使用する必要があります。

11110___  10______ 10______  10______

もう一度、2進数の数字を入力します:Rust1 11110___ 10_11111 10011001 10000010

そして今、残りを0で埋めます:Rust1 1111000 10011111 10011001 10000010

これがRubyでどのように見えるか見てみましょう。

これには4バイトかかることがすでにわかっているので、出力を読みやすくするために最適化できます。

'🙂'.unpack('B8 B8 B8 B8').join(' ')

# 11110000 10011111 10011001 10000010

ただし、そうでない場合は、次を使用できます:

'🙂'.unpack('B*')

「bytes」文字列メソッドを使用して、バイトを配列に抽出することもできます。

"🙂".bytes

# [240, 159, 153, 130]

次に、次のコマンドを使用して要素をバイナリにマッピングできます。

"🙂".bytes.map {|e| e.to_s 2}

# ["11110000", "10011111", "10011001", "10000010"]

文字列が必要な場合は、joinを使用できます:

"🙂".bytes.map {|e| e.to_s 2}.join(' ')

# 11110000 10011111 10011001 10000010

UTF-8にはUnicodeに必要なスペースよりも多くのスペースがあります

UTF-8のもう1つの重要な側面は、すべてのUnicode値(またはコードポイント)を含めることができることです。現在存在するものだけでなく、将来存在するものも含めることができます。

これは、4バイトのテンプレートを使用するUTF-8では、21個のスロットを埋めるためです。つまり、最大2 ^ 21(=2,097,152)の値を格納できます。これは、標準でこれまでに存在する最大量の約110万のUnicode値をはるかに上回ります。

これは、新しい文字や言語を割り当てるために将来別のエンコーディングシステムに切り替える必要がないという自信を持ってUTF-8を使用できることを意味します。

Rubyでのさまざまなエンコーディングの操作

Rubyでは、次のようにすることで、特定の文字列のエンコーディングをすぐに確認できます。

'Hello'.encoding.name

# "UTF-8"

別のエンコードシステムで文字列をエンコードすることもできます。例:

encoded_string = 'hello, how are you?'.encode("ISO-8859-1", "UTF-8")

encoded_string.encoding.name

# ISO-8859-1

変換に互換性がない場合、デフォルトでエラーが発生します。 「hello🙂」をUTF-8からASCIIに変換するとします。絵文字「🙂」はASCIIに適合しないため、適合できません。その場合、Rubyはエラーを発生させます:

"hello 🙂".encode("ASCII", "UTF-8")

# Encoding::UndefinedConversionError (U+1F642 from UTF-8 to US-ASCII)

ただし、Rubyでは、文字をエンコードできない場合に「?」に置き換えることができるという例外を設けることができます。

"hello 🙂".encode("ASCII", "UTF-8", undef: :replace)

# hello ?

新しいエンコーディングでは、特定の文字を有効な文字に置き換えるオプションもあります。

"hello 🙂".encode("ASCII", "UTF-8", fallback: {"🙂" => ":)"})

# hello :)

Rubyでのスクリプトのスクリプトのエンコーディングの検査

作業中のスクリプトファイル「.rb」ファイルのエンコーディングを確認するには、次の手順を実行します。

__ENCODING__

# This will show "#<Encoding:UTF-8>" in my case.

Ruby 2.0以降、RubyスクリプトのデフォルトのエンコーディングはUTF-8ですが、最初の行のコメントで変更できます:

# encoding: ASCII

__ENCODING__
# #<Encoding:US-ASCII>

ただし、UTF-8標準を変更する非常に正当な理由がない限り、UTF-8標準に固執することをお勧めします。

Rubyでエンコーディングを操作するためのヒント

Encoding.name_listを使用すると、Rubyでサポートされているエンコーディングの全リストを確認できます。 。これにより、大きな配列が返されます:

["ASCII-8BIT", "UTF-8", "US-ASCII", "UTF-16BE", "UTF-16LE", "UTF-32BE", "UTF-32LE", "UTF-16", "UTF-32", "UTF8-MAC"...

英語以外の文字を操作する場合のもう1つの重要な側面は、Ruby 2.4より前では、upcaseなどのメソッドがあったことです。 またはreverse 期待どおりに機能しませんでした。たとえば、Ruby 2.3では、upcaseは思ったとおりに機能しません。

# Ruby 2.3
'öıüëâñà'.upcase

# 'öıüëâñà'

回避策は、RailsのActiveSupport、または別の外部gemを使用することでしたが、Ruby 2.4以降、完全なUnicodeケースマッピングがあります:

# From Ruby 2.4 and up
'öıüëâñà'.upcase

# 'ÖIÜËÂÑÀ'
絵文字を楽しんでください

UnicodeとRubyで絵文字がどのように機能するか見てみましょう:

'🖖'.chars

# ["🖖"]

これは、「バルカン敬礼」絵文字としても知られる「中指と薬指の間にある上げられた手」です。同じ絵文字を使用しているが、デフォルトではない別の肌の色である場合、何か面白いことが起こります:

'🖖🏾'.chars

# ["🖖", "🏾"]

つまり、1つのキャラクターではなく、1つの絵文字に対して2つあります。

そこで何が起こった?

さて、Unicodeのいくつかの文字は、いくつかの文字の組み合わせとして定義されています。この場合、コンピューターがこれら2つの文字を一緒に見ると、肌の色が適用された1つだけが表示されます。

フラグで見ることができるもう1つの楽しい例があります。

'🇦🇺'.chars

# ["🇦", "🇺"]

Unicodeでは、フラグの絵文字は、🇦や🇿などの「RegionalIndicatorSymbols」と呼ばれるいくつかの抽象的なUnicode文字で内部的に表されます。これらは通常、フラグの外部では使用されません。コンピューターが2つのシンボルを一緒に見ると、その組み合わせにフラグがある場合はフラグが表示されます。

自分で確認するには、これをコピーして、テキストエディタまたはフィールドのカンマを削除してみてください。

🇦,🇺
結論

UnicodeとUTF-8がどのように機能し、それらがRubyと潜在的なエラーにどのように関連しているかについてのこのレビューが、お役に立てば幸いです。

学ぶべき最も重要な教訓は、あらゆる種類のテキストを扱うときは、関連付けられたエンコーディングシステムがあることを覚えておくことです。また、テキストを保存または変更するときは、テキストを存在させておくことが重要です。可能であれば、UTF-8などの最新のエンコーディングシステムを使用して、将来変更する必要がないようにしてください。

Rubyリリースに関する注意

この記事のすべての例でRuby2.6.5を使用しました。オンラインREPLで、またはターミナルにアクセスしてirbを実行することにより、ローカルで試すことができます。 Rubyがインストールされている場合。

Unicodeのサポートは前回のリリースで改善されたため、この記事の関連性を維持するために最新のものを使用することにしました。いずれにせよ、Ruby 2.4以降では、すべての例がここに示すように機能するはずです。


  1. Outlookで文字エンコードを変更する方法

    多くの場合、送信者がOutlook経由でメールを送信すると、メッセージは表示されませんが、判読できない文字が表示されます。 Outlookメールに奇妙な文字や正しくない文字が定期的に表示される場合は、この短いチュートリアルが問題の解決に役立ちます。キーボードで何かを入力すると、コンピューターはエンコードと呼ばれる複雑なプロセスを介してそれを認識します。次に、関連する文字を画面に表示します。つまり、これはこの文字エンコードです これは、どの値がどの文字に対応するかを決定する際のキーとして機能します。 これらの文字の視覚的表現は、グリフと呼ばれます。それの異なるセットは「フォント」を構成します。

  2. RuboCopを使用したRubyコードのリンティングと自動フォーマット

    リンティングは、プログラムおよびスタイルのエラーについてソースコードを自動チェックすることです。このチェックは、リンターと呼ばれる静的コード分析ツールによって実行されます。ただし、コードフォーマッタは、事前に構成された一連のルールに厳密に準拠するようにソースコードをフォーマットするためのツールです。リンターは通常違反を報告しますが、問題を修正するのは通常プログラマー次第ですが、コードフォーマッターはそのルールをソースコードに直接適用する傾向があるため、フォーマットの間違いを自動的に修正します。 プロジェクトでより一貫性のあるコードスタイルを作成するタスクでは、通常、個別のリンティングツールと