読者です 読者をやめる 読者になる 読者になる

たいちょーの雑記

ぼくが3日に一度くらい雑記をかくところ

競プロとかのテストケースを自動化したい

蜘蛛ってこわい

家蜘蛛っていいやつなのは知ってるけどデカいと怖い

テストケースを自動化したい

いまさら競プロを始めた隊長です
いまはABCを中心にやっています

基本的に
問題読む

コード書く(長考)
↓  ↑
問題のページに書いてあるテストケースを試す

提出

って流れですけどこの テストケースを試すとこが正直ダルい
そう思いませんか!?

なのでVisualStudioの単体テスト機能を使ってこれを自動化したいんですけど、ggってもあんまり出てこないので書きました。

自動化する

これをテストしたぃょ・・・

public class Calc{
    public void Solve(){
        var item=int.Parse(Console.ReadLine());
        Console.WriteLine(item);
        return;
    }
}

適当なテキストファイルにテストケースの入力と出力を一行開けて並べるだけで全部チェックしてほしぃょ・・・

123

123

456

456

↑のテキストファイルをtestcase.txtという名前でプロジェクトに追加して、以下の単体テストを実行すると・・・????

using Microsoft.VisualStudio.TestTools.UnitTesting;
using System;
using System.Text;
using System.IO;

namespace CS_AtCoder.Tests {
    [TestClass()]
    public class CalcTests {
        private static bool[] active = new bool[4] { false, false, false, false };
        private static StringBuilder[] expected = new StringBuilder[4];
        private static StringBuilder[] Stdin = new StringBuilder[4];
        private const string testcasePath = @"..\..\test_case.txt";
        Calc Target = new Calc();
        [ClassInitialize()]
        public static void ClassInit(TestContext tc) {
            int cnt = 0;
            string s;
            StreamReader sr = new StreamReader(testcasePath);
            while (sr.Peek() >= 0 && cnt < 4) {
                var sb = new StringBuilder();
                while ((s = sr.ReadLine()) != "" && sr.Peek() >= 0) {
                    sb.Append(s);
                }
                Stdin[cnt] = sb;
                StringBuilder expect = new StringBuilder();
                if (sr.Peek()<0) break;
                while (sr.Peek() >= 0 && (s = sr.ReadLine()) != "") {
                    expect.Append(s);
                }
                expected[cnt] = expect;
                active[cnt] = true;
                cnt++;
            }
            sr.Close();
        }

        private Tuple<StringWriter, StringReader, StringBuilder> MakeRedirect(int index) {
            var sb = new StringBuilder();
            var sr = new StringReader(Stdin[index].ToString());
            var sw = new StringWriter(sb);
            Console.SetIn(sr);
            Console.SetOut(sw);
            return new Tuple<StringWriter, StringReader, StringBuilder>(sw, sr, sb);
        }

        //↓と同じようなのを好きなだけ並べる
        [TestMethod]
        public void Case0() {
            int index = 0;
            if (!active[index]) { Assert.IsTrue(true);return; }
            var item = MakeRedirect(index);
            Target.Solve();
            item.Item1.Close();
            item.Item2.Close();
            var actual = item.Item3.ToString().Replace("\r\n", "");
            Assert.AreEqual(expected[index].ToString(), actual);
        }
    }
}

unittest1

やったあぁぁぁ

やってること

やってることはただ単にStringBuilderに入力と出力を格納し、入力の方をStringReaderとしてstdinに割り当てる。
もう一つStringBuilderを用意し、それをStringWriterとしてstdoutに割り当てる。これをほしい出力を格納したStringBuilderAssert.AreEqualしてあげるだけです。

ちなみにテストケースは5つ以上できません。
まあでもだいたい5つもないしな~みたいなw
余ったテストメソッドはIsTrueでOKだします

もう少しいい書き方もあるだろうけど、これで動くのでこれでいいです。 テストケースをコピペするのもなんか自動化できないかな~とか思いますけどまぁこれでいいです。

よーし!めっちゃはかどりそうです!

