どう見ても正しいキーが一致しない。結合が何も返さない。目の前に見えているのに、翻訳の文字列が「見つからない」と報告される。両方の値をテキストエディタにコピーしても、やはり同じに見える。
同じではありません。片方に、場所を占めず、何も描かない文字が入っています。そして文字列の比較は、あなたに見えるかどうかを気にしません。
見えない文字とは、どれのことか
決まった一覧があり、感覚よりは短いものです。何のために作られたかで分けると、こうなります。
U+00A0改行なしスペース — ブラウザやワープロがそこで行を折り返さない空白です。普通の半角スペースとまったく同じに見えて、別の文字です。U+00ADソフトハイフン — 単語をここで分割してもよいという助言です。ちょうどそこで行が折り返されないかぎり、見えません。U+200B幅ゼロスペース、U+2060単語結合子 — 幅をまったく持たない、行分割のための助言です。U+200C幅ゼロ非接合子、U+200D幅ゼロ接合子 — 隣り合う文字のつながり方を制御します。アラビア語やペルシア語では正当な文字であり、U+200Dは現代の絵文字の並びを 1 つにまとめている文字でもあります。U+200E/U+200F書字方向マークと、U+202A〜U+202Eの埋め込み・上書き制御 — 左横書きと右横書きが混ざる文章で、方向を強制します。U+FEFF— ファイルの先頭にあれば BOM、それ以外の場所にあれば幅ゼロの改行なしスペースです。U+3000全角スペース — 日本語・中国語・韓国語の組版で使う、全角幅の空白です。これは見えます。ただ、あなたが思っている空白ではない、というだけです。
それぞれの出どころ
出どころを知る価値があります。あと何個あるかが、それで分かるからです。
Web ページやワープロからの貼り付けは、改行なしスペースを連れてきます。HTML を書く人が要素を離しておくために使い、貼り付けがそれを保ちます。この経路で 1 つ入ったなら、元の文書にあった数だけ入っています。
PDF からの貼り付けは、ソフトハイフンと幅ゼロスペースを連れてきます。レイアウト処理が文字を均等に配置するために置いたもので、テキスト抽出がほかのものと一緒に渡してきます。
右横書きの文章は、書字方向マークを連れてきます。アラビア語とラテン文字が混ざった文章が_自分の_エディタで正しく表示されるように、入力した人が足したものであることが多いです。その人は実在の問題を解いていました。ただ、そのマークがキーにまで残るべきではなかった、というだけです。
複数の書き出しを連結したファイルは、途中に U+FEFF を抱えます。それぞれの断片が自分の BOM を持っていて、先頭にあったのは最初の 1 つだけだったからです。
日本語入力で打った文章は、全角スペースを連れてきます。変換中のスペースキーが打つのが、それだからです。
なぜ、どこもおかしく見えないのか
文字列の比較は 1 文字ずつ行われます。"item_mug" と "item_mug" は長さが違うので等しくありません。それ以上の話は何もありません。参照も、結合も、辞書も、=== も、この 2 つを同じものとは扱いません。
謎めいて感じられるのは、調べるために使うどの層も証拠を隠してしまうからです。ターミナルは何も描きません。ブラウザも何も描きません。差分は同じに見える 2 行を並べます。値をコピーして持ち出せばその文字も一緒に付いてくるので、どこかに貼っても、問題が露わになるのではなく再現されるだけです。
「trim すればいい」で足りないのも、これが理由です。trim() が取り除くのは空白文字であり、ここに挙げた文字の多くは、その定義でいう空白ではありません。幅ゼロスペースは trim を無傷で生き延びます。
もう 1 つ、上の一覧には入らない似た失敗があります。2 つの値が、見えない文字ではなく Unicode の正規化形の違いですれ違うことがあります。é は 1 つのコードポイントでも、e のあとに結合用のアクセントを続けた形でも書け、その 2 つは同じに描かれる別の文字列です。日本語では、濁点付きの「が」を 1 文字で書くか「か」+結合用濁点で書くかがこれに当たります。見えない文字を探す機能では、これは見つかりません。どちらの形も目に見えているからです。これには正規化の処理が要ります。
間違いではないことが多い 1 つ
全角スペース、U+3000 は別に扱う価値があり、このサイトのエディタもそのために、ほかとは違う分類をしています。警告ではなく、補足として報告します。
日本語の文章では、全角スペースは書き手が意図したとおりのものであることがよくあります。その文章にとって正しい空白であり、標準の入力方式から出てきたものであり、取り除けば文章が変わります。
ですから、そこにあると知っておくことには意味があります——識別子の列にある全角スペースは、まず間違いなく誤りです。一方で、日本語の説明文が並ぶファイルに対して「この種の空白をすべて削除」を一括で当てれば、中身を黙って損ないます。判断の分かれ目は、その文字が疑わしいかどうかではなく、どの列に現れたかです。
見つけて取り除く
必要なのは、それらを描いてくれる道具です。定義上、見えないものも打てないものも、検索できないからです。見えるようになったら、順序はこうなります。
- 壊れた 1 つではなく、すべての箇所を見つける。 失敗した値は、あなたが気づいた 1 つであって、影響を受けた唯一の 1 つではありません。改行なしスペースを 1 回貼り付けたものは、50 回貼り付けています。
- セル単位ではなく、文字の種類ごとに決める。 キーの列にある幅ゼロの文字は、常に誤りです。改行なしスペースは、消すよりも普通の半角スペースに置き換えたい場合がほとんどです。説明文の中の全角スペースは、そのまま残すべきです。
- ファイル全体に 1 回の操作として当てる。 結果が一貫し、1 ステップで取り消せます。
- あとでもう一度検査する。 幅ゼロスペースを取り除いたことで、その陰に隠れていた末尾の半角スペースが現れることがあります。
ファイルを壊すもっと広い問題群の中でこれがどこに位置するかは、壊れた TSV を直す方法が順序立てて説明しています——まず文字コード、次に構造、文字は最後です。
ここでやる場合
このサイトのエディタは、空白を表の中のその位置に描きます。ですから末尾の半角スペースは、推測するものではなく、目で見えるものになります。見えない文字は正確なコードポイントとともに報告され、セルごとに数えられます。セル情報パネルの件数と検査パネルの件数は同じ関数から来ています。「見えない」の定義が 2 つあれば、いずれ食い違います。それが起きてはいけない機能があるとすれば、これです。
種類ごとに別々の指摘として並び、それぞれをファイル全体に対して、取り消し 1 ステップに収まる形で適用できます。試してみる代償はありません。全角スペースはエラーではなく補足として報告されます。日本語の文章が入った列では、おそらく正しいからです。
アップロードはありません。ファイルを読み、検査し、書き出しているのは、あなたのブラウザです。