Javaについて徹底解説!

みんな実は困っていた? 半角・全角空白をJavaではどうtrimすべきか

大石 英人

開発エンジニア/Java20年/Java GOLD/リーダー/ボールド歴2年

String.trimは、文字列の前後にある空白文字を削除するメソッドです。実際のプログラムでも、かなり頻繁に使うものです。

ただ、ここで「空白」と言った時に、アジアの片隅に生きる日本人の立場の弱さを感じる時があります。例えば、いわゆる全角空白も削除したいですよね。でも、残念ながらString.trimは全角空白を削除してはくれません。これは何とかしたいです。

そして、プログラムで削除したい文字は空白だけとは限りません。“”といったクォーテーションを削除したい時もあります。それに、SQLには前や後ろからだけ文字を削除してくれるLTRIMRTRIMがあるのに、Javaにはなぜないのだろうと思っている方も多いでしょう。

この記事では、String.trimを足掛かりに、文字列から余分な文字を削除する方法を、プログラムの実例をたくさん交えながらお伝えします。

※この記事のサンプルは、Java 11の環境で動作確認しています。


1.【基本】文字列の空白文字を削除するtrim

まずは基本から行きましょう。文字列の前後にある「空白」を削除するには、String.trimを使います。戻り値は、前後から空白が削除された文字列です。

ですが、trimにはいくつかの困ったところがあります。それを一緒に見ていきましょう。

1-1.String.trimは片側の空白だけを削除できない

trimは常に文字列の両側から空白を削除します。残念ながら、片側の空白だけを削除はできません。

文章やプログラムでは、文字列先頭の空白やタブへは意味を与えていることがあるので、消したくないケースがあります。例えば、タブをインデントに使っていたりする場合は、後ろの空白だけ消したいとかtrimはそういう用途には使えません。

1-2.String.trimは空白扱いする文字が固定されている

trimが空白扱いする文字は固定されていて、変更できません。String.trimJavadocにはこう書いてあります(Java 11の日本語版/英語版Javadoc)

値がこの文字列で、先頭と末尾のすべてのスペースが削除され、コード・ポイントが‘U+0020’ (空白文字)以下の文字でスペースが定義されている文字列を返します。

Returns a string whose value is this string, with all leading and trailing space removed, where space is defined as any character whose codepoint is less than or equal to ‘U+0020’ (the space character).

つまり、コードポイントが空白以下の文字を、文字列の両端から削除してくれます(※コードポイントについては後述します)。対象の文字は、タブや改行、色々な制御文字など、普通は印字しない文字です。

これはJava 1.0からのtrimの仕様です(Javadocに明確に書かれているのは、恐らく1.1から)。動作を変えると過去に作ったプログラムが動かなくなる可能性があるので、きっとずっとこのままでしょう。

1-3.String.trimは全角空白を削除してくれない

英語環境ならこのtrimでまあ充分なのですが、そう…trimはいわゆる全角空白を空白として認識してくれず、削除しません。必要なら自分で処理を作る必要があります。日本人にとっては、少し使いづらいですよね。


2.全角空白も削除するstrip/stripLeading/stripTrailingJava 11~】

Java 11でString.stripが追加されました。stripは、全角空白も含む広い意味での「空白」を削除してくれます。stripの仲間として、前からだけ削除するstripLeading、後ろからだけ削除するstripTrailingもあります。

2-1.stripが削除する空白はCharacter.isWhitespaceが判断する

stripが削除する空白は、Character.isWhitespacetrueを戻す文字です。stripJavadocの記述は以下のとおりで、「white space」の部分にはCharacter.isWhitespaceへのハイパーリンクがあります。

値がこの文字列であり、先頭と末尾のすべてのwhite spaceが削除されている文字列を返します。

Returns a string whose value is this string, with all leading and trailing white space removed.

そして、Character.isWhitespaceJavadocでの記述は以下のとおりです。現実世界で空白扱いされる文字は、全角空白を含め、大体はその範囲内です。実際にどの文字が対象かは別の章で簡単にお伝えします。