続・世界一わかりやすい絶対パス

マスクしてると眠たくなる

解る?

改良した

ちょいと前に書いた記事でこういうのがありました
xztaityozx.hatenablog.com

この記事で公開したシェルスクリプトはちょっと アレ だったのでシェル芸っぽく書きなおしました

#!/bin/bash

dir=${1:-`pwd`}
dir=`echo $dir|sed 's/\/$//'`

length=$(echo $dir|sed -e 's@[^/]@@g'|wc -c)

echo $dir|
sed 's/\///;s/\//\n/g'|
awk -v end=$length '
  BEGIN{
    idx=0
  }
  {
    if(idx==end-2){
      print "\\\\e[1;33m/"$1
    }else if(idx<=1){
      print "\\\\e[1;30m/"$1
    }else if(idx>=end-4){
      print "\\\\e[1;34m/"$1
    }else if(idx==2){
      print "\\\\e[1;30m/.."
    }
    idx++
  }
'|
while read LINE;do
  echo -en $LINE
done
echo -e "\e[39m"

出来るだけ配列と変数を削ってパイプでつなげました。

ついでに使いやすさを考えて$1が空ならpwdを使うようにしておきました
処理としてはsedで切り分けてawkでテキスト整形してwhile read LINEで連結しただけですね
動作速度とかはどうなんだろ・・・まぁ普通に使う分には全然問題ないですね
awkってホント何でもできて怖いなって思いました

見た目とかは前回の記事と同じになっているはずです
どちらかというとこっちのが安定してるので書き換えてよかったですね

それではよい 絶対パスライフを!

AAの画像作ってくれるSlackBotつくった

Pythonむずい

勉強が足りない

AAは面白いが崩れる

AAって面白いですよね。

dic.nicovideo.jp

でも2chの専ブラとかでないと本来の見た目でなかったり、そもそも崩壊していたり・・・。

slackbot1
ブーム君のAA

Slackでもそれは例外でないです。
この間某Slackで「いよいよ明日は~」というコピペをもじった告知がありました。
slackbot2
↑こういうやつです。

画像の通り、返信には「今日と明日だよ・・・」と返すのがそれっぽいと思うんですが・・・(押しつけ)
これをそのままSlackに張り付けると
slackbot3
こんなふうにちょっとずれます。
これはこのままでも見れないこともないですけど、大きなAAになるとめちゃめちゃになります。
特にスマホとかだと確定でめちゃめちゃになります。
slackbot4
めちゃめちゃになったAAの例

AAは モナーフォント で見せるようになっている(?)ので普通のフォントでは崩れるのは当然なのですが・・・

IPAモナーフォント

崩れてしまうと何がなんだか分からずに白けてしまうので何とかしたい・・・
しかし全員にこのフォントにするように言うのはなんか違いますよね。

文字列がダメなら画像にしちゃう

画像にAAを張り付けてUpすれば全員が同じ崩れていないAAを見ることができます。
でも普通の人はそんな手間なことしませんし、そんなことしてたら会話の流れが過ぎ去っていきます。オタクの会話はスピードがはやいですから。

ここはSlackBot AAGenBot とでも名付けて、コイツにAAを画像化するのをやってもらいましょう。
slackbot5
AAGenBotのイメージ1

こうすればAAをコピペするだけでAAがずれることなくみんな見れます (実際はテキストが張り付けられるのでタイムラインがガーッと流れたりしますが・・・)

他にも特定の発言に対してそれっぽいAAを張り付ける機能も欲しいです。
slackbot6
AAGenBotのイメージ2

こうすればいちいちAAをコピペせずともBotが勝手にAAを貼ってくれます(邪魔だという意見もありそうです)

つくってワクワク

使った言語はPythonBashシェルスクリプトです。
SlackAPIを使ってメッセージの送受信と画像の生成をするのがPythonのお仕事で、それっぽいAAの画像を探したり、AA用のテキストファイルを用意したりするのをBashシェルスクリプトでやります。
Pythonはやったことなかったのでワクワクしてます

