Pistolfly のすべての投稿

Software Engineer in Tokyo, Japan

UTF-8の4バイト文字のバリデーション

MySQLの文字セットがutf8の場合、utf-8で符号化すると4バイトになる文字(😁のような絵文字など)をセットすると、SQLモード(sql_mode)が厳密モード(STRICT_ALL_TABLES または STRICT_TRANS_TABLES のいずれかが有効)でない場合、その文字以降が切り捨てられてしまう。(警告は発生する)

4バイトUTF-8文字に対応するためには、CHARACTER SET に utf8mb4(COLLATE に utf8mb4_unicode_520_ci など utf8mb4_xxx) を指定したカラムを使用し、接続文字セットも utf8mb4 を使用する必要がある。

ちなみにRailsでは、MySQLのカラムの文字セットがutf8の場合に4バイトUTF-8文字をセットしようとすると、以下のようなエラーが発生するので、気付かないうちに文字列が切り捨てられてしまうことはない。

An ActiveRecord::StatementInvalid occurred in news#update:

Mysql2::Error: Incorrect string value: '\xF0\x9F\x98\x80\x0D\x0A' for column 'description' at row 1: UPDATE `news` SET `description` = '😀\r\n' WHERE `news`.`id` = 2
app/controllers/news_controller.rb:98:in `update'

これは特に指定していない場合、AbstractMysqlAdapter#configure_connection で、STRICT_ALL_TABLES がセッションのSQL_MODEに追加されているから。(NO_AUTO_VALUE_ON_ZERO も追加される。)

これは以下のようにして確認できる。

  • mysqlクライアントで確認
    mysql> show variables like 'sql_mode';
    +---------------+------------------------+
    | Variable_name | Value                  |
    +---------------+------------------------+
    | sql_mode      | NO_ENGINE_SUBSTITUTION |
    +---------------+------------------------+
    1 row in set (0.00 sec)
    
  • 同じデータベースに対して、rails consoleで確認
    > con = ActiveRecord::Base.connection
    > con.select_all("SHOW VARIABLES LIKE 'sql_mode'")
       (0.8ms)  SHOW VARIABLES LIKE 'sql_mode'
     => #<ActiveRecord::Result:0x00007fc6ca533728 @columns=["Variable_name", "Value"], @rows=[["sql_mode", "NO_AUTO_VALUE_ON_ZERO,STRICT_ALL_TABLES,NO_ENGINE_SUBSTITUTION"]], @hash_rows=nil, @column_types={}>
    

対処方法

カラムの CHARACTER SET を utf8mb4 、COLLATE を utf8mb4_xxx に変換して、接続文字セットに utf8mb4 を使用すればよいが、何らかの事情でカラムを utf8mb4 に変換できない場合は、黙って4バイトUTF-8文字以降が切り捨てられるといろいろとまずいので、4バイトのUTF-8文字をバリデーションではじくことになるだろう。

UTF-8にエンコードすると4バイトになるUnicode文字の範囲は、U+10000からU+10FFFFである。

PHPでの例

if (preg_match('/[\x{10000}-\x{10FFFF}]/u', $s) { /* ... */ }
if (preg_match('/[\xF0-\xF7][\x80-\xBF][\x80-\xBF][\x80-\xBF]/', $s)) { /* ... */ }
preg_match_all('/[\x{10000}-\x{10FFFF}]/u', $s, $matches);
// $matches[0]に4バイトutf-8の文字の配列が格納される。

Rubyでの例

if /[\u{10000}-\u{10FFFF}]/ =~ s
  # ...
end
chars = s.scan(/[\u{10000}-\u{10FFFF}]/)
# charsに4バイトutf-8の文字の配列が格納される。

Windowsでファイルのタイムスタンプを変更する

PowerShellでSet-ItemPropertyを実行する。

更新日時を変更する

> Set-ItemProperty "<ファイルのパス>" -Name LastWriteTime -Value "<日時>"

作成日時を変更する

> Set-ItemProperty "<ファイルのパス>" -Name CreationTime -Value "<日時>"

-Valueに指定する日時のフォーマットは、"2018/06/01 12:27:59" など、.NETで解析できる標準の日時文字列であればよいようだ。

Emacsでカーソル位置の文字コードを表示する

C-x = (M-x what-cursor-position)

ミニバッファに、カーソル位置の文字のUnicodeにおけるコードポイントが表示される。
例えば、Shift_JISのファイルの「あ」という文字にカーソルを置いて、C-x = すると、ミニバッファに以下が表示される。

Char: あ (12354, #o30102, #x3042, file ...) point=1 of 2 (0%) column=0

12354, #o30102, #x3042 の部分が、「あ」のUnicodeにおけるコードポイントの10進数、8進数、16進数表記となる。

C-u C-x = (M-x describe-char)

カーソル位置の文字の詳細情報を分割ウィンドウに表示する。
例えば、Shift_JISのファイルの「あ」という文字にカーソルを置いて、C-u C-x = すると、分割ウィンドウに以下が表示される。

             position: 1 of 2 (0%), column: 0
            character: あ (displayed as あ) (codepoint 12354, #o30102, #x3042)
    preferred charset: japanese-jisx0208 (JISX0208.1983/1990 Japanese Kanji: ISO-IR-87)
code point in charset: 0x2422
               script: kana
               syntax: w  which means: word
             category: .:Base, H:2-byte Hiragana, L:Left-to-right (strong), c:Chinese, h:Korean, j:\
Japanese, |:line breakable
             to input: type "C-x 8 RET 3042" or "C-x 8 RET HIRAGANA LETTER A"
          buffer code: #xE3 #x81 #x82
            file code: #x82 #xA0 (encoded by coding system japanese-shift-jis-dos)
              display: terminal code #xE3 #x81 #x82
  • code point in charset: 0x2422 が、文字集合JISX0208における「あ」のコードポイント、
  • buffer code: #xE3 #x81 #x82 が、バッファ(UTF-8)でのエンコーディング、
  • file code: #x82 #xA0 が、ファイル(Shift_JIS)でのエンコーディング

を表している。

CentOS6にRuby-2.5.0をインストール

CentOS6にRuby-2.5.0をソースからインストールしようとすると、makeでエラーが発生してインストールできない。
CentOS6のgccが古いためにエラーになっている。

$ ./configure --prefix=/opt/ruby-2.5.0 --disable-install-doc
$ make
...(略)
prelude.c: In function ‘prelude_eval’:
prelude.c:204: error: #pragma GCC diagnostic not allowed inside functions
prelude.c:205: error: #pragma GCC diagnostic not allowed inside functions
prelude.c:221: error: #pragma GCC diagnostic not allowed inside functions
トップレベル:
cc1: 警告: unrecognized command line option "-Wno-self-assign"
cc1: 警告: unrecognized command line option "-Wno-constant-logical-operand"
cc1: 警告: unrecognized command line option "-Wno-parentheses-equality"
cc1: 警告: unrecognized command line option "-Wno-tautological-compare"
make: *** [prelude.o] エラー 1

Bug #14234: Failed to build on CentOS 6.9 - Ruby trunk - Ruby Issue Tracking System

Ruby-2.5.1では修正されるようだが、とりあえずsclのdevtoolsetを使ってインストールできる。

CentOS6にsclのdevtoolsetをインストール

/etc/profile.dに以下のようなファイルを作成しておいてシステムを再起動すれば、システム起動時からsclでインストールしたdevtoolsetが有効になるので、passengerのインストールやCapistranoでビルドが必要なgemのインストールもOK。(下記はdevtoolset-4の場合の例)

$ cat /etc/profile.d/enabledevtoolset-4.sh
#!/bin/bash
source scl_source enable devtoolset-4

macOS(10.12 Sierra以降)でログを見る

概要

`log`を使う。

リアルタイムで見るなら
`log stream`
過去のログを検索するなら
`log show`

cron

$ log stream --info --predicate 'process == "cron"'
$ log show --info --predicate 'process == "cron"' --start '2017-05-25'

postfix

$ log stream --info --predicate '(process == "smtp") || (process == "smtpd")'
$ log show --info --predicate '(process == "smtp") || (process == "smtpd")' --start '2017-05-25'

指定するprocess名が分からない場合は

$ log show --info --start '2017-11-08' | grep 'xxx'

などとして、process名の当たりをつけて、predicateに指定するといいだろう。

ブラウザの全画面表示

Windows

  • IE, Chrome
    F11
  • Edge
    Shift + Win + Enter

Mac

  • Safari, Chrome
    Ctrl + Command + F
    タブやアドレスバー等のツールバーが表示される場合は、メニューの「全画面表示でツールバーを常に表示する」をオフにする。(または Shift + Command + F)
  • Firefox
    アドレスバーを非表示にできないようだ。

iOS, Android

  • HTMLのmetaタグで、apple-mobile-web-app-capable を yes に設定しておく。
    <meta name="apple-mobile-web-app-capable" content="yes" />
    
  • ページを「ホーム画面に追加」してホーム画面のアイコンから開く。

スワップ領域の作成

# dd if=/dev/zero of=/swapfile bs=1M count=2048
2048+0 レコード入力
2048+0 レコード出力
2147483648 バイト (2.1 GB) コピーされました、 22.799 秒、 94.2 MB/秒
# mkswap /swapfile
スワップ空間バージョン1を設定します、サイズ = 2097148 KiB
ラベルはありません, UUID=xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
# swapon /swapfile
swapon: /swapfile: 安全でない権限 0644 を持ちます。 0600 がお勧めです。
# chmod 600 /swapfile

/etc/fstab

/swapfile none swap defaults 0 0

確認

# swapon -s
Filename				Type		Size	Used	Priority
/swapfile                              	file	2097148	0	-1

スワップ領域のサイズを変更する場合は、

# swapoff /swapfile

してから /swapfile を削除し、上記手順で再作成すればよい。