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

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

ただいまの
回答率

89.53%

文字コードsjisの変換について

解決済

回答 2

投稿 編集

  • 評価
  • クリップ 0
  • VIEW 1,907

T.FUJII

score 19

前提・実現したいこと

全体の流れ

DBからCSVファイルを出力 → ユーザがそのCSVファイルをエクセルで編集 → DBにCSVファイルを入力

の ”DBにCSVファイルを入力” 部分の質問になります。
このプログラムはartisanコマンドで呼び出され実行されます。
csvを読み込んで配列に格納し、その配列をもとにDBのデータを更新するプログラムです。

発生している問題・エラーメッセージ

下記のエラーが出てしまいました。

SQLSTATE[22001]: String data, right truncated: 1406 Data too long for column 'phone_number' at row 16876

該当のソースコード

該当箇所をvar_dumpで見ると下記のようになっており、

array(11) {
 [0]=>
 string(13) "001r357820094"
 [1]=>
 string(3) "001"
 [2]=>
 string(57) "あいう機構",00-1234-5678""

csvの区切り文字がうまく読まれません。本来は[3]に下記のように入るはずです。

array(10) {
 [0]=>
  string(13) "001r357820094"
 [1]=>
  string(3) "001"
 [2]=>
  string(57) "あいう機構"
 [3]=>
  string(12) "00-1234-5678"

初心者ながら自力で調べたところ0x5c問題というもので、構の文字の2バイト目が¥になり,がエスケープされているということに気が付きました。

しかし読み込まれるcsvの文字コードはsjis(エクセルの保存関係で)であり、それをうまくデータベースに戻さなければならず、どう解決すればいいかわかりません。

またsjisのファイルをsjis-winとしてmb_convert_variablesをかけていいのでしょうか?そうすることでハシゴの高(髙)などの問題は解決できました。しかしそれがやっていいことなのかはわかっていません。

csvの読み込みは、下記のようにSplFileObjectを使い、それをlist関数を使い変数に詰めています。
(変数名$aなどは簡略化して書いています)

public function handle()
{   
    // CSVファイル読み込み
    $filePath = './aaa.csv';
    $file = new SplFileObject($filePath);
    $file->setFlags(SplFileObject::READ_CSV);

    // ファイル内のデータをループ
    foreach ($file as $row) {    
        // 文字コード変換
        mb_convert_variables('utf8', 'sjis-win', $row);

        // list関数を使い変数にデータを格納
        list(
            $a,
            $b,
            $c,
            $d,
            $e,
            $f,
            $g,
            $h,
            $i,
            $j,
            $k
        ) = $row;

その後、DBは下記のようにクエリービルダーを使い必要項目だけアップデートしています。
DBはutf8-unicode-ciでMySQLを使っています。

// CSVから取得したデータをDBに戻す
DB::table('test_database')->where('a', $a)->update([                    
    'b' => $b,
    'c' => $c,
    'updated_at' => date("Y-m-d H:i:s")
]);  

補足情報(言語/FW/ツール等のバージョンなど)

開発環境:Vagrant + VirtualBox / Homestead
言語:php-7.1.14 (NTS)
フレームワーク:Laravel5.5 (LTS)
サーバー:nginx
DB:MySQL

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

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

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

    クリップを取り消します

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

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

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

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

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

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

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

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

    質問の評価を下げる

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

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

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

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

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

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

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

    詳細な説明はこちら

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

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

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

回答 2

checkベストアンサー

0

mb_convert_variables()やmb_convert_encoding()を使ってSJIS-winからUTF-8に変換しないと、
お気づきのように正しく処理できないと思われます。

あと、Excelで加工するとのことなので、
例えばCSVでなくxlsやxlsxをphpから読み書きする方法もありますね、
PHPExcel(保守されてないので非推奨)とかPHPSpreadsheetとか。
PHPExcelの後継っぽいPhpSpreadsheetでExcelファイルを読み書きするサンプルコード - ariarijp’s blog
PhpSpreadsheetを試してみる(テンプレート読み込み・セルの結合・スタイル設定・CSV出力) - Qiita

投稿

編集

  • 回答の評価を上げる

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

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

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

  • 回答の評価を下げる

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

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

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

  • 2018/02/22 10:05 編集

    m6u様 ありがとうございます。
    確かにExcelファイルでやる方がいい気がします。
    参考にさせていただきます。
    今今自分なりに解決方法はどれも無理やりな感じです。
    ①DBにデータをインサートする時点で0x5c文字はエスケープする
    ②ユーザにはエクセル以外のcsvエディタでutf8として編集してもらう
    ③エクセルでcsvを編集する場合は下記手順でutf8にしてもらう
    https://goo.gl/NKX8sV

    キャンセル

0

       // (略)
            if(isset($row[2])) {
                $company_name = $row[2];
                # 電話番号検索
                $flg = false;
                # 異常値から電話番号を抽出 ["あいう機構",03-5555-6666""]
                if(preg_match('/([0-9]{2,4}-[0-9]{2,4}-[0-9]{3,4})/', $company_name, $match)) {
                    $phone_number = $match[0];
                    # 異常値から電話番号の削除 ["あいう機構",""]
                    $company_name = str_replace($phone_number, '', $company_name);
                    # 異常値から\,\を削除 ["あいう機構"]
                    $company_name = str_replace("\",\"", '', $company_name);
                    $flg = true;
                }
            };
            if(isset($row[3])) {
                if($flg) {
                    $mail_address = $row[3];
                } else {
                    $phone_number = $row[3];
                }
            };
            // (略)


ごり押しかもしれませんが、list関数を止め上記のようにして解決しました。

投稿

  • 回答の評価を上げる

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

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

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

  • 回答の評価を下げる

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

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

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

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

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