だいたいの流れはこうです
slackbot7

SlackAPIの利用にはpython-slackclientを使いました
github.com
Slackのサイトからテキトーにクリックしたら見つかったのでこれにしました(頭わるい感)
Pythonで画像を生成するにはPillowを使いました
https://pillow.readthedocs.io/en/4.1.x/pillow.readthedocs.io
なんかPILとか呼ばれてたやつ?っていうかそれのforkらしい?(詳しく調べてない)

slackbot8

つくります

できました

github.com
色々説明は面倒なのでこれをみてね

py/SlackMessageGetter.pyでメッセージを受信(送信もするけどね)して機能分け
py/aagen.pyでAAを生成 sh/search.shsh/aaImageSearcher.shで発言に合いそうな画像をさがします。二つに分けたのはsh/search.shの方をパイプに挟みたかったからです。 このsh/search.sh形態素解析とか結構謎深い検索方法をしているので興味があれば見てみてください。
sh/FileUploader.shcurl使って画像をアップロードします。なぜか前述のpython-slackclientではうpできませんでした・・・

またsh/FileUploader.shではある程度Botの画像うpを自重するようにしてます。

  1. 30秒以内に画像をうpしてたら自重
  2. 候補の画像を30分以内にうpしてたら自重

これで自重できてるのかは・・・わからん・・・な

インストールのしかた

必要なものをapt-getとかpip3します。python3がなければそれもいれてね

# mecab  ファイルサーチにつかっちゃったw
sudo apt-get install mecab
# もとPIL
sudo pip3 install Pillow
# Slackのライブラリ
sudo pip3 slackclient
  • メモ:普段あんまり使わなさそうなコマンドとか
  • jpegtopnm
  • pnmquant
  • ppmtosixel
  • これらは生成した画像をターミナルに表示するのに使っています。Sixelに対応しているターミナルでないとダメなのでいらない場合はsh/aaTextRedirector.shの25行目をコメントアウトしてOKです

普通にgit cloneします

git clone https://github.com/xztaityozx/AAGenBot
cd AAGenBot

AAGenBotはシェル変数からTokenやユーザ名、フォントへのパスを読み取るのでexportします

export SLACK_API_TOKEN="xxxx-xxxxxxxxxxxxxxxxxxxxxxxx" #APIトークン
export AAGENBOT_NAME="<@Uxxxxxxxxxx>" #Uから始まるBotのID?名前?みたいなやつ、<@ >で囲むこと
export AAGENBOT_MASTER_NAME="Uyyyyyyyyyy" #Uから始まる自分の名前?ID?。Botを管理する人の名前?ID?
export monafontPath="/home/user/.fonts/ipagp-mona.ttf" #AA作るときに使うフォント 絶対パスで書かないと見つからない?

~/.bashrcとかに書いておくと楽ちんです

シェルスクリプトに実行権限がなかったらchmodしておきます。

chmod +x sh/*

これだけだよー(^o^)/

起動方法

AAGenBotディレクトリ以下で

sh/Starter.sh

とするだけだよー(^o^)/

つかいかた

使いたいチャンネルにAAGenBotを参加させます。

生成

AAGenBotに対して生成してほしいAAの名前とAA本体を発言します
フォーマットは

@aagenbot [AAの名前]
[AA本体]

とします
slackbot9

こんなふうに結果がうpされます

ちなみにAAの名前はそのままファイル名に使われるので*/が入ると癇癪というかエラーを起こします。

呼び出し

AAGenBotに対して呼び出したい画像の名前をcallキーワードの後に付けて言うとうpしてくれます
slackbot10
こんな感じですね。
名前が間違ってるとうんともすんとも言いません(不親切)

発言に反応

これはAAGenBotに特に何か言うということはないです。
ユーザー同士の会話の中を勝手に読み取って適度にAAを張り付けます。
slackbot11
こういった感じですね

