今日もエンジニにゃー♪なブログ

(主に)Rubyを書いてる猫です

Rubyで文字コードを扱ってて四苦八苦したのでまとめてみた

TL;DR

文字コードってUTF-8で統一すれば良いんでしょ?くらいの認識しか持っていなかったWebエンジニアマンが、実際にちゃんと文字コードについて学びまとめてみた話

この記事ってどんな人が対象なの?

B2B系の仕事に関わる人は得になるかもしれません(の割にはJavaではなくRubyの話ですが)。
理由としては、仕事をしているとクライアントの使っているOSの状況などで、様々な文字コードに対応することがあるかと思います。私の前職はC2Cで、ユーザが直接打ち込んでくるものに対応することが多かったので、SQLインジェクションやそれに対するエスケープ処理などを気にかけていました。しかし、文字コードに関してなにかしら注意を払うといったことはありませんでした(更に言えば、私は社内の管理画面なども手がけていたので、より気にする必要がなかった)。

そもそも文字コードって

UTF-8
Shift_JIS
ASCⅡ
EUC
みたいなやつらです。

文字はどうやって表現されるのか

バイト列 + エンコーディング情報 = 文字表現
すなわち、あるバイト列は文字コードに関する情報を持つことで初めて文字として表示されるということ。バイト列が文字コードと対応していれば、それにあった文字が表示されます。

Rubyの文字列の扱いをバイト列から探ってみる

Rubyには String#bメソッドとString#bytesメソッドがある。それぞれの役目についてまとめると
String#b ... ASCII-8BITの文字列の複製を返す
String#bytes ... 整数値(10進数)で文字列中のバイトを繰り返し取り出す

このふたつのメソッドを使って実際に文字列を解体してみる。

>> a = 'あ'
=> "あ"
>> a.encoding
=> #<Encoding:UTF-8>
>> a.b
=> "\xE3\x81\x82"
>> a.b.encoding
=> #<Encoding:ASCII-8BIT>

この \xE3\x81\x82 は文字エンコーディングASCII-8BIになる。
ところで、ASCII-8BIとはなんだろう。ASCⅡのお仲間なのかな?と思って調べたところこんな記事がヒットした。引用すると

ASCII-8BITは基本的に現実のエンコーディングではなく、任意のバイトストリーム(0から > 255までの値をとるバイト)を表すものであり、生のバイトストリームに用いたり、文字列のエンコーディングが不明であることを明示したりするときに用いられます。

とのこと。上記は日本語に対してString#bメソッドを使っているので、ASCⅡの範囲外である日本語レシーバはバイト列がそのまんま返ってくるよということである。逆に言えば名前が示す通り、ASCⅡの範囲(1文字を7bitで表している)であれば文字を表示することができる。

>> 'hoge'.b
=> "hoge"

ちなみに String#b は16進数で返ってくる。これは人間でも理解しやすいようにしているためである。

>> a.bytes { |b| puts b.to_s(16) }
e3
81
82
=> "あ"

ここで文字コードの対応表を見てみる。
の部分を確認すると E38182 となっている。つまり上のバイト列はUTF-8に対応したバイト列であることが分かる(UTF-8エンコーディング情報をもって初めて意味をなすということ)。この対応したという部分が符号化文字集合ということである。

>> a.b.force_encoding('UTF-8')
=> "あ"

ちなみにString#encodeUTF-8 を指定しても上手くいかない。
String#encodeは現在のエンコーディングが正しいと仮定した上で動作するものだからである。

>> a = 'あ'
=> "あ"
>> a = a.encode('Windows-31J')
=> "\x{82A0}"
>> a.encode('UTF-8')
=> "あ"

一方ASCII-8BITは、

文字列のエンコーディングが不明であることを明示したりするときに用いられます

ということなので、そもそものエンコーディングが不明状態のバイト列になります。 しかし、UTF-8の並びをしているので、String#force_encodingを用いることで を表示することができます。もし結合をしたい場合はString#force_encodingを用いて対処しましょう。

