Are you sure you want to delete the question?

If your question is resolved, you may close it.

Leaving a resolved question undeleted may help others!

We hope you find it useful!

JavaScript で文字列を文字ごとに分解後、半角=1、全角=2として情報を得るにはどうしたらよいでしょうか?

解決したいこと

表題の通りですが、
特定の処理をする VSCode のExtension を作りたいと思っています。その場面で、文字列処理をするときに、文字列を1文字に分解する方法はわかってきたのですが、その1文字が半角で描画されるのか、全角で描画されるのかを知りたいです。

厳密には、フォントによって、半角と全角の中間の幅の文字などがあるような気もして明確に分けられないのかもしれません。基本的にはいわゆる英数字のアスキー文字を半角としてサイズ1として、日本語の文字は全角としてサイズ2として求める方法がしりたいです。

とはいえ、アルファベットの結合文字などもあり、それは半角に入るのだろうと思うのですが、そのあたり経験がないのでよくわからないのでご存知のかたおられましたら、教えていただきたいです。

発生している問題・エラー

以前、こちらの記事を書いたことがあります。

JavaScript 文字列を反転させたい。結合文字とサロゲートペア対応。 - Qiita
https://qiita.com/standard-software/items/24a8cc99171f988e4523

そこで記載している通りで、サロゲートペアや結合文字に対応した文字列の1文字ずつの配列分解についてはわかってきました。
ただ、その1文字が半角なのか全角なのか、の判断をするときに何を判断すればよいのかがわからないです。

下記記事が大変役にたっています。

文字とは何か?IchigoJam絵文字対応とUnicodeの旅、結合文字対応の文字分割ライブラリJavaScript対応版公開! #js #IchigoJam / 福野泰介の一日一創 / Create every day by Taisuke Fukuno
https://fukuno.jig.jp/2831

JavaScript における文字コードと「文字数」の数え方 | blog.jxck.io
https://blog.jxck.io/entries/2017-03-02/unicode-in-javascript.html

だいぶ知らない事も多いため、あいまいな質問になっているかもしれませんが、アドバイスいただけたら助かります。

よろしくお願いします。

0 likes

3Answer

Unicode において、いわゆる全角文字・半角文字は Annex #11: East Asian Width で定義されています。それによれば文字は以下の6種類に分けられます。

  • Wide - 漢字やかななどの全角文字
  • Narrow (Na) - 英数字などの半角文字
  • Fullwidth - 全角カタカナなど、明示的に Fullwidth として収録されている全角文字
  • Halfwidth - 半角カタカナなど、明示的に Halfwidth として収録されている半角文字
  • Ambiguous - ギリシャ文字など、和文では全角で、英文では半角で扱われるために幅が曖昧な文字
  • Neutral (N) - デーヴァナーガリーなど、従来の日本語環境では使われないために全角とも半角とも文類されない文字

Ambiguous と Neutral の文字幅は文脈に合わせて利用者が決めることになります。また結合記号はそれ自身がどの種別の文字と結合するかによって分類されていますが、記号が実際に描画されるときの幅とは一致しない場合があることに注意が必要です。

JavaScript では East Asian Width を計算するパッケージを npm で探すといいでしょう。いくつかありますが、分類表は Unicode の改訂に合わせておよそ年に1回更新されるので、なるべく更新日が新しいパッケージを選ぶといいと思います。 https://www.npmjs.com/package/meaw は最新の Unicode 14.0.0 に対応していました。

1Like

Comments

  1. すごい!!!

    ありがとうございます。大変勉強になります。

    非常に助かりました。

Ambiguous と Neutral の幅はデフォルトで1ですが、2にしたいだけであれば meaw.computeWidth(str, { A: 2, N: 2 }) でできますよ。

1Like

Comments

  1. あ、そのAは文字のAじゃなくて、種類のAだったのですね。ありがとうございます。

meaw だと、自分の文字幅計算と微妙に違うかなと思い、少しカスタマイズして次のような実装を行ってみました。

const split = require('graphemesplit');
const { getEAW, computeWidth } = require('meaw');

const computeWidthCustome = (str) => {
  let result = 0;
  for(const char of split(str)){
    console.log(char, getEAW(char))
    switch (getEAW(char)) {
      case 'Na':
        result += 1;
        break;
      case 'H':
        result += 1;
        break;
      case 'W':
        result += 2;
        break;
      case 'F':
        result += 2;
        break;
      case 'A':
        result += 2;
        break;
      case 'N':
        result += 2;
        break;
      default:
        new Error(`computeWidthCustome ${char} ${str}`)
    }
  }
  return result;
}
0Like

Your answer might help someone💌