slackbot14
こんなふうに複雑な文章でもそれっぽいのを見つけてきます。
これはなんかアルゴリズムを使ったとかじゃないです。シェル芸を使ってます。(sh/search.shを見ればわかると思います。)
前述のとおり 自重システム がが備わっているので会話の流れが汚染されることは無いと思います()

終了

AAGenBotのMASTER(Botを管理するやつ)限定でSlackからAAGenBotを終了できます。
デバッグ用だったんですけど残しました。
slackbot12
こうです。煽ってるわけではないです

まとめ

Pythonはじめてでしたけどなかなか楽しかったです。またなんか作りたいですね・・・
最初は全部Pythonでやるつもりだったんですが
シェルスクリプトのが楽やわ!w
ってなったところはシェルスクリプトにしました。その結果Pythonシェルスクリプトが交互する意味の解らない構造になりましたがまあいいでしょう

まだ某Slack内で使ってないのでどれほど実用に堪えるのかわからないのですが、書く記事もちょうどなかったので書きました。
まだ生成失敗したときとか呼び出し失敗とか、エラーをユーザー達に伝えるような部分できてないのでその辺をもうちょっとだけメンテしてから某Slack内で公開したいです。どきどきする・・・

ちなみにこのAAGenBot副作用があります。
slackbot13
ALLFilesのところがめちゃめちゃになる

それでは良い AAライフ を!

参考

既知のバグ

  • 画像をUpしたあともう一度sh/FileUploader.shが呼ばれる。1行目からエラーが出て終わる
  • 下の方がはみ出る。謎

果物も食えないこんな世の中じゃ

ポイズン

最近はコタツを付けると熱いが消すと寒いので、雨が降ってほしいと願うばかりである。花粉も落ちるし。

更新をサボってたわけじゃないですよ。記事を推敲しまくってたんです(嘘)

果物を食うとだいたいのどが痒くなる

これわかる人いますか。他にもモヤシや火の通ってない豆腐などでも喉がかゆ~くなるやつです。
(僕の場合ですが)具体的に言うと のどちんこ のすぐ手前のあたり 軟口蓋 周辺がモーレツに痒くなります。(いまモーレツ宇宙海賊が予測変換に出てきて懐かしくなりました)

モヤシの場合は少しややこしくて、太めのモヤシ(緑豆や大豆モヤシ)はダメですが細めのモヤシ(黒豆モヤシ)はOKなんです。
他には高野豆腐もダメですね。
生の果物だけかと思ったら実は結構いろはすももでも来てるんですよね・・・
irohasu

だいたい花粉症のせい

僕は白樺の花粉症持ちなんですが、どうやらコイツがダメっぽいですね。
www.newsdigest.de

さらにブタクサもOUTなのでポピュラーな果物はほぼ死滅して、食えるのはブドウ、ミカンぐらいです。(バナナはなんかOK)
火を通せばだいたいのモノが食えるようになりますが・・・

わざわざ火なんか通してられるか!

という話です

かといって

かまわん!食ってやる!!

と食ってしまえば当然痒くなるので泣く泣く我慢しています。

しかし果物がダメなのはわかりましたが、大豆系がダメなのがよくわからなかったのでggりました
allabout.co.jp

どうやらここによると僕は クラス2の食物アレルギニスト のようですね  そしてその2ページ目

花粉症との関係についてですが、シラカンバ、ハンノキなどのカバノキ科の植物の花粉には、PR-10というアレルゲンタンパクが含まれています。このPR-10 と似たアレルゲンが大豆にも含まれているため(大豆の アレルゲンタンパクは「Glym4」)に、カバノキ科の花粉症患者の中には、豆乳等を飲んだ時にアレルギーを発症するケースが見られるのです。

Ah…
なるほど納得です(納豆は大丈夫)

モモが食べたい

僕の場合は 食べられへん!!! というほどではないので食べることはできますが
無理に食うことで 、これが悪化してしまうと今度はまだ発症していない食べ物にも反応してしまうかもしれない ので控えています。ジャガイモなんかが発症したらえらいことですからね