>> a = 'あ'
=> "あ"
>> b = 'あ'.b
=> "\xE3\x81\x82"
>> a + b
Traceback (most recent call last):
        2: from /Users/asadashougo/.rbenv/versions/2.5.0/bin/irb:11:in `<main>'
        1: from (irb):3
Encoding::CompatibilityError (incompatible character encodings: UTF-8 and ASCII-8BIT)
>> a + b.force_encoding('UTF-8')
=> "ああ"

最初から不正なバイト列はどうなるの?

さきほどの話はそもそもバイト列として正しいものになります。
なぜならちゃんと日本語の からバイト列を作ったからです。

>> 'あ'.bytesize
=> 3
>> 'あ'.b.bytesize
=> 3

どちらも3バイトになっています(ちなみにUTF-8は3バイトで日本語を表記しています)。しかし不正なバイト列ならどうでしょう。

>> a = "\x82\xa0"
=> "\x82\xA0"
>> a.encoding
=> #<Encoding:UTF-8>
>> a.bytesize
=> 2
>> a.valid_encoding?
=> false

UTF-8エンコーディングなのに2バイトなので不正なバイト列だと言うことが分かります。
これに対処するにはString#scrubを利用すると不正なバイト列を別の文字に代替することができます。

>> a.scrub
=> "��"
>> a.scrub.valid_encoding?
=> true

参考

学生時代から働いていた会社を辞めて転職しました

TL;DR

タイトル通りなのですが、最近2年半ほどお世話になっていました会社を辞めました。貴重な経験を得られた現場でしたが、綴りたいところはあるので語っていきたいと思います。

そもそも前職で何をやっていた & 私の経歴は

学生時代をあわせ2年半ほどfintech系の事業に携わっていました。自社開発で環境は以下になります。

Admin開発(PHP)

マーケティング担当の方や営業の方が主に使うアドミンの開発を担当していました。開発自体は上司から落ちてくる仕様を実際に作りステージングにリリースし、上司と認識の違いを埋めて最終的に本番にあげるといったものでした。マーケティング担当が実際に使って、その部分を朝会などに利用して貰ったときは嬉しかったです。

ユーザページ保守・機能開発(Laravel, Vue)

いわゆるユーザ詳細ページです。日本側でコードを理解できるひとがいなかったので、一部オフショアではなく私が担当していたり、また私にしか理解できないサービスのコード(これはコードの質の話ではなく、単純に技術の話です。Vue.jsは日本側は私しか保守できていませんでしたし、後述する特殊言語の組み合わせも加味して、オフショア含めても私しかコードを触れていない現状でした)を開発・保守していました。

特殊言語によるシステム開発・保守(ここはちょっと秘密で)

ここはかなり特殊な言語(?)を扱った仕事もしていたので省きます。
現在のユーザー数としてはCOBOLよりもおそらく少ない言語です(JavaとかCとかに近いものです)。

ほかやってたこと、やってきたこと

仕事では、DBはMySQLで対応したり、ORMを利用せず直でSQL書いて叩いたりチューニングしたりとか、会社で使うDocker環境用意したりとかしてました。
学生時代はPython機械学習(テキストマイニングなど)やAPI開発、また卒業間際から趣味でRoRやってたのでRubyの開発してました。SNS作って大学の授業で使ってもらってレビュー貰ったり、このSNSをネタに大学のビジネスコンテストでなんか勝って決勝いっちゃったりしてました。ちなみに世界にこんにちはしてから2年半ほどなので、ド素人がエンジニアのアルバイトを初めた格好になります。

なぜ退職したのか

不満だらけになってしまいますが

  1. エンジニアの環境
  2. 企業体質

で分けたいと思います。

エンジニアの環境

利用するべき技術の議論がされない

技術に関してエンジニアが情熱を持っていませんでした。正確には持っている方もいましたが、例によろしく会社に失望して辞めていきました。
実態として、開発形態は十数年前かと思われるようで生産性は劣悪の状況でした。オフショア先はかろうじて新技術の導入が認められていましたが、向こうの勝手で使っている技術がころころ変わるといった状況で、会社で統制が取れてはいませんでした(本当はコントロールしたかったはずだが、役員は技術的な話ができず良いのか悪いのかさえ理解できない状況だったと思います)。
AWSといったインフラは 勉強したくないだけ 運用コストがかかるということで導入がスルーされ続け、計測・ログ収集も導入していなかったから復旧作業や保守がやばかったり、Webサイトが生のPHPで作られ、しかもHTMLがPHPの文字列としてコンソールされるという 意味がわからない 開発だったので、そもそも閉じタブがちゃんと書かれているのか(PHPの文字列として評価されるのでプラグインも意味がない)、CSSがタブ内に style= 定義で書かれているのもざるで、コードのコピペ使い回しなどアンチパターンが詰められていました。
状況を変えるために会社全体で議論するべき技術の話がされるわけもなく、本来リードするべきエンジニアがそれを放置し続けた結果とも言うべき悪がそこにはありました。

コードレビュー・テストの自動化がされない

エンジニアとして本番環境にあげるのであれば、それはより良いコードであるべきだし、そうありたいと思うのが普通だと私は思っています。ですがそうじゃない会社も中にはあるのも事実です。。
コードレビューは前職では全くなされていませんでした。リポジトリの中身は使い回しのコードで溢れ、少なからず使われていたフレームワークは、どんどん俺流開発が進められコードの品質の善し悪しよりも 動けば正義 を地で行く現場でした。当然リファクタリングもされることはなく、まるで毒とも思えるコードがそこにあり続けました。
テストの自動化もまたなされていませんでした。上司の言い分としては、「どんなにテストコードを書こうとも人の目が通っていないとダメだ。なら書かないほうが良いでしょ。」というものだった。そのためひとつの機能をリリースするだけで、全てのエンジニアがその週ずっと手動テストをするといったこともありました。バグが見つかっては修正して1からやり直す工程を何度見たか分かりません。

チームではなく個人の開発に依存している

仕事はすべて上司の思いつきと脳内計画によって立てられていました。
とは言っても、当然スプリントとして計画は全ての社員の目に通ってはいましたが、技術について詳しくない上司が計画を立てるので大幅な遅延が多発する。またひとつのプロジェクトが、ひとりで1チームのような状況だったので誰が何をしているかも把握はできませんでした。上司が思いついた機能を下請けという社員に振っているような状況だったので、自社開発ではありましたが、自分が請け負ったサービスに対して誇りのようなものはあまり持てませんでした。またみんなで会社を良くしていくという意識自体が社員になかったのも事実です(これは後述する企業体質にも関わってくる部分だと思います)。

ドキュメントを残そうとしない(改善しつつあったが...)

ここら辺はあまり多くを語らなくてもすでに出尽くされた感じはありますが(言うて上述もか)、ドキュメントがほぼありません。そのため人の出入りが激しい会社にありがちだとは思いますが、入社された方にひとつひとつ説明するコストが多発しました。また説明しても一ヶ月で辞めた方もいらっしゃって、この教育コストや転職サイトなどを会社が利用したコストはまったくペイできていない状況でした。さらに言えば、かなり細かく説明しないといけないエンジニアしか来ない状況なので(まぁつまり未経験の未経験みたいな人)、技術者のレベルはますます落ちていく一方だったと思います。

企業体質

改善案を出すと社長に呼び出される

これは完全に実体験なのですが、上記のような問題を改善しようとするともれなく社長直々の面談が行われます(笑)。私は技術ミーティングのときに(ミーティングとは名ばかりのまるで明治時代の国会、つまり実態は役員の協賛を以てといったものだった)、上の問題をちゃんと話し合いしたほうが良いと発言したところ社長が怒りだし(本当です)、後日私は素敵なカフェデート(社長は男ですが)に誘われました(笑)。
そこでは社長に具体的に話してほしいと言われ、「お、ワンチャンなんとかできるかな?」とほんと極わずかに思ったのですが、説明したら途中で遮られ、「君の話は長すぎるよ!そんちゃんじゃ誰も話を聞いてくれないよ!?」と文句を言われ、そこからおそらくは私の倍以上は少なく見積もってもあるだろうという説教をされ、その内容をまとめると「会社に対してなにか改善したいことがあるならまず結果を出せ!結果を出して初めて評価はされるんだ」といったものでした。
実に明快です!結果を出せばちゃんと評価をして意見を聞いてくれると条件を話してくれているんですから!
...例えそれが会社を良くしようとする提案、つまり卵がさきか鶏がさきかという内容に対しても(もし私の提案が上手くいっていたら、こいつは会社を良くした提案をしたんだ!という評価とかもらえていたのでしょうか?その提案をそもそも聞かないから無理だろうけど...)。

社員を自分の道具にしている

これは私が辞めるまえになって色々聞いたことですが、大多数の社員が喧嘩別れをしているようです。やっぱりありがちなのですが、社長が怒り、やめちまえー!といった状況になっているものでした。
社員が反発するのは、社長が「お前らは俺の手足のように動けば良いんだ。俺の考えが正しい!俺の頭脳で導き出された戦略は必ず成功するんだ!実行することだけ考えれば良いんだ!」というのを感じるからだと思います。実際私は起業し会社を運営していくことを凄いことだと考えています。別に法に触れているような会社でもなく、ちゃんと有給もいただきましたし。ただ、社員は道具ではありません。また社員の中には自主的に会社を良くしようとする人も必ずいます。
それなのにその芽を摘み、また会社全体で、社長または役員に(役員も社長の協賛のようなもの)対して意見の提案自体を拒否するかのような雰囲気にするのは会社の寿命を短くするようなものだと思います。結果的に社員自体が自主性を持たなくなるので、変化に弱い、時代錯誤の感性しか持ち合わせない人たちだけで運営をしていくことに危機感を覚えました。

仕事の評価は業務時間の長さ

社員の評価ですが、実質これはどれだけ働いたかの一点のみでした。スプリントをこなすことが評価ではありますが、まず計画自体がざるすぎるので、絶対に終わらないかつ、終わっても終電になるだろといったようなものでした。
一応ほかにも360度評価などはありましたが、上述のような状況ですし、またエンジニアの仕事としての評価項目はなかったので、下にあるような項目に意味があるとは思っていません(余談ですが、役員の評価は軒並み低かったようです。まぁボーナスなんかの影響はなさそうですが。。)。そもそもデザイナーがエンジニアの仕事だったりまた逆もしかりですが、きちんと評価するなんて無理だよ〜〜〜業種の違いあるし結局人間性の評価にしかならないよ〜〜〜〜。

じゃあなんで仕事を続けてたの

ひとえに言えば会社側から色々良くしてもらえたこともあります。

  • 有給をふつうの新卒よりも多く貰えてた
  • fintech事業はなかなか経験できないことなので、面白みを感じた
  • オフショア先と同等の開発環境の選択ができる(はずだった...)

また時間に結構融通を効かせてもらえた面もあって、比較的ストレスフリーな部分も最初はありました。プライベートを優先したことにより、評価されなくなってしまいましたが、貢献できるサービスは作れたし、自分のやりたいことはやれて会社から学ぶことがなくなるまでは頑張れたかなと思います(月100万近く稼げるサービスも作ってほぼ1人で保守できた)。

これからどこに転職するの

会社名を公表するか迷いに迷ったのですが、オフであったり仲の良いフォロワー限定にすることにしました > <
会社からは別に公表しても良いと研修の時点でおっしゃってくださったのですが、やはりまだ入ったばかりだし、上のような結構苛烈な内容を含んでいる記事ですので、少なくとも暫くは穏やかにしていようかと思います。
仕事してはB2Bマーケティング業界に関わる部分の仕事をしていきます。自社開発・サービスです。かなり成長著しい会社また業界だと思っています。技術としては主にRuby(RoR)やNodeを扱ったプロダクトを触っていくことになります。すでに仕事は先月より始めていまして、AWSもがっつり触ってもいますし、周りのエンジニアのレベルも高いので非常に満足しています(むしろ自分のレベルの低さを痛感しています)。

転職先の居心地はどうですか

最高です!!!
とても居心地が良いですし、周りのエンジニアのレベルも高く、また上述した前職の雰囲気なんかはひとつもないです。長く仕事をしていけそうで安心している一方少しでも実力をつけ、チーム・会社に貢献できるように頑張っていかないとなと思っています。

最後にこのブログってなに

技術ブログを主に書いていくつもりですが、日常の思ったこととかまた、趣味の話でも徒然に書いていきたいと思っています。どうぞよろしくお願いします。