指定された文字(Unicodeコード・ポイント)Javaの基準に従った空白かどうかを判定します。 次の基準のどれかを満たす場合にだけ、Javaの空白文字になります。

Determines if the specified character (Unicode code point) is white space according to Java. A character is a Java whitespace character if and only if it satisfies one of the following criteria:

 


3.欲しいtrimを自分で作る

Strin.trimの不便さや、Java 11でのstripとその仲間たちが便利なのは分かりました。でも、Java 11ではない環境ではどうすればいいのでしょうか。

やはり、欲しいものは自分で作るか、外部ライブラリを使うことになります。この章では、必要な機能を持つtrimを自分自身で作る方法を探ってみます。

3-1.【お手軽】replaceFirst/replaceAllによるtrim

一番簡単なのは、文字列を置換するメソッドString.replaceFirstreplaceAllを使うものです。replaceFirst/replaceAllで置換する文字を指定するには、正規表現と呼ばれるパターンが使えます。

例えば、全角・半角空白両対応のtrimをしたければ、正規表現として \h を指定するだけです。

このパターンでは、先頭(^)の後と末尾($)の前で空白扱いしたい文字があれば、それを“”に置換しているだけです。前だけ、後ろだけをtrimしたい場合は、対応するreplaceFirstだけを呼びましょう。

これらをメソッドにしてみると、こんな感じです。nullと文字数のチェックだけ追加した感じですね。

なお、この例ではたまたま空白を使っていますが、置換したい文字は正規表現で自由に指定できます。これがreplaceFirst/replaceAllの便利な所です。でも、対象とする文字の数が増えてくると、なかなか面倒になってきます。

3-1-1.【参考】Javaの正規表現の定義済み文字クラス

先程の例では、空白を指定するのに文字クラス \h を使いました。同じようなものがいくつかありますので、簡単に紹介します。詳しくはPatternJavadocを参照してください。

Pattern (Java SE 11 & JDK 11)

https://docs.oracle.com/javase/jp/11/docs/api/java.base/java/util/regex/Pattern.html

文字クラス 説明
. 任意の文字 行末記号とマッチする場合もあります。
\d 数字 0~9までの数字です。
\D 数字以外 \dを否定したものです。
\h 水平方向の空白文字 タブや改行など。全角空白もこれに含まれます(!)
\H 水平方向の空白文字以外 \hを否定したものです。
\s 空白文字 空白、改行、タブなどです。
\S 空白文字以外 \sを否定したものです。
\w 単語文字 大小のアルファベットと数字です。
\W 単語文字以外 \wを否定したものです。

3-2.文字でループして判断する

Stringが持つ文字でループをして、削除したい文字かを判断する処理を作るのも、地味ながら確実です。削除したい文字の判断を自分でできるので、柔軟にも作れます。

例えば、そんなメソッドを持つクラスを素直に作るなら、以下のとおりです。文字削除の判断ロジックはCharacter.isWhitespaceをそのまま使いました。これなら、今使われているどんなJavaの環境でも動くでしょう。

なお、CharacterにはisWhitespaceの他にも、色々な文字種を確認するためのis~系メソッドがあります。これらを組み合わせてもいいでしょう。

3-2-1.【発展】もう少し汎用的に作ってみる

さきほど作ったStripperをもう少し汎用的にすると、例えば以下のようになります。このクラスを動かすにはJava 9以降が必要です。でも、String.codePointsStreamを使っている箇所を作り直せば、Java 1.5以降であれば動くはずです。

このクラスとメソッドは、以下のような感じで使います。削除対象にできる文字についてはUnicodeのサロゲートペアも考慮していますので、例えばいわゆる「つちよし」や「はしご高」も使えます。削除する文字は、コンストラクタで指定する以外にも、インスタンスを作った後にもaddで追加できます。


4.【便利】外部ライブラリのtrim的なメソッドを使う