花粉症がある人は一度ggってこういうのがないか確認してみた方がいいかと思います。

!!????!??????! !!?????!??!!?? !!?????!?!?!!! !!????!????!!! !!?????!!!!!?? !!?????!?!?!!! !!????!????!!! !!?????!!!!!?? !!?????!?!!??! !!????!???!?!!

!!????!!?!!!?? !!????!?!??!?? !!????!?!!!??! !!????!!!?!!?! !!????!?!??!?? !!????!!??!??! !!?????!??!!?? !!?????!!!!?!! !!?????!?!?!!! !!?????!???!??

!???!?!????!!??? !??!!!?!???!?!! !!?????!!?!??? !!?????!??!?!! !???!?!?!?!?!!?! !!?????!!!!!!? !!?????!?!!?!! !!?????!?!!!!! !!?????!???!??

!!?????!?!??!! !!?????!!?!!!? !!??!?!!????!!! !!!!?!?!!!????? !!?????!??!!?? !???!?!?!?!?!!?! !!????!??????! !!?????!!??!!? !!?????!???!?? !!????!???!?!! !!?????!!?!??? !!?????!???!?? !!?????!???!!? !!?????!?!??!! !!?????!!?!??? !!?????!!?!!!!

!!?????!?!??!! !!?????!!?!!!? !!??!?!!????!!! !!!!?!?!!!????? !!?????!??!!?? !???!?!?!?!?!!?! !!????!??????! !!?????!!??!!? !!?????!???!?? !!????!???!?!! !!?????!!?!??? !!?????!???!?? !!?????!???!!? !!?????!?!??!! !!?????!!?!??? !!?????!!?!!!! !!???????????! !?!?!?????!!?!! !!?????!!?!!!! !!?????!!?!??? !!?????!!???!! !!?????!!??!!? !!????!?????!? !!??!!?!????!!! !!?????!!?!?!? !!????!??!??!! !!?????!!????? !!?????!!?!!?! !!!!!!!!???!!!!!

!!????!?????!? !!?????!!????! !!????!???!!?! !!????!??!??!! !!???????????! !!?????!???!?? !!?????!!!!!!? !!?????!?!??!! !!????!???!!?? !!????!??!??!? !!??!!?!!!!!??? !!?????!???!?? !!?????!!??!!? !!?????!???!?? !!????!???!?!! !?!????!!?!?!?! !!????!?????!? !!??!!?!????!!! !??!!!?!?!!!?!? !!?????!!?!?!? !!????!??!??!! !!?????!!????? !!?????!?!???! !!?????!!?!??! !!?????!!?!!?!

!!?????!!!?!!? !!?????!!???!! !!?????!!????! !!????!?????!! !!?????!?!???! !??!!!!?!?!?!?! !!!?!?!?!!?!?!? !!!???!?!??!!!? !!?????!?!!??? !!?????!!????? !!????!???!??? !!?????!!???!! !!?????!!??!!? !!????!!??!!?! !!????!?!!!!!! !!?????!!??!!! !!?????!?!!??! !!?????!??!!?? !!?????!!?!!?!

!!?????!?!??!! !!????!??!??!! !!?????!!?!?!? !!?????!?!??!! !!?????!!?!??? !!????!??!??!? !!?????!?!??!! !!?????!?!??!! !!?????!!?!?!! !!??!!?!!!!!??? !!?????!??!!!! !!?????!!?!!!? !!?????!!?!!!! !!?????!!?!?!? !!????!??!??!! !!?????!!????? !!?????!?!???! !!?????!!?!??! !!???????????! !!?????!?!??!! !!?????!!?!!!? !???!?!????!!??? !??!!!?!???!?!! !!????!??!??!? !???!?!?!?!?!!?! !!????!??????? !!?????!!?!?!! !!?????!!?!!!! !??!!!?!!!??!?! !??!!!?????!?!! !!?????!!?!!!? !!????!?!!?!!! !!????!?!??!!! !!????!!!?!?!! !?????!?!?!!!??? !!?????!??!!?? !!??!!!????!??! !?!??!?!?!!!??! !!?????!!????? !!????!???!???

