質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

ただいまの
回答率

87.35%

PHPで文字コードをチェックする関数

解決済

回答 4

投稿

  • 評価
  • クリップ 1
  • VIEW 9,121

score 72

ある本を参考にして、与えられた文字列の文字コードが有効なものかをboolで返す関数を作りました。そこで3点質問があります。

function check(array $data){
  $result = true;

  foreach ($data as $key => $value) {
    if(is_array($value)){
      $value = implode("", $value);
    }
    if(!mb_check_encoding($value)){
      $result = false;
      break;
    }
  }
  return $result;
}


(1)引数が配列型であるのは、文字列を一つずつチェックするためだと思いますが、一度に一つの文字列をチェックする方法はないのでしょうか?

(2)関数内にあるmb_check_encodingですが、マニュアルを見ると指定するエンコーディング(ここではutf-8)が第二引数で必要なようですが、何も指定せずに動作するのはなぜでしょうか?

(3)utf-8でない文字列を代入した$jisで、以下のように実行しているのですが、check()の引数に[$jis]と[]がついている理由を教えてください。配列にキャストしているのでしょうか?

$word = "こんにちは";
$jis = mb_convert_encoding($word, 'Shift-JIS');

if(check([$jis])){
  echo "TRUE";
}else{
  echo "FALSE";
}

つまり上記は

$word = "こんにちは";
$jis = mb_convert_encoding($word, 'Shift-JIS');
$jis = str_split($jis);

if(check($jis)){
  echo "TRUE";
}else{
  echo "FALSE";
}

に同じでしょうか?

質問が多くてすみません。一つでもお答えしていただけると幸いです。

どうかよろしくお願いします。

  • 気になる質問をクリップする

    クリップした質問は、後からいつでもマイページで確認できます。

    またクリップした質問に回答があった際、通知やメールを受け取ることができます。

    クリップを取り消します

  • 良い質問の評価を上げる

    以下のような質問は評価を上げましょう

    • 質問内容が明確
    • 自分も答えを知りたい
    • 質問者以外のユーザにも役立つ

    評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。

    質問の評価を上げたことを取り消します

  • 評価を下げられる数の上限に達しました

    評価を下げることができません

    • 1日5回まで評価を下げられます
    • 1日に1ユーザに対して2回まで評価を下げられます

    質問の評価を下げる

    teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。

    • プログラミングに関係のない質問
    • やってほしいことだけを記載した丸投げの質問
    • 問題・課題が含まれていない質問
    • 意図的に内容が抹消された質問
    • 過去に投稿した質問と同じ内容の質問
    • 広告と受け取られるような投稿

    評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。

    質問の評価を下げたことを取り消します

    この機能は開放されていません

    評価を下げる条件を満たしてません

    評価を下げる理由を選択してください

    詳細な説明はこちら

    上記に当てはまらず、質問内容が明確になっていない質問には「情報の追加・修正依頼」機能からコメントをしてください。

    質問の評価を下げる機能の利用条件

    この機能を利用するためには、以下の事項を行う必要があります。

回答 4

+3

(1)引数が配列型であるのは、文字列を一つずつチェックするためだと思いますが、一度に一つの文字列をチェックする方法はないのでしょうか?

check複数の文字列について一気にチェックを掛ける関数です。そもそも1個であれば、このようなヘルパー関数を使わずとも、直接mb_check_encodingを呼ぶか(3)のように、「1個だけの配列」にすれば片付きます。

(2)関数内にあるmb_check_encodingですが、マニュアルを見ると指定するエンコーディング(ここではutf-8)が第二引数で必要なようですが、何も指定せずに動作するのはなぜでしょうか?

エンコードを指定しない場合、mb_internal_encoding()の設定を参照することになりますが、そのやり方はおすすめできません。UTF-8を明記した方がいいです。

(3)utf-8でない文字列を代入した$jisで、以下のように実行しているのですが

これは(1)で触れたように、配列しか受け付けない関数なので、1個でも配列にして代入している、というだけです。なお、日本語などマルチバイトのコードをstr_splitにかけると、バイト単位で処理する都合上文字の途中でぶった切られる危険性がありますので、やってはいけません。

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2017/08/29 18:41

    ご回答ありがとうございます!
    (1)については、例えば "abc" という文字列を配列にキャストしてcheck()に渡すと、check関数では
    Array ( [0] => abc )と複数の文字列を1つの配列インデックス[0]に格納するという意味ですね。
    ( [0]=>a [1]=>b...となるのではなく)
    文字列を一文字ずつ取り出しているのだと勘違いしていました。

    (2)に関しては、マニュアルの string $encoding = mb_internal_encoding() とは引数がないときの初期値を意味していたのですね。念のためmb_check_encoding($value, "utf-8")としておきます。

    (3)について、mbがついていないオプションはマルチバイトに対応していない可能性が高いのですね。使うときはマニュアルを見てみます。

    キャンセル

checkベストアンサー

+2

(1)引数が配列型であるのは、文字列を一つずつチェックするためだと思いますが、一度に一つの文字列をチェックする方法はないのでしょうか?