Javaには文字・文字列を扱うためのライブラリが多数あります。その中でも有名なもの、例えばApache Commons-LangGuavaには、trimをするための便利なクラス・メソッドがあります。早速いくつかを使ってみましょう。

4-1.Apache Commons-LangのStringUtilsを使う

Apache Commons-Langでtrim的な操作をするなら、StringUtils.stripなどを使います。

StringUtils (Apache Commons Lang 3.9-SNAPSHOT API)

https://commons.apache.org/proper/commons-lang/apidocs/org/apache/commons/lang3/StringUtils.html

stripは、引数が一つのものだとCharacter.isWhitespaceを使います。引数が二つstripは、削除したい文字を指定できます(サロゲートペアには対応していないようです)

これらの他にも、頭だけ、終わりだけ、nullだったら“”にする、“”だったらnullにするなど様々な種類のstripがあります。ぜひAPIを見て、使えそうなものを探してみましょう。

4-2.GuavaのCharMatcherを使う

GoogleのGuavatrim的な操作をするなら、CharMatcherと関連するtrim~という名前のメソッドを使います。

CharMatcher (Guava: Google Core Libraries for Java HEAD-jre-SNAPSHOT API)

https://google.github.io/guava/releases/snapshot-jre/api/docs/com/google/common/base/CharMatcher.html

この例では文字列の頭と終わりから削除するtrimFromを使っています。他にあるメソッドとしては、trimLeadingFromは最初から削除し、trimTrailingFromは後ろから削除します。

特徴的なのは、対処とする文字の指定の仕方です。CharMatcher.whitespaceだと空白用のCharMatcherを取得し、CharMatcher.asciiならASCIIの範囲にある文字となります。


5.【参考】trim/stripで知っておくと役立つ知識

5-1.コードポイントとはUnicodeでの文字の連番

JavaではUnicodeという方式で文字を扱います。そのUnicodeで、一つの文字ごとに0から順番に割り振られた番号がコードポイントです。UTF-8UTF-16などのやり方(文字符号化方式)でエンコーディングされた結果であるバイト列の値と、コードポイントは違うものです。

Javaのプログラム中で文字を指定する時でも、コードポイントという中立的な数字を使うことがあります。一つの文字のコードポイントはどこでも必ず同じなので、どうエンコーディングされるかには関係なく文字を示せるからです。Javaのプログラム上では、コードポイントは32ビットのintになります。

例えば、半角空白はU+0020というコードポイントが割り当てられています。数字部分は16進数で、これを十進数にすると32です。UTF-8では0x20となり、UTF-16では0x0020です。U+007Fまでは、いわゆるASCIIコードと同じ数字と文字が割り当てられています。

全角空白のコードポイントはU+3000で、UTF-8では0xE3 0x80 0x80となり、UTF-16では0x3000です。いわゆるつちよし(𠮷)のコードポイントはU+20BB7で、UTF-8では0xF0 0xA0 0xAE 0xB7となり、UTF-16BEでは0xD842 0xDFB7です。

5-2.Character.isWhitespaceが空白扱いする文字

空白を判断するCharacter.isWhitespaceは、実はJavaにずっと昔からあるメソッドです。なんと、Java 1.1(11ではない!!)の時代からです。サロゲートペア対応したisWhitespace(int)は、Java 1.5からです

Java 11のJavadocをひも解くと、Character.isWhitespaceは、以下の文字を空白扱いするとあります。

指定された文字(Unicodeコード・ポイント)Javaの基準に従った空白かどうかを判定します。 次の基準のどれかを満たす場合にだけ、Javaの空白文字になります。

Unicodeの空白文字(SPACE_SEPARATORLINE_SEPARATOR、またはPARAGRAPH_SEPARATOR)であるが、改行なしの空白(‘\u00A0’‘\u2007’‘\u202F’)ではない。

‘\t’ (U+0009水平タブ)である。

‘\n’ (U+000A改行)である。

‘\u000B’ (U+000B垂直タブ)である。

‘\f’ (U+000Cフォーム・フィード)である。

