1
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.at ~ String.chunk の紹介

Last updated at Posted at 2024-11-24

こんにちは!
プログラミング未経験文系出身、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番目の文字を取り出します

iex
String.at("elixir", 0)
0

stringよりpositionの値が大きい場合はnilを返します

iex
String.at("elixir", 10)
nil

positionが負の値の場合は最後尾から数えます

iex
String.at("elixir", -1)
"r"

String.bag_distanceとは

String.bag_distance(string1, string2)は2つの文字列間の類似度を計算します。
string1string2の類似度を表す0から1までの浮動小数点値(bag distanceと呼ばれる値)を返します。
この関数の目的は2つの文字列をマッチングし、大幅に異なる文字列をすばやく除外することです。

string1string2に含まれる文字数が全く一致しない場合

iex
String.bag_distance("abc", "")
0.0

string1string2に含まれる文字数が1文字だけ一致する場合

iex
String.bag_distance("abcd", "a")
0.25

string1string2に含まれる文字数が2文字だけ一致する場合

iex
String.bag_distance("abcd", "ab")
0.5

string1string2に含まれる文字数が完全一致する場合

iex
String.bag_distance("abcd", "abcd")
1.0

string1string2に含まれる文字数が完全一致する場合、かつ並び順だけ異なる場合。
文字列内における順番は考慮しないことが分かります

iex
String.bag_distance("abcd", "dbca")
1.0

string1string2に含まれる文字数が完全一致する場合、かつ、文字列長が異なる場合。
文字列長も考慮することが分かります

iex
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"のみである。

iex
inspect("héllo", binaries: :as_binaries)
"<<104, 195, 169, 108, 108, 111>>"

String.byte_sliceを使用すると期待値通りの出力が得られました。

iex
String.byte_slice("héllo", 0, 2)
"h"

類似の関数との比較

binary_slice

binary_slice(binary, start, size)を使用すると書記素を意識せずに先頭から指定されたバイト数の文字(今回は2バイト)を取り出します。
éは<<195, 169>>の2バイトで1文字を表す結合文字列です。
したがって、今回の場合にbinary_sliceを使用して取り出すと書記素として無効なバイナリが出力されてしまいます。

iex
binary_slice("héllo", 0, 2)
<<104, 195>>

String.slice

String.slice(string, start, length)を使用すると書記素ベースで先頭から指定された文字数(今回は2文字)を取り出します。
は"<<104, 195, 169>>であり3バイトです。
したがって、今回の場合にString.sliceを使用して取り出すと先頭の2バイトだけを取り出せません。

iex
String.slice("héllo", 0, 2)
"hé"

String.capitalizeとは

String.capitalize(string, mode \\ :default)stringの先頭の文字を大文字に変換し、残りの文字をmodeに従って小文字に変換します。

iex
String.capitalize("abcd")
"Abcd"

1文字目がリガチャ(合字)あり文字列の場合、分解されます

iex
String.capitalize("finfi")
"Finfi"

String.chunkとは

String.chunk(string, trait)stringを文字列を共通の特性(trait)を持つかたまり(chunk)に分割します。
traitは :valid または :printableのいずれかを取ります。
この関数の目的は、(この記事を執筆した段階では)バイナリ型の第一引数の内部をフィルタすることだと理解しています。

trait = :valid の場合

有効な文字列(=UTF-8でエンコードされたバイナリ)か否かで分割します

無効な文字列を含まない場合

iex
String.chunk(<<?a, ?b, ?c, 0>>, :valid)
[<<97, 98, 99, 0>>]

無効な文字列を含む場合

<<0xFFFF::utf16>>は、0xFFFFをUTF-16でエンコードしたもので、Elixirでの有効な文字列(=UTF-8でエンコードされたバイナリ)の範囲外です

iex
String.chunk(<<?a, ?b, ?c, 0, 0xFFFF::utf16>>, :valid)
[<<97, 98, 99, 0>>, <<255, 255>>]

trait = :printable の場合

印刷可能か否かで分割します。
つまり、制御文字などの非表示文字をフィルタします。

iex
String.chunk(<<?a, ?b, ?c, 0, 0x0FFFF::utf8>>, :printable)
["abc", <<0, 239, 191, 191>>]

余談

iex内で i関数を実行すると印刷可能な文字列か分かる

印刷可能な文字列の場合

Descriptionの説明にprintable(印刷可能)と記載されます

iex
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(印刷不能)と記載されます

iex
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は先端のアレコレをだいたい全部できちゃいます:laughing::sparkles::sparkles:

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

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

  1. ユーザーが文字として認識する単位のことを書記素(grapheme)と呼びます。

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

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

1
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
1
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?