CODE Polyglot / 入門 / 第15回

先頭だけ出す — プレビューのために文字数で切る

文字列の先頭から決まった文字数だけを出し、残りがあれば「…」を付けます。問い合わせの一覧で本文の先頭だけを見せる処理です。絵文字や「𠮷」のような文字の途中で切ってしまう言語があります。

画面にこう出れば正解

どの言語で書いても、同じ入力なら画面に出る文字は1文字も違わない、というのが正解の決め方です(例は Python で動かしたときのコマンドです)。

基本

$ python3 truncate.py こんにちは世界 5
こんにちは…

ちょうど

$ python3 truncate.py こんにちは 5
こんにちは

短い

$ python3 truncate.py abc 5
abc

英数字

$ python3 truncate.py "Hello, World" 5
Hello…

絵文字

$ python3 truncate.py 🍣🍺🍜🍙 2
🍣🍺…

めずらしい漢字

$ python3 truncate.py 𠮷野家の牛丼 2
𠮷野…

絵文字の手前で切る

$ python3 truncate.py 今日は🍣 3
今日は…

0文字

$ python3 truncate.py こんにちは 0
…

10言語のプログラム

どれもそのまま保存して動かせます。毎朝、上の正解の例をすべて実際に動かして確かめています。

truncate.php8.4.26 で 8 / 8 件の出力が正解と一致

<?php
// 文字列の先頭から決まった文字数だけを出し、残りがあれば「…」を付ける
$s = $argv[1];
$n = (int) $argv[2];

// strlen・substr はバイトで数えるので、文字の途中で切れる。/u を付けた preg_split で1文字ずつの配列に直す
// (mb_substr は mbstring 拡張が無いと使えない)
$chars = preg_split('//u', $s, -1, PREG_SPLIT_NO_EMPTY);
if (count($chars) > $n) {
    echo implode('', array_slice($chars, 0, $n)) . "…\n";
} else {
    echo $s . "\n";
}

動かし方(ファイルを保存したフォルダで) ・ PHP を入れる

$ php truncate.php こんにちは世界 5

言語でつまずきやすいところ

初心者がよく書く形(各言語で最初に習う書き方をそのまま使ったもの)を、同じ正解表で動かした結果です。言語を出した答えごとにまとめ、赤い字はその言語が実際に出した間違った行です。

入力初心者がよく書く形の答え(10言語)
基本こんにちは世界 5
  • PHP・Goこ�…
  • Rustエラーで止まった
  • Python・JavaScript・Java・C#・Ruby・Kotlin・Swift✓ 正解
ちょうどこんにちは 5
  • PHP・Goこ�…
  • Rustエラーで止まった
  • Python・JavaScript・Java・C#・Ruby・Kotlin・Swift✓ 正解
短いabc 5

✓10言語すべて正解

英数字"Hello, World" 5

✓10言語すべて正解

絵文字🍣🍺🍜🍙 2
  • PHP・Go�…
  • JavaScript・Java・C#・Kotlin🍣…
  • Rustエラーで止まった
  • Python・Ruby・Swift✓ 正解
めずらしい漢字𠮷野家の牛丼 2
  • PHP・Go�…
  • JavaScript・Java・C#・Kotlin𠮷…
  • Rustエラーで止まった
  • Python・Ruby・Swift✓ 正解
絵文字の手前で切る今日は🍣 3
  • PHP・Go・Rust今…
  • Python・JavaScript・Java・C#・Ruby・Kotlin・Swift✓ 正解
0文字こんにちは 0

✓10言語すべて正解

この回でやること

  • 文字列の先頭から、決まった文字数だけを取り出す
  • 残りがあるときだけ「…」を付ける

この回の決まり

  • 1文字は、Unicode の文字1つ(コードポイント)で数える。「🍣」も「𠮷」も1文字
  • 文字数を超えたときだけ、最後に「…」を付ける。ちょうどなら付けない

解説

「文字数」の数え方は、言語によって違う

第4回 で見たとおり、「文字数」の数え方は言語によって違います。バイト数で数える言語、UTF-16 の単位で数える言語、Unicode の文字で数える言語があります。

先頭を切り出すときも同じです。各言語のいちばん普通の書き方で試した結果です。

書き方数える単位「こんにちは世界」の先頭5文字「🍣🍺🍜🍙」の先頭2文字
正解文字こんにちは…🍣🍺…
Python スライス・Ruby [0, n]・Swift prefix文字こんにちは…🍣🍺…
JavaScript slice・Java/Kotlin substring・C# SubstringUTF-16こんにちは…🍣…(1文字少ない)
PHP substr・Go s[:n]バイトこ�…(文字の途中で切れて壊れる)�…
Rust &s[..n]バイト止まる(文字の境目でないとエラー)止まる

UTF-16 で数える言語では、絵文字や「𠮷」が2つ分に数えられます。この回の入力ではちょうど絵文字の切れ目で切れたので、1文字少なくなるだけで済みました。切る位置が奇数なら、絵文字の半分だけが残った壊れた文字列になります。

文字の単位で切る

文字列を、いったん「文字の並び」(配列)に直してから先頭を取るか、文字の単位で数える関数を使います。

なお、「👨‍👩‍👧」のように、いくつもの文字を組み合わせて1つに見える絵文字もあります。これを「1文字」として扱うには、さらに別の数え方(書記素クラスタ)が要ります。この回では扱いません。

練習

  1. 「…」を付けた結果が、必ず決まった文字数に収まるようにしてみましょう(「…」も1文字と数える)
  2. 文字数ではなく、画面の幅(全角は2、半角は1)で切ってみましょう