!!??!?! !!???!! !!?!??? !!?!!!! !????? !???!? !!????!?????!? !!?????!?!!??? !!????!???!!?? !!?????!!??!?? !???!? !!!!!?? !!!??!! !!??!?! !!??!?? !????? !??!!! !!!??!! !?!!!! !!???! !?!!!! !????! !?!!!! !!??!!! !!!?!! !!!??!! !?!!!! !!???? !?!!!! !!!!!! !?!!!! !!??!!! !!!?!! !!!??!! !?!!!! !????? !?!!!! !?!!!?? !!?!!!? !?!!!! !!??!!! !??!!! !!!!!?? !!????! !!!?!!! !!?!?!! !????? !??!!! !!!!?!! !!!???? !!!??!? !!?!??! !!?!!!? !!!?!?? !????? !???!? !!?!??! !!???!? !!????! !!!??!! !!??!?! !!!!?! !!??!? !!!?!! !???!? !??!?? !!???! !!!!!?! !??!!! !!!!!?? !!???!? !!???!! !!!!!?? !!????! !!!?!!! !!?!?!! !????? !??!!! !!!!!?? !!!???? !!!??!? !!?!??! !!?!!!? !!!?!?? !!??!!? !????? !???!? !??!?! !!???!! !???!? !?!!?? !??!?? !!???! !!!?!! !!??!?! !!?!??? !!???!! !!?!!!!

!!?????!????!? !!?????!!?!??? !!?????!!?!!!! !?????! !?!??!! !????!! !??!??! !??!??! !!?????!!?!?!! !?!!??!????!??! !!???!!!!?!!?!! !!?????!?!!??! !!????!???!?!! !!?????!!????? !!?????!?!???! !!?????!!??!!! !!?????!?!!??! !!?????!!?!!?! !!!!!!!!???????!

!??!!!!?!?!?!?! !!?????!!?!!!? !!?????!?!!!!! !!????!??????! !!?????!!?!!!? !???!?!????!!??? !??!!!?!???!?!! !!?????!!?!?!? !!?????!!?!!!? !!!!!!!!???!!!!!

!!????!?????!? !!?????!??!!!! !!?????!!??!!? !!?????!??!!?! !!?????!!?!!!! !!?????!!?!??? !!?????!??!!!! !!?????!!?!?!! !!?????!!?!?!? !!?????!???!?? !!?????!!??!!! !!?????!?!!??! !!????!???!??? !!????!!!!!?!! !!????!!!!!?!! !!????!!!!!?!! !!!!!!!!???????! !!?????!?!?!!! !!?????!???!?? !!?????!!??!!? !???!?!????????? !!?????!???!!? !!?????!!?!?!? !!????!???!??! !!?????!!!???! !!?????!!???!! !!?????!!?!??? !???!??!!???!?!! !!?????!!??!!! !?!!!!!???!?!?! !!?????!???!?? !!?????!!??!!? !!?????!!!!?!! !!?????!?!?!!! !!?????!??!?!! !!?????!!???!! !!?????!?!!!!! !!?????!!????? !!?????!?!???! !!?????!!??!!! !!?????!?!!??! !!?????!!?!!?! !!!!!!!!???????!

!!?????!?!!!?! !!????!???!!?! !!?????!?!!!?! !!????!???!!?! !??????!!!!?!?!? !?!??!??????!!? !!?????!!??!!! !!????!?????!? !??!!!!?!?!?!?! !!?????!??!?!! !!?????!???!?? !!?????!!??!!? !!????!???!?!! !!?????!??!?!! !!????!???!!!! !!?????!??!?!! !!????!???!??! !!?????!!?!?!? !!?????!??!!!! !!?????!!?!?!? !!?????!!???!! !!?????!!??!!? !!?????!??!!?! !!?????!?!!!!! !!?????!!?!!!? !!?????!!??!!! !!?????!?!??!! !!?????!!?!!!? !???!!!!!?!!!?!? !!?????!!??!!! !!!!!?!?!????!? !!????!???!!!! !!????!???!?!? !!?????!!!!!!? !!?????!?!!??!

