【初心者向け】JavaScriptのIntl.Segmenterとは?日本語の単語分割・文字数カウントを意図通りに扱う方法

こんにちは!Reiです‎(˵ •̀ ᴗ – ˵) ✧


突然ですが…、文字列処理、ちゃんと扱えていますか?


日本語を単語ごとに分けるときや絵文字を含む文字数カウントで、

「うまく区切れない…」

「思っていた数と違う…?」

なんてなったりしていませんか?


こういった「なんかズレるな…」という違和感、ちゃんと理由があります。

そして、そのズレをいい感じに整えてくれるのが Intl.Segmenter という仕組み ✨


\\ この記事で学べること //

・ なぜ普通の分割ではうまくいかないのか
Intl.Segmenter が何をしてくれるのか
split() との違いや使い分け

このあたりを、ひとつずつ整理しながら見ていきます 👀


「なんとなく動いてるけど、理由まではわかってないかも…」
そんな部分も理解していきましょう ᥫᩣ ̖́-


Intl.Segmenterの基本

ここからは、Intl.Segmenter の基本を見ていきます 👀
まずは、「どんなときに役立つのか」を軽くイメージしていきましょう!


Intl.Segmenter は文字列を自然な単位で区切る仕組み

Intl.Segmenter は、文字列を自然なまとまりで区切るための機能です。

単純に1文字ずつ切るのではなく、言語や文字のまとまりを考えながら分けてくれる のが特徴です!
そのため、日本語の文章や絵文字を含む文字列でも扱いやすくなります ✨


たとえば、配列の分割やデータの区切り処理でよく使われる split() では、カンマ区切りのような「区切り文字」が必要です。

const text = "りんご,バナナ,みかん";
console.log(text.split(",")); // TODO:出力内容を明記

日本語の文章は空白で単語が分かれていないので、区切り文字を使って分割するという考え方ではうまく処理できません。


そこで出てくるのが、Intl.Segmenter

Intl.Segmenter を使うと、区切り文字がなくても、言語のまとまりをもとに自然な単位で分割することができます!


Intl.Segmenter で日本語や絵文字が必要になる

文字列処理というと、最初は「1文字ずつ取り出せばよさそう」と思いやすいですが、でも実際は、見た目の1文字と内部の扱いが一致しないことがあります 👀


たとえば、

  • 英語は単語の区切りが空白だが、日本語は単語の区切りが空白ではない
  • 絵文字は、見た目は1文字でも内部では複数のコードの組み合わせでできている

とかです。

このズレが、文字数カウントや単語分割を難しくする原因になります。


Intl.Segmenter は、こうした違いをふまえて、人が読む単位に近い形で区切ってくれる仕組み なのです ✨


Intl.Segmenterの使い方

ここからは、Intl.Segmenter の使い方を見ていきます 👀

流れはシンプルで、次の2ステップ↓

  • ① ルールを決めて new で Segmenter を作る
  • ② segment メソッドで区切り結果を取得する

書き方自体はそこまで難しくないので、まずは全体の流れをざっくりつかんでいきましょう (˵ •̀ ᴗ – ˵) ✧


ルールを決めて new で Segmenter を作る

最初に、どんなルールで区切るかを決めます。

const segmenter = new Intl.Segmenter("ja-JP", {
granularity: "word",
});

ここで指定しているのはこの2つ↓

  • "ja-JP" → どの言語として扱うか(ロケール)
  • granularity → どの単位で区切るか

どの言語で、どの粒度で区切るかを new するタイミングで決めています。

要は、コンストラクタで「区切り方のルール」をあらかじめ定義して、文字列をそのルールに沿って区切れるようにしています。


この設定によって、Intl.Segmenter を使って文字列を扱う際に、文字ごとに見るのか、単語ごとに見るのか、文ごとに見るのかが変わります 👀


segment メソッドで区切り結果を取得する

実際に文字列を区切るときは、segment() メソッドを使います。

const text = "今日は良い天気です";

const segments = segmenter.segment(text);
console.log([...segments]);

ここで、ポイント!
segment() の戻り値は、普通の配列ではありません。区切り結果を順番に取り出せるオブジェクトになっています。


そのため、確認するときは

console.log([...segments]);

のように、展開して配列にする必要があります 💡


細かい使い方は、このあと granularity で指定する単位ごとに見ていきましょう!


Segmenter の granularity の指定

Intl.Segmenter を使ううえで一番大事なのが、granularity の違いです 👀

ここが変わると、同じ文字列でも区切り方が大きく変わります。


