[C#]文字列をバイト単位で切り取る

IMG_0845

簡単なまとめ

ネットで検索したがどのコードも、バグがある、もしくはダサい。Linqで書けば美しい。

string text = "マルチバイト文字";
int size = 10; // 10バイト
Encoding e = System.Text.Encoding.GetEncoding("Shift_JIS");
string result = new String(text.TakeWhile((c, i) => e.GetByteCount(text.Substring(0, i + 1)) <= size).ToArray());

以下余談

IMG_0845

プログラムの世界でも言語の壁というのは大きくて、最近だとUnicodeおよびUTF-8なんて文字セット、エンコーディングがだいぶ浸透してきたので、便利になってきているんだけど、やっぱまだ大きい。

例えば、私はお客さんに「OracleデータベースのエンコーディングはShift-JISとすること」と言われ、「なんで?Unicodeでキャラクタセマンティクスでいいじゃん。お前ら文字数制限とか厳しく指定してくるのにバイト単位で管理しろとかすげー面倒なんだけど」という旨を、ひたすら丁寧な言葉を選びつつ、私めの不勉強で思いつきと言っても過言ではない、あくまでも、ひとつのご意見として参考にしていただけたらと思いますが、みたいな、よくわからん遠回しな表現で言ってみたところ、

「我々のシステムが出力するデータは最終的に官公庁に出る。官公庁では丸囲み数字を利用することが多い。従って本システムでも丸囲み数字などが使えるということが重要なのだ(キリッ)」

ということを言われた。うるせーバカ、いわゆる「機種依存文字」のほとんど全てはUnicodeで定義されてんだよ。と、Unicode表をおでこに貼り付けてやりたがったが、じゃあShift-JISが扱う文字セットがUnicodeのサブセットかと言われると自信が無く、結論として、Shift-JISでバイトセマンティクスとなった。

従って、文字列をあるエンコーディングによるバイト単位で切り取るという処理が必要になった。

ちょっと考えたが、これ、結構面倒くさい処理で、ネットに何か転がってないかな。と検索してみた。すると、文字をまずバイト列にして、目的のバイト数だけ取り出すということをやっているコードがあった。これは良くない。切り出した箇所が、マルチバイト文字の途中だったら文字化けする。

じゃあ文字を一文字ずつ増やして都度バイト数を計算し、バイト数が目的のサイズよりオーバーしたら、一つ前の状態の文字列を出力すればいいねっ!って考えた。事実、そうやってるコードも検索すると結構ヒットしたが、forループがいっぱいあってダサい。forループがいっぱいあるとC#なのに昭和の薫り(C言語の薫り)がする。

もういい!LINQでやるもん!

string text = "マルチバイト文字";
int size = 10; // 10バイト
Encoding e = System.Text.Encoding.GetEncoding("Shift_JIS");
string result = new String(text.TakeWhile((c, i) => e.GetByteCount(text.Substring(0, i + 1)) <= size).ToArray());

やってることはforループと変わらないけど、すっきりでしょう?Fluentなinterfaceが素敵でしょう?

たった2,3行書くのが面倒でネットで検索しまくった後、時間だけ浪費して結局考えて書いたほうが早かったという典型例でした。