こんにちは!
プログラミング未経験文系出身、Elixirの国に迷い込んだ?!見習いアルケミストのaliceと申します。
今回はStringモジュールについて学んだことをまとめます。
目次
1.Stringモジュールで遊んでみたシリーズ① -String.at ~ String.chunk の紹介(本記事)
2.Stringモジュールで遊んでみたシリーズ② -String.codepoints ~ String.ends_with? の紹介
3.Stringモジュールで遊んでみたシリーズ③ -String.equivalent? ~ String.last の紹介
目的
Stringモジュールに含まれる関数を触って機能を理解したい
実行環境
Windows 11 + WSL2 + Ubuntu 22.04
Elixir v1.17.3
Erlang v27.0
String.atとは
String.at(string, position)
は0ベースインデックスで、string
からposition
番目の文字を取り出します
例
String.at("elixir", 0)
0
string
よりposition
の値が大きい場合はnilを返します
String.at("elixir", 10)
nil
position
が負の値の場合は最後尾から数えます
String.at("elixir", -1)
"r"
String.bag_distanceとは
String.bag_distance(string1, string2)
は2つの文字列間の類似度を計算します。
string1
と string2
の類似度を表す0から1までの浮動小数点値(bag distance
と呼ばれる値)を返します。
この関数の目的は2つの文字列をマッチングし、大幅に異なる文字列をすばやく除外することです。
例
string1
と string2
に含まれる文字数が全く一致しない場合
String.bag_distance("abc", "")
0.0
string1
と string2
に含まれる文字数が1文字だけ一致する場合
String.bag_distance("abcd", "a")
0.25
string1
と string2
に含まれる文字数が2文字だけ一致する場合
String.bag_distance("abcd", "ab")
0.5
string1
と string2
に含まれる文字数が完全一致する場合
String.bag_distance("abcd", "abcd")
1.0
string1
と string2
に含まれる文字数が完全一致する場合、かつ並び順だけ異なる場合。
文字列内における順番は考慮しないことが分かります
String.bag_distance("abcd", "dbca")
1.0
string1
と string2
に含まれる文字数が完全一致する場合、かつ、文字列長が異なる場合。
文字列長も考慮することが分かります
String.bag_distance("abcd", "daccadcb")
0.5
String.byte_sliceとは
String.byte_slice(string, start_bytes, size_bytes)
はstart_bytes
(0ベースインデックス)から抽出を開始して、size_bytes
のバイト数ぶんの部分文字列を返します。
この関数の目的は、文字列の特定の部分をバイト単位で取り出すことです。
例
前提
文字列héllo
は下記6バイトからなるバイナリデータである。
なおé
は<<195, 169>>の2バイトで1文字を表す結合文字列である。
ここで先頭の2バイトを取り出し、かつ書記素として有効な文字列だけ出力したいとする。1
つまり、期待する出力は"h"
のみである。
inspect("héllo", binaries: :as_binaries)
"<<104, 195, 169, 108, 108, 111>>"
String.byte_sliceを使用すると期待値通りの出力が得られました。
String.byte_slice("héllo", 0, 2)
"h"
類似の関数との比較
binary_slice
binary_slice(binary, start, size)
を使用すると書記素を意識せずに先頭から指定されたバイト数の文字(今回は2バイト)を取り出します。
é
は<<195, 169>>の2バイトで1文字を表す結合文字列です。
したがって、今回の場合にbinary_sliceを使用して取り出すと書記素として無効なバイナリが出力されてしまいます。
binary_slice("héllo", 0, 2)
<<104, 195>>
String.slice
String.slice(string, start, length)
を使用すると書記素ベースで先頭から指定された文字数(今回は2文字)を取り出します。
hé
は"<<104, 195, 169>>であり3バイトです。
したがって、今回の場合にString.slice
を使用して取り出すと先頭の2バイトだけを取り出せません。
String.slice("héllo", 0, 2)
"hé"
String.capitalizeとは
String.capitalize(string, mode \\ :default)
はstring
の先頭の文字を大文字に変換し、残りの文字をmode
に従って小文字に変換します。
例
String.capitalize("abcd")
"Abcd"
1文字目がリガチャ(合字)あり文字列の場合、分解されます
String.capitalize("finfi")
"Finfi"
String.chunkとは
String.chunk(string, trait)
はstring
を文字列を共通の特性(trait)を持つかたまり(chunk)に分割します。
traitは :valid
または :printable
のいずれかを取ります。
この関数の目的は、(この記事を執筆した段階では)バイナリ型の第一引数の内部をフィルタすることだと理解しています。
例
trait = :valid
の場合
有効な文字列(=UTF-8でエンコードされたバイナリ)か否かで分割します
無効な文字列を含まない場合
String.chunk(<<?a, ?b, ?c, 0>>, :valid)
[<<97, 98, 99, 0>>]
無効な文字列を含む場合
<<0xFFFF::utf16>>
は、0xFFFFをUTF-16でエンコードしたもので、Elixirでの有効な文字列(=UTF-8でエンコードされたバイナリ)の範囲外です
String.chunk(<<?a, ?b, ?c, 0, 0xFFFF::utf16>>, :valid)
[<<97, 98, 99, 0>>, <<255, 255>>]
trait = :printable
の場合
印刷可能か否かで分割します。
つまり、制御文字などの非表示文字をフィルタします。
String.chunk(<<?a, ?b, ?c, 0, 0x0FFFF::utf8>>, :printable)
["abc", <<0, 239, 191, 191>>]
余談
iex内で i
関数を実行すると印刷可能な文字列か分かる
印刷可能な文字列の場合
Descriptionの説明にprintable(印刷可能)と記載されます
i <<?a>>
Term
"a"
Data type
BitString
Byte size
1
Description
This is a string: a UTF-8 encoded binary. It's printed surrounded by
"double quotes" because all UTF-8 encoded code points in it are printable.
Raw representation
<<97>>
Reference modules
String, :binary
Implemented protocols
Collectable, IEx.Info, Inspect, List.Chars, String.Chars
印刷不能な文字列の場合
Descriptionの説明にnon-printable(印刷不能)と記載されます
i <<0x0FFFF::utf8>>
Term
<<239, 191, 191>>
Data type
BitString
Byte size
3
Description
This is a string: a UTF-8 encoded binary. It's printed with the `<<>>`
syntax (as opposed to double quotes) because it contains non-printable
UTF-8 encoded code points (the first non-printable code point being
`<<239, 191, 191>>`).
Reference modules
String, :binary
Implemented protocols
Collectable, IEx.Info, Inspect, List.Chars, String.Chars
~Elixirの国のご案内~
↓Elixirって何ぞや?と思ったらこちらもどぞ。Elixirは先端のアレコレをだいたい全部できちゃいます
↓ゼロからElixirを始めるなら「エリクサーチ」がおすすめ!私もエンジニア未経験から学習中です。
↓We Are The Alchemists, my friends!2
Elixirコミュニティは本当に優しくて温かい人たちばかり!
私が挫折せずにいられるのもこの恵まれた環境のおかげです。
まずは気軽にコミュニティを訪れてみてください。3
-
ユーザーが文字として認識する単位のことを書記素(grapheme)と呼びます。 ↩
-
@torifukukaiouさんのAwesomeな名言をお借りしました。Elixirコミュニティを一言で表すと、これに尽きます。 ↩