!!?????!?!!!?! !!????!???!!?? !!?????!!??!!! !!?????!!?!!!! !?????!??!!?!!!! !!?????!???!?? !??!!!?!???!!?? !??!??????!!??!? !!??!?!?!!!???? !!????!!!?!??! !!????!?!??!?? !!????!!?!?!?! !!????!??!??!? !!!!!!!!???????!

!?!??!!!!????!? !?????????????!!

!?????! !?!??!! !????!! !??!??! !??!??! !!????!?!!??!! !!????!!!!!!?? !!????!!??!??! !?!!??!????!??! !!???!!!!?!!?!! !!?!?!??!?!!!!!

たのしい拡張メソッド

肘の痛みが取れない

たぶんしぬ

VSがないといきていけない

VisualStudio2017がリリースされましたね。 www.visualstudio.com
自分はUnityとかC#でなんかするときはエディターにVSCommunityを使ってますので新バージョンのリリースはわくわくもんです。

ぼくはインテリセンスにどっぷり浸かってしまっているので他のではできないですね・・・おそるべし

インストーラーの段階でUnityのツールが同時にインスコできたりするので楽になりましたね。

何よりUIがかっこいいのよね

拡張メソッドたのしい

前回の記事でちょいと書いた拡張メソッドなんですけど、あれからそれにはまってしまいました。
拡張メソッドにハマるってなんだよ って思うと思うんですけど、拡張メソッドばかり書いて肝心の作品ができてないってことです。沼ですね。

最近ではメソッドチェーンとか三項演算子を見ていると

纏めて一つの拡張メソッドにしたい

というわけのわからない衝動がわくようになりました。
例えば

str = str.Length < limit ? str : str.Substring(0,limit);

こういうのがあれば

public static string Truncate(this string s,int boader) {
    if (s.Length < boader) return s;
    return s.Substring(0, boader);
}

こうして

str = str.Truncate(limit);

こうしたくなるんです!可読性もいいし精神的に良いでしょ?変な変数も増えないし?補完も変数名かいて.打てばインテリセンスがしてくれるし

だからなんか作るのが止まらないです。おかげできれいにはなったんじゃないかな・・・・・・
最近は文字列処理系が多くて前回の記事にあったDocking()は派生が結構できちゃってたのしくなっちゃう

public static string RangeDocking<T>(this IEnumerable<T> list, int start, int range, Func<T, string> filter = null) {
    string str = "";
    T[] array = list.ToArray();
    for (int i = start; i < range; i++) {
        str += (filter != null ? filter(array[i]) : array[i] + "");
    }
    return str;
}

コレクションのここからここまで連結したい!なんていうのは日常茶飯事だし、デリゲート渡せばちょいとした加工もしながら連結できるし気に入ってる一つです。
ほかにも個人的に気に入ってるのがこちら

[Conditional("UNITY_EDITOR")]
public static void UnityDebugLog(this object s) {
    UnityEngine.Debug.Log("UnityDebugLog value = " + s);
}

てきとうに値が見たいだけなのにDebug.Log()打つのが面倒だったので拡張しちゃったほしみ
単純だけどすごくべんり・・・とけちゃう

文字列の後ろから何文字か削除とかいうメソッドも(たぶん)ないし、いちいちSubstringするのたるいし

public static string RemoveRangeFromLast(this string s,int range) {
    if (range <= 0) return s;
    return s.Substring(0, s.Length - range);
}

自分しか使わないので需要とかパフォーマンスも気にする必要ないし