‘\r’ (U+000D復帰)である。

‘\u001C’ (U+001Cファイル区切り文字)である。

‘\u001D’ (U+001Dグループ区切り文字)である。

‘\u001E’ (U+001Eレコード区切り文字)である。

‘\u001F’ (U+001F単位区切り文字)である。

さて、コードポイントが書いてあるものはわかりやすいです。でも、SPACE_SEPARATORLINE_SEPARATORPARAGRAPH_SEPARATORなる記述があります。これらは、Unicodeで決められている、同じような種類の文字をまとめた「一般カテゴリ(General Category)」と呼ばれるものです。

それぞれどんな文字が含まれるかは、例えば以下のサイトを参照してください。空白のカテゴリーだけで17個も文字があるのです。世の中は広いですね

Unicode Characters in the ‘Separator, Space’ Category

http://www.fileformat.info/info/unicode/category/Zs/list.htm

→ SPACE_SEPARATORに対応するもの(カテゴリーZs)。全角空白はここに含まれます。ただし、U+00A0U+2007U+202FCharacter.isWhitespaceでは空白扱いされません。

Unicode Characters in the ‘Separator, Line’ Category

https://www.fileformat.info/info/unicode/category/Zl/list.htm

→ LINE_SEPARATORに対応するもの(カテゴリーZl)

Unicode Characters in the ‘Separator, Paragraph’ Category

https://www.fileformat.info/info/unicode/category/Zp/list.htm

→ PARAGRAPH_SEPARATORに対応するもの(カテゴリーZp)


6.まとめ

この記事では、String.trimの使い方から初めて、Java 11で追加されたstripに触れました。そして、自分が欲しいと思うtrimを自分で作ったり、関連する外部ライブラリまでを簡単に紹介しました。

紹介したプログラムはあくまで一例なので、皆さんの必要に応じて参考にしたり、カスタマイズしてください。

Javaの標準APIに無い機能はどんどん自分で作りましょう。標準APIと言えども、皆さんのプログラムと同じように、標準APIにあるクラスを使って作られていることには変わりません。

それに、標準APIがどういう風に実装されているか調べて知ることは、プログラマとしてのスキルアップにもつながる、大事なことですよ。

私たちは、全てのエンジニアに市場価値を高め自身の望む理想のキャリアを歩んでいただきたいと考えています。もし、今あなたが転職を検討しているのであればこちらの記事をご一読ください。理想のキャリアを実現するためのヒントが見つかるはずです。

8/28(水)開催決定!
【テックジム×ENGINEER.CLUB】ゼロからはじめるPythonプログラミング入門講座

プログラミングは初めてだけどPythonから始めてみたいという方のために、無料のハンズオン開発講座をテックジム×ENGINEER.CLUBで共同開催することになりました。開催日時は以下の通りです。

  • 8月28日(水)19時〜21時 東京開催

本講座で学んでいただく「TechGYM方式」とは、基礎知識なしでも座学なしでプログラミングに専念できるように設計されたプログラミングのカリキュラムメソッドです。「まるで魔法にかかったようにプログラミンスキルが習得できる」と評判の本講座をぜひ一度体験してみてください。

『技術力』と『人間力』を高め市場価値の高いエンジニアを目指しませんか?

私たちは「技術力」だけでなく「人間力」の向上をもって遙かに高い水準の成果を出し、関わる全ての人々に感動を与え続ける集団でありたいと考えています。

高い水準で仕事を進めていただくためにも、弊社では次のような環境を用意しています。

  • 定年までIT業界で働くためのスキル(技術力、人間力)が身につく支援
  • 「給与が上がらない」を解消する6ヶ月に1度の明確な人事評価制度
  • 平均残業時間17時間!毎週の稼動確認を徹底しているから実現できる働きやすい環境

現在、株式会社ボールドでは「キャリア採用」のエントリーを受付中です。

まずは以下のボタンより弊社の紹介をご覧いただき、あなたの望むキャリアビジョンをエントリーフォームより詳しくお聞かせください。