8
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

Stringモジュールで遊んでみたシリーズ⑤ -String.next_grapheme_size ~ String.printable? の紹介

Last updated at Posted at 2024-12-08

こんにちは!
プログラミング未経験文系出身、Elixirの国に迷い込んだ?!見習いアルケミストのaliceと申します。
今回はStringモジュールについて学んだことをまとめます。

目次

1.Stringモジュールで遊んでみたシリーズ① -String.at ~ String.chunk の紹介
2.Stringモジュールで遊んでみたシリーズ② -String.codepoints ~ String.ends_with? の紹介
3.Stringモジュールで遊んでみたシリーズ③ -String.equivalent? ~ String.last の紹介
4.Stringモジュールで遊んでみたシリーズ④ -String.length ~ String.next_grapheme の紹介
5.Stringモジュールで遊んでみたシリーズ⑤ -String.next_grapheme_size ~ String.printable? の紹介(本記事)
6.Stringモジュールで遊んでみたシリーズ⑥ -String.replace ~ String.replace_suffix の紹介
7.Stringモジュールで遊んでみたシリーズ⑦ -String.replace_trailing ~ String.split の紹介
8.Stringモジュールで遊んでみたシリーズ⑧ -String.split_at ~ String.to_charlist の紹介
9.Stringモジュールで遊んでみたシリーズ⑨ -String.to_existing_atom ~ String.trim の紹介
10.Stringモジュールで遊んでみたシリーズ10 -String.trim_leading ~ String.valid? の紹介

目的

Stringモジュールに含まれる関数を触って機能を理解したい

実行環境

Windows 11 + WSL2 + Ubuntu 22.04
Elixir v1.17.3
Erlang v27.0

String.next_grapheme_sizeとは

String.next_codepoint(string)string内の次の書記素のサイズ(バイト単位)と、残りの文字列のタプルを返します。

iex
String.next_grapheme_size("olá")
{1, "lá"}

残りの文字列が存在しない場合

iex
String.next_grapheme_size("o")
{1, ""}

空文字の場合

iex
String.next_grapheme_size("")
nil

stringがUTF-8でエンコードされた文字列ではないバイトシーケンスで始まる場合

string型と同様に、次の書記素のサイズ(バイト単位)と、残りの文字列のタプル(バイナリ型)で返されます。

※下記の\x80はUTF-8でエンコードされた文字列ではない

iex
string = "\x80\x80hogehoge"
String.next_grapheme_size(string)
{1, <<128, 104, 111, 103, 101, 104, 111, 103, 101>>}

類似の関数との比較

String.normalizeとは

String.normalize(string, form)stringform形式でUnicode正規化1します。
無効な Unicode コードポイントはスキップされ、文字列の残りの部分が変換されます。

:nfd

form = :nfdのときは文字を可能な限り細かく分解。2アクセントや修飾記号が独立した部品になります。

下記は目検ではインプットとアウトプットの文字列に違いがあるようには見えません。

iex
converted_string = String.normalize("yêṩ", :nfd)
"yêṩ"

しかし、マッチ演算子がfalseを返すので、インプットとアウトプットは同じでないということです。
インプットとアウトプットの差分について詳細を見てみましょう。

iex
"yêṩ" == converted_string
false

インプットの文字列は5つのコードポイントによって構成されていました。

iex
String.codepoints("yêṩ") 
["y", "ê", "s", "̇", "̣"]

アウトプットの文字列はアクセントや修飾記号が独立した部品になっています。
今回の場合は^(サーカムフレックス記号)が独立し、6つのコードポイントによって構成されています。
したがって、インプットとアウトプットは同じでないことが分かります。

iex
String.codepoints(converted_string)
["y", "e", "̂ ", "s", "̣", "̇"] 

:nfc

form = :nfcのときは文字を可能な限り細かく分解した後、再度合成します。

下記も目検ではインプットとアウトプットの文字列に違いがあるようには見えません。

iex
converted_string = String.normalize("leña", :nfc)
"leña"

しかし、マッチ演算子がfalseを返すので、インプットとアウトプットは同じでないということです。
インプットとアウトプットの差分について詳細を見てみましょう。

iex
"leña" == converted_string
false

インプットの文字列は5つのコードポイントによって構成されていました。

iex
String.codepoints("leña") 
["l", "e", "n", "̃ ", "a"]

アウトプットの文字列はアクセントや修飾記号が合成された文字列になっています。
今回の場合は̃ (チルダ記号)が合成され、4つのコードポイントによって構成されています。
したがって、インプットとアウトプットは同じでないことが分かります。

iex
String.codepoints(converted_string)
["l", "e", "ñ", "a"]

:nfkd

form = :nfkdのときは文字を可能な限り細かく分解。アクセントや修飾記号が独立した部品になります。
但し「互換性」を考慮して文字を変換します。3
つまり、装飾や特別な書式情報を取り除き、プレーンな文字にします。

下記は目検で合字(リガチャ)が分解されたように見えています。

iex
converted_string = String.normalize("fi", :nfkd)
"fi"

さらに、マッチ演算子がfalseを返すので、インプットとアウトプットは同じでないということです。
インプットとアウトプットの差分について詳細を見てみましょう。

iex
"fi" == converted_string
false

インプットの文字列は1つのコードポイントによって構成されていました。