/// <summary>
/// T1tT2のIEnumerableからUniRx.Tupleのリストを生成して返す罪深いメソッド
/// </summary>
public static List<Tuple<T1, T2>> MakeTupleList<T1, T2>(this IEnumerable<T1> list, IEnumerable<T2> combination) {
    List<Tuple<T1, T2>> rt = new List<Tuple<T1, T2>>();
    var listRange = list.Count() > combination.Count() ? combination.Count() : list.Count();
    var array_list = list.ToArray();
    var array_combi = combination.ToArray();
    for (int i = 0; i < listRange; i++) {
        rt.Add(new Tuple<T1, T2>(array_list[i], array_combi[i]));
    }
    return rt;
}

インデックスが範囲外になっちゃうの気にするのが楽になるし

/// <summary>
/// 定義域を超えないようにしてくれる心優しいメソッド
/// </summary>
public static int AutoDomain(this int value, int _min, int _max) {
    return value < _min ? _min : value > _max ? _max : value;
}

三項演算子りすぎてわけわからんくなった

さて作業にもどりますかね

UnityのTextでシンタックスハイライトしたい

半袖半ズボンにはまだ早い

さむい

今回はちょっと雑記

シンタックスハイライトしたい

シンタックスハイライト(英: Syntax highlighting)とは、テキストエディタの機能であり、テキスト中の一部分をその分類ごとに異なる色やフォントで表示するものである。シンタックスカラーリング(英: Syntax coloring)とも。 Wikipedia -シンタックスハイライト

したい

まずは手法をググってみたんですが、パーサかいたりするのとかツール使うのとかあるみたいですね

でも大体はテキストマッチングみたいですね・・・

普通にテキストマッチング

リッチテキスト形式を使って色付けするのでテキストマッチングはむしろ良い手泣きがします。

早速書くんですがその前に練習がてら3つほど拡張メソッド書きました。

public static string[] Split(this string s,char[] separator,bool containSeparator) {
    List<string> rt = new List<string>();
    rt.Add("");
    int index = 0;
    for(int i = 0; i < s.Length; i++) {
        var item = s[i];
        int _sepaIndex = Array.IndexOf(separator, item);
        if (_sepaIndex != -1 && containSeparator) {
            rt.Add(separator[_sepaIndex]+"");
            rt.Add("");
            index += 2;
        } else {
            rt[index] += s[i];
        }
    }
    return rt.ToArray();
}
public static string Docking(this string[] s) {
    var length = s.Length;
    string str = "";
    for (int i = 0; i < length; i++) {
        str += s[i];
    }
    return str;
}
public static string ToRichTextColor(this string s, Color col) 
    => "<color=#" + ColorUtility.ToHtmlStringRGB(col) + ">" + s + "</color>";

Splitオーバーロードですね。既にあるSplitだとセパレーターに指定した文字列を失ってしまうので作りました。
セパレーターが1種類なら連結するときに指定すればいい話なんですが、今回は複数個あったので作りました。
Dockingstring.Joinに似たやつってかそのものですがJoinを使わなかったのはメソッドチェーンに組み込めな・・・いとおもうから(あいまい)
ToRichTextColorはそのままリッチテキスト形式に書き換えるだけですね

Dockingの方は使う予定がないけどもう一つ書きました

public static string Docking<T>(this IEnumerable<T> list,Func<T,string> toStringer=null) {
    string rt = "";
    foreach (var item in list) {
        if (toStringer != null) {
            rt += toStringer(item);
        } else {
            rt += item.ToString();
        }
    }
    return rt;
}

list.ForEach(a => str += a);とかするのちょっとカッコ悪く感じてたのでいいでしょう。うん まぁJoinでいいんですが・・・

それらを組み合わせてシンタックスハイライトするのがこちら

//テキスト更新
TX_OneLine.text = user_inputString
                    .Split(separator, true)
                    .Select(splited =>
                    {
                        if (separator.Any(___=>___.ToString()==splited)) {
                            return splited.ToRichTextColor(color_separator);
                        } else if (syntaxArray.Any(___ => splited == ___)) {
                            return splited.ToRichTextColor(color_syntax);
                        }
                        return splited;
                    }).ToArray().Docking();

シンタックスしたいのは大した量でないのでこれでいいでしょう

それでは良いシンタックスハイライトを!