granularity で指定できるのは、次の3つになります!

  • grapheme → 文字単位
  • word → 単語単位
  • sentence → 文単位

目的に合わせて選べるように、それぞれの役割を分けて見ていきましょう 👀


grapheme は見た目に近い1文字単位で扱う

grapheme は、見た目として1文字に見えるまとまりで区切る設定です。

const segmenter = new Intl.Segmenter("ja-JP", {
granularity: "grapheme",
});

const text = "A😊𩸽";

for (const item of segmenter.segment(text)) {
console.log(item.segment);
}

この設定では、見た目に近い単位で1つずつ取り出す子ができます。

grapheme のポイント 💡
・ 見た目は1文字でも length が 2 になる文字でも1文字として扱うことができる


絵文字など length だと扱いづらい文字がある文字数カウントや入力欄の制御などの場面で、grapheme の設定がとっても役立ちます!


word は単語単位の分割に向いている

word は、単語ごとに区切りたいときに使う設定です。

const segmenter = new Intl.Segmenter("ja-JP", {
granularity: "word",
});

const text = "私はReactが好きです。";

for (const item of segmenter.segment(text)) {
console.log(item.segment, item.isWordLike);
}

この設定では、単語っぽいまとまりで区切ることができます。

word のポイント 💡
・ 句読点なども一緒に含まれることがある
isWordLike を使うと、単語だけを判定できる


日本語の文章を単語ごとに扱いたいときや、検索・ハイライト処理の前段階として word を用いると、単語ベースで分けて扱えるようになるので、後続の処理につなげやすくなります!


sentence は文章を文ごとに区切りたいときに使う

sentence は、文単位で区切る設定です。

const segmenter = new Intl.Segmenter("ja-JP", {
granularity: "sentence",
});

const text = "今日は晴れです。明日は雨かもしれません。";

for (const item of segmenter.segment(text)) {
console.log(item.segment);
}

この設定では、文単位で文字列を区切ることができます。

sentence のポイント 💡
・ 表示や整形処理と相性がいい
・ 文ごとにまとまるので、文章の扱いがしやすくなる


長い文章をいったん文に分けてから処理したいときに便利です。
長文コンテンツの加工や、テキストを段階的に表示するUIでも考えやすくなります。


同じ文字列でも、どの単位で扱うかによって処理の組み立て方が変わるので、用途に合わせて選べるようになると、文字列処理の幅が一気に広がります 👀


Segmenter と split

Intl.Segmenter を使っていく上で、split() も合わせて押さえておきましょう 👀

どちらも文字列を分割するためのものですが、分け方の考え方がまったく違うんです。

  • split は区切り文字ベースで分割する
  • Intl.Segmenter はまとまりで分割する

片方だけに頼るのではなく、用途に応じて使い分けながら処理を書いていきましょう


split は区切り文字ベースで分割する

split() は、「この文字で区切る」と自分で決めて分割する方法です。


const text = "react,vue,svelte";
console.log(text.split(","));

このようにカンマ区切りやスラッシュ区切りなど、扱う形式がはっきりしているデータに強いのが特徴です。


Intl.Segmenter はまとまりで分割する

一方の Intl.Segmenter は、文字や言語のまとまりをもとに区切る仕組みです。

たとえば、

  • 日本語の文章を単語ごとに分けたい
  • 絵文字を含む文字列を正しく扱いたい

こういったケースでは、split() だけでは対応しづらくなります。

こういったときに Intl.Segmenter を使うと、意味のまとまりを保ったまま分割することができます。


今回のポイントおさらい !!

最後に、今回出てきた用語と学んだ内容をぎゅっと凝縮しました!

Intl.Segmenter
・文字列を自然なまとまりで区切るためのJavaScriptの機能
・日本語の単語分割や、絵文字を含む文字列の扱いで役立つ


segment()
・区切り結果を取得するときに使うメソッド
・展開構文と組み合わせると結果を確認しやすい


granularity
grapheme は見た目に近い文字単位
word は単語単位
sentence は文単位


split()との違い
split() は区切り文字が明確なデータ向け
Intl.Segmenter は言語や文字のまとまりを考慮した処理向け


最後に

これで困っていた、文字数カウントや日本語での文字分割が容易にできるようになりました!

意外と、知っていれば文字列の扱いを実装の段階で正しく対処できるけど、知らないと文字数カウントや分割のズレがテストや不具合として表に出てくる内容かと思います 👀


あとから気づくと修正に手間がかかるポイントでもあるので、この記事が文字列処理の考え方をひとつ知るきっかけになっていればうれしいです ᥫᩣ ̖́-

コメント

タイトルとURLをコピーしました