iex
String.codepoints("fi") 
["fi"]

アウトプットの文字列はアクセントや修飾記号が独立した部品になっています。
今回の場合は合字が分解され、2つのコードポイントによって構成されています。
したがって、インプットとアウトプットは同じでないことが分かります。

iex
String.codepoints(converted_string)
["f", "i"]

:nfkc

form = :nfkcのときは文字を可能な限り細かく分解。アクセントや修飾記号が独立した部品になります。
但し「互換性」を考慮して文字を変換します。3
つまり、装飾や特別な書式情報を取り除き、プレーンな文字にします。
さらに、分解後、再度合成します。

下記は目検で合字(リガチャ)が分解されたように見えています。

iex
converted_string = String.normalize("fi", :nfkc)
"fi"

さらに、マッチ演算子がfalseを返すので、インプットとアウトプットは同じでないということです。
インプットとアウトプットの差分について詳細を見てみましょう。

iex
"fi" == converted_string
false

インプットの文字列は1つのコードポイントによって構成されていました。

iex
String.codepoints("fi") 
["fi"]

アウトプットの文字列はアクセントや修飾記号が独立した部品になっています。
今回の場合は合字が分解され、2つのコードポイントによって構成されています。
合字はプレーンな文字列ではないため、再度合成しても元の合字には戻りません。
したがって、インプットとアウトプットは同じでないことが分かります。

iex
String.codepoints(converted_string)
["f", "i"]

まとめ

String.normalize(string, form)formごとの差分をまとめると下記の表の通りです

formの値 分解後に再度合成するか 分解時に互換性を考慮するか
:nfd
:nfc
:nfkd
:nfkc

String.pad_leadingとは

String.pad_leading(string, count, padding \\ [" "])stringの文字数を数えた上で、countに満たない場合、stringの先頭から不足分をpaddingで満たします。

iex
String.pad_leading("abc", 5)
"  abc"

不足分に対しpaddingの一部を使用するだけで満たされる場合。
paddingの先頭から不足分にあたる文字数だけが使われる

iex
String.pad_leading("abc", 4, "12")
"1abc"

不足分に対しpaddingを全て使用してもまだ不足がある場合。
paddingが繰り返し使われる

iex
String.pad_leading("abc", 6, "12")
"121abc"

stringの文字数がcountよりも多い場合

iex
String.pad_leading("abcdef", 5)
"abcdef"

paddingはリストでもOK

iex
String.pad_leading("abc", 10, ["1", "23", "45"])
"abcdef"

String.pad_trailingとは

String.pad_trailing(string, count, padding \\ [" "])stringの文字数を数えた上で、countに満たない場合、stringの末尾から不足分をpaddingで満たします。

iex
String.pad_trailing("abc", 5)
"abc  "

不足分に対しpaddingの一部を使用するだけで満たされる場合。
paddingの先頭から不足分にあたる文字数だけが使われる

iex
String.pad_trailing("abc", 4, "12")
"abc1"

不足分に対しpaddingを全て使用してもまだ不足がある場合。
paddingが繰り返し使われる

iex
String.pad_trailing("abc", 6, "12")
"abc121"

stringの文字数がcountよりも多い場合

iex
String.pad_trailing("abcdef", 5)
"abcdef"

paddingはリストでもOK

iex
String.pad_trailing("abc", 10, ["1", "23", "45"])
"abcdef"

String.printable?とは

String.printable?(string, character_limit \\ :infinity)stringに対してcharacter_limitまでの文字数が印刷可能であるか否かをチェックします。

iex
String.printable?("abc")
true

バイナリ型は印刷不能なのでデフォルトではcharacter_limit = :infinityのためfalseになります。

iex
String.printable?("abc" <> <<0>>)
false

バイナリ型は印刷不能だがcharacter_limitで文字数制限を付ければtrueにもなりえます。

iex
String.printable?("abc" <> <<0>>, 2)
true

~Elixirの国のご案内~

↓Elixirって何ぞや?と思ったらこちらもどぞ。Elixirは先端のアレコレをだいたい全部できちゃいます:laughing::sparkles::sparkles:

↓ゼロからElixirを始めるなら「エリクサーチ」がおすすめ!私もエンジニア未経験から学習中です。

We Are The Alchemists, my friends!:bouquet:4
Elixirコミュニティは本当に優しくて温かい人たちばかり!
私が挫折せずにいられるのもこの恵まれた環境のおかげです。
まずは気軽にコミュニティを訪れてみてください。5

  1. https://ja.wikipedia.org/wiki/Unicode%E6%AD%A3%E8%A6%8F%E5%8C%96

  2. 文字を可能な限り細かく分解することを「正準等価」または「正規等価」というそうです。https://ja.wikipedia.org/wiki/Unicode%E3%81%AE%E7%AD%89%E4%BE%A1%E6%80%A7#%E6%AD%A3%E6%BA%96%E7%AD%89%E4%BE%A1

  3. 互換性を考慮した上で文字を可能な限り細かく分解することを「互換等価」というそうです。 2

  4. @torifukukaiouさんのAwesomeな名言をお借りしました。Elixirコミュニティを一言で表すと、これに尽きます。

  5. @kn339264さんの素敵なスライドをお借りしました。Elixirコミュニティはいろんな形で活動中!

8
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
8
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?