書いてある通り、「一度に一つの」文字列をチェックするなら、引数の型をstringにして、foreachを削除すれば良いでしょう。

function check(string $value){
  $result = true;

  if(!mb_check_encoding($value)){
    $result = false;
    break;
  }
  return $result;
}

また、mb_check_encoding自体がbooleanを返すので、下記のようにすることもできます。
(もはや何のために関数でラップしているのか意味不明ですが)

function check(string $value){
    return mb_check_encoding($value);
}

「一度に複数の」の書き間違いでしたら、可変長引数を使ってcheck($str1, $str2, $str3...)のように呼び出すこともできます。
PHPのバージョンによって書き方が違うので、詳しくは公式ドキュメントをご覧ください。
http://php.net/manual/ja/functions.arguments.php

(2)関数内にあるmb_check_encodingですが、マニュアルを見ると指定するエンコーディング(ここではutf-8)が第二引数で必要なようですが、何も指定せずに動作するのはなぜでしょうか?

内部文字エンコーディングを使っています。内部文字エンコーディングは下記の方法で調べることができます。

echo mb_internal_encoding();

(3)utf-8でない文字列を代入した$jisで、以下のように実行しているのですが、check()の引数に[$jis]と[]がついている理由を教えてください。配列にキャストしているのでしょうか?

配列にキャストしています。
str_split($jis)は全角文字に対応していませんので、下記のような恐ろしいことになります。

var_export(str_split('こんにちは'));

array (
  0 => '?',
  1 => '?',
  2 => '?',
  3 => '?',
  4 => '?',
  5 => '?',
  6 => '?',
  7 => '?',
  8 => '?',
  9 => '?',
  10 => '?',
  11 => '?',
  12 => '?',
  13 => '?',
  14 => '?',
)

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2017/08/29 18:48

    ご回答ありがとうございます。

    おっしゃる通り、

    function check(string $value){
    return mb_check_encoding($value);
    }

    このほうが簡単だと思うのですが、なぜ私のコード(本に書いてあった)はややこしく書いてあるのでしょうか?

    文字コードをチェックする対象は基本「文字列」ですよね?わざわざ配列にキャストしてからcheck()に渡さないといけないし面倒な気がします。配列でも文字列でも一つの関数でチェックできるようにしているだけでしょうか?

    キャンセル

  • 2017/08/29 18:55

    > 配列でも文字列でも一つの関数でチェックできるようにしているだけでしょうか?
    その通りです。一つの文字列をチェックするだけなら、単にmb_check_encoding($string)で出来ます(新たに関数を作る必要はありません)。

    キャンセル

  • 2017/09/01 01:44

    ありがとうございました。

    キャンセル

+2

(1)引数が配列型であるのは、文字列を一つずつチェックするためだと思いますが、一度に一つの文字列をチェックする方法はないのでしょうか? 

文字列を1文字ずつの配列にしたものを受け取る、と勘違いされてませんか?

文字列の配列を受け取りそれを1つずつチェックする、1つでも不正な物があったら打ち切り、という実装になってます。Webアプリケーションなどで、渡された複数のパラメータを一度にチェックするためのコードでしょう。

(2)関数内にあるmb_check_encodingですが、マニュアルを見ると指定するエンコーディング(ここではutf-8)が第二引数で必要なようですが、何も指定せずに動作するのはなぜでしょうか? 

マニュアルを見ると第二引数を省略した場合はmb_internal_encoding()が渡されることになっています。

http://php.net/manual/ja/function.mb-check-encoding.php

(3)utf-8でない文字列を代入した$jisで、以下のように実行しているのですが、check()の引数に[$jis]と[]がついている理由を教えてください。配列にキャストしているのでしょうか? 

上に書いたとおり、複数の文字列を配列として受け取りチェックするコードですので、1つの文字列をチェックするにも配列として渡す必要があります。1要素の配列として渡しているコードで、1文字ずつに分解される訳ではありません。

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

+2

とりあえず(2)に関して。
PHP: mb_check_encoding - Manual
第2引数を省略すると、代わりにmb_internal_encoding()の結果を使うので問題なし。

(3)に関して。
check()関数の引数が配列を前提としているので、
$jis単体では渡せないから配列をその場で作るために
if(check(array($jis))){って書くか、
if(check([$jis])){って書くことになった、
ということではないかと。
配列の初期化(の文法)に関しては
PHP: 配列 - Manual
を参照。

(1)に関して。
引数が配列かどうかを関数の冒頭で判断して、
配列のときの動作と文字列単体のときの動作を組むようにすれば良いですね。
例えばis_array()を使えば配列かどうかの判定が出来ます。
でもまぁ、中身としてはmb_check_encoding()での判定を
foreach()で回しているだけで、
その場面での必要な設計をしただけで実践では使わないんじゃないかなと察します。

投稿

編集

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

15分調べてもわからないことは、teratailで質問しよう!

  • ただいまの回答率 87.35%
  • 質問をまとめることで、思考を整理して素早く解決
  • テンプレート機能で、簡単に質問をまとめられる

関連した質問

同じタグがついた質問を見る