くりーむわーかー

プログラムとか。作ってて ・試しててハマった事など。誰かのお役に立てば幸いかと。 その他、いろいろエトセトラ。。。

C#

.net MVCで「roslyn\csc.exe」のエラー

最近、どこからか.netMVCをCloneしてビルドすると↓のエラーが出た。

'bin\roslyn\csc.exe' パスの一部が見つかりませんでした

で、これが出たら↓

パッケージマネージャコンソール(ツール⇒Nugetあたり)

update-package Microsoft.CodeDom.Providers.DotNetCompilerPlatform -r

csc自体はc#のコンパイラだったと思うんだけど、これが見つからないってこと?

ちょっとなぞ。

むかーしむかし、IEのDLLホスティングっていうのがありまして、

そん時に確か、何かのDLL使おうとした時にcsc.exeがねーってエラーが出た記憶があるなー。

だから何だって話ですけど。

参考サイト↓

https://kotaeta.com/62165347

C#でちょっと「ほや?」って思った事

大した話じゃないんですが、下記ソース。

public void hoge()
{
    //プリミティブ
    int t = 1;
    int tt = t;
    Console.WriteLine("ref={0}", Object.ReferenceEquals(t, tt));//⇒False
    //オブジェクト
    Object o = new object();
    Object oo = o;
    Console.WriteLine("ref={0}", Object.ReferenceEquals(o, oo));//⇒True
    //string
    string s = "s";
    string ss = s;
    Console.WriteLine("ref={0}", Object.ReferenceEquals(s, ss));//⇒True
    //オブジェクトの中のプリミティブ
    SampleValue x = new SampleValue();
    SampleValue xx = x;
    Console.WriteLine("ref={0}", Object.ReferenceEquals(x, xx));//⇒True
    Console.WriteLine("ref={0}", Object.ReferenceEquals(x.v, xx.v));//⇒False!!!
}
public class SampleValue
{
    public int v = 1;
}

オブジェクトは参照渡しだから、ReferenceEqualsがTrueになるのは分かるんだけど、

中のプリミティブな奴ってFalseになるんですね。

そうなのかーーーと思って。

なんか、すんませんでした。。。

最近のプログラミング言語 流行り廃り(StackOverFlowのアンケート)

Googleトレンドで遊んでて、最近の言語はどんな感じなのかなって見てた。だいぶ偏見によった検索ワードですが、下記。

sample01

意外だったのがPythonがJava抜いてるじゃんってとこ。ついにJavaも終焉か・・・って事にはならないと思いますが、私的にはJavaは10年後にレガシーとか言われてそうな気がしている。

昔はPythonとかニッチ(死語?)な言語だと思ってましたが、割と来てるんだね。

最近どうなのか気になったので、StackOverFlowのアンケート結果(2018)を確認してみた。

アンケートの前提

回答:100,000人

地域的なとこ↓

sample02

インドとアメリカとヨーロッパが多そう?

仕事柄↓

03

フロントエンド・バックエンド・フルスタックがほとんどだけど、正直SEなんて、よっぽど特化してなければ、ほぼ3つのどれかにあてはまるよね。。。

あと、回答者のうち半分くらいは5年未満の経験者(仕事として)って事みたい。

若めの回答者が多いっていう認識でよろしいのかな?

良く使う言語

04

良く使うフレームワーク

05

JavaScriptを一番使うらしい。まー、今はほぼWeb系でしょうし。フロントエンドの人はそうなるでしょうね。フレームワークもJavaScript寄りのものが順当に上位。

個人的には.net Coreが割と上位に来てて意外だった。Spring抜いてんだね。C#好きとしてはなんかうれしい。VS2003あたりの.net(正確にはVisualStudio)はクソだと思ったけど、2008あたりから急激に良くなりましたよね。MSがOSSに寄ってきた感じがちょっとした。

まー、C#はVisualStudioありきの言語なのは否めませんが、それでも気持ちよく書けるのが素晴らしいと思う。半分はVSが良いって感じかもかも。

あと、Pythonもやっぱ来てるんだね。ほぼ機械学習・AI・データマイニング絡みだと思いますが、Pythonも書きやすくて好き。

今後数年はPythonとC#がバックエンド的には来るのかしらね。フロントエンドはJavaScript一択でしょうけど。

使ってるDB

06

あれ、SQLServerって上位なん?Oracleってどうしたの?MariaDBには頑張ってほしい。期待上げ。PostgreSQLも上位にあがってるのね。つか、DBランキングとちょっと違う感じがする。↓DBランキング。

07

Oracleって実際どうなんでしょうね。あと、MongoDBはジワジワ来てますな。何系で使われてるんだろ。業務システムって基本RDB使うと思うから、Webサービス的なやつで使ってんのかしら?スキーマレスなDBは業務システム的なデータ構造でガチガチのシステムは微妙な気がするしの。検索のパフォーマンスとかもろもろ。IDで抜くとかだけならMongoの方がパフォーマンスは良さそうなのかしら。いちをNo検証でイメージだけで書いてますので。。。

必要な言語

08

Python大人気すな。AI・機械学習まわりでしょうけども。3年後にはどうなっているかしらね。

使ってるIDE

09

VisualStudioCodeつよ。とゆーか自分もVSCode使ってますね。何か、軽くて使い勝手がよい。ただ、2年前かな?もっと前?に出たばっかで細かいツールというかプラグインが揃いきってないかもかも。でも十分。

所感

やっとJavaは終わったね。.netには頑張ってほしい。C#好き。

で、Pythonがひとまず伸びそうな感じ?今使ってる最中だから人が増えて便利なライブラリとかツールとか増えるとうれしい。まー自分で作れって話ですけど。

意外にもDBはMongoDBが来てるのね。多少は来ると思ってたけど、こんなに来てるとは思ってなかった。Webサービス的なシステムには向いてるのかな。業務システムではちょっと厳しそうな気がする。とりあえずロックが割とゆるかった記憶が。。。

まー、日本で仕事してる身としては、そもそもアンケートに日本からの回答があまり入ってないので世界的にはそんな感じかーという程度。日本てガラパゴスってよく言われるじゃない。世界的な基準のアンケートだと身の回りには割と当てはまらなかったりするしね。

参考サイト

https://insights.stackoverflow.com/survey/2018/

https://db-engines.com/en/ranking_trend

C#からRを使う

この前、Rを外部から実行するっていうの書いたけど、C#から呼ぶためのライブラリが普通にあったので、書き直し。探し方が悪かったかしらね。。。

R.Netっていうのを使う。NuGetからインスコ。「R.NET.Community」っていうのもあるんだけど、何が違うんだろうか?リンク先とかも一緒なんだけど。。。あー、もちろん使う場合は端末にR本体がインストールされてる必要あり。

そしたらとりあえず、呼び出しサンプル。

using RDotNet;
static void Main(string[] args)
{
    REngine.SetEnvironmentVariables();//これは省略可。
    using (REngine engine = REngine.GetInstance())
    {
        engine.Evaluate("sample.data <- iris");
        engine.Evaluate("str(sample.data)");
    }
    Console.WriteLine("おしまい");
    Console.ReadLine();
}

↑は呼び方的には公式のチュートリアルのまま。

まず、32bitか64bitでビルド変えないとだめかも。64bitでやる場合はプロジェクトのプロパティ⇒ビルドで「32ビットを優先」のチェックを外さないとだめそうですね。あと、使うR.dllのフォルダも設定が必要。さらに、Rのパスを通してない場合は、実行時に環境変数のPATHにRのパスを追加しておいた方が良さげ。で、色々したのが↓。パスを編集するメソッドはさすがのすたっくおーばーふろーがサンプル。

static void Main(string[] args)
{
    string rpath = SetupPath("R-3.4.2");//インストールしたバージョンを指定
    REngine.SetEnvironmentVariables(rpath);
    using (REngine engine = REngine.GetInstance())
    {
        engine.Initialize();//最近はこれがいるらしい
        engine.AutoPrint = false;//これ付けないと代入してる式で中身が丸ごと出てきてうざい
        engine.Evaluate("sample.data <- iris");
        engine.Evaluate("str(sample.data)");
    }
    Console.WriteLine("おしまい");
    Console.ReadLine();
}
public static string SetupPath(string Rversion)
{
    string rDllPath = Environment.Is64BitProcess ?
                    string.Format(@"C:\Program Files\R\{0}\bin\x64", Rversion) :
                    string.Format(@"C:\Program Files\R\{0}\bin\i386", Rversion);
    //DLLのフォルダが無かったら例外
    if (!Directory.Exists(rDllPath))
    {
        throw new DirectoryNotFoundException(string.Format("Not found : {0}", rDllPath));
    }   
    var newPath = string.Format("{0}{1}{2}", rDllPath, System.IO.Path.PathSeparator, Environment.GetEnvironmentVariable("PATH"));
    Environment.SetEnvironmentVariable("PATH", newPath);
    return rDllPath;
}

でだ、ここまでやっても何故だか実行時に「Rlapack.dllがありません」みたいなエラーが出る端末が稀にいる。意味が分からん。どう見てもあるんだけどね。。。プローブ?がおかしいのだろうか。そういう時はとりあえず、実行ファイルと同じフォルダに「Rlapack.dll」をRのインストール先からコピーしてあげると動くようになる。なんだろね。

ほいで、あとは色々な呼び出し。

static void Main(string[] args)
{
    string rpath = SetupPath("R-3.4.2");
    REngine.SetEnvironmentVariables(rpath);

    using (REngine engine = REngine.GetInstance())
    {
        engine.Initialize();
        engine.AutoPrint = false;

        ////////////////////////////////////////
        //arulesでアソシエーション分析
        ////////////////////////////////////////

        //ライブラリの読み込み
        engine.Evaluate("library(arules)");
        //アソシエーション分析
        engine.Evaluate("data(\"Adult\")");
        engine.Evaluate("rules <- apriori(Adult,parameter=list(minlen=2, maxlen=4, support=0.5, confidence=0.9))");
        //結果のサマリー
        engine.Evaluate("summary(rules)");
        //結果の表示
        engine.Evaluate("inspect(rules)");
        //RのデータフレームをC#で扱えるデータにする
        var dd = engine.Evaluate("Adult@itemInfo").AsDataFrame();
        //つまみ食い
        Console.WriteLine(dd[1, 1]);


        ////////////////////////////////////////
        //kmeansの結果を取得
        ////////////////////////////////////////
        //irisデータ使う(いつものやつ)
        engine.Evaluate("iriswk <- iris[1:150, 1:4]");
        engine.Evaluate("iriswk.km <- kmeans(iriswk, 2)");
        engine.Evaluate("iriswk.km");
        engine.Evaluate("iriswk.km$cluster");
        var kmlist = engine.Evaluate("iriswk.km$cluster").AsNumeric();

        ////////////////////////////////////////
        //Plotの画像をファイルにはく
        ////////////////////////////////////////
        var path = Path.Combine(Path.GetTempPath(), Path.GetTempFileName() + ".png");
        string Code = @"plot(1:10, pch=1:10, col=1:10, cex=seq(1, 2, length=10))";
        engine.Evaluate(string.Format("png('{0}', {1}, {2})", path.Replace('\\', '/'), 420, 420));
        engine.Evaluate(Code);
        engine.Evaluate("dev.off()");
        
    }
    Console.WriteLine("おしまい");
    Console.ReadLine();

}

C# NMecabでユーザ辞書を使う

最近、またMecab使ってるんですが、Mecabはそのまま使うと凄く細かく分解されちゃって結構使いにくい。形態素解析したい文章である程度の単語のまとまりのままにしたい場合はユーザー辞書作るのが手っ取り早い。

C#でNMecab使う場合を想定。NMecabはNugetでインストール。

辞書作るときは、本家のサイトからWindows用をダウンロードしてインスト。

そしたら↓の感じのCSVで登録した単語の一覧を作る。例えば「chacha.csv」っていうファイル名で保存した場合。

ジャスミン茶,,,10,名詞,一般,*,*,*,*,独自辞書,ヨミ,ハツオン
ウーロン茶,,,10,名詞,一般,*,*,*,*,独自辞書,ヨミ,ハツオン

フォーマットは↓の感じらしい。自分は原型のところに分かりやすいように自分の辞書っていうのがわかる文字を入れてる。

表層形,左文脈ID,右文脈ID,コスト,品詞,品詞細分類1,品詞細分類2,品詞細分類3,活用形,活用型,原形,読み,発音

そしたら、↓コマンドでコンパイルして辞書ファイルを作る。フォルダはインストールしたフォルダを適当に。

cd C:\Program Files (x86)\MeCab\bin
mecab-dict-index -d "C:\Program Files (x86)\MeCab\dic\ipadic" -u "C:\Program Files (x86)\MeCab\dic\userdic\chacha.dic" -f shift-jis -t utf-8 "C:\Program Files (x86)\MeCab\dic\userdic\chacha.csv"

オプションとかは「mecab-dict-index -h」でヘルプ見た方が早い。

で、読み込みのCSVはSJISでいいと思うんだけど、dicファイルの方はUTF8指定しておかないと、プログラム側で読み込むときにエラーになる。。。あと、CSVファイルの最後は改行入れない方がいい。

dicファイルが出来たらプログラム側の適当なフォルダに入れて、↓の感じでMeCabParamを作る。

MeCabParam mPara = new MeCabParam();
//まず、システム辞書があるフォルダの指定
mPara.DicDir = @"C:\*******\dic\ipadic";
//そしたら、ユーザ辞書ファイルを下の感じで指定
List<string> wkList = new List<string>();
//複数指定可
wkList.Add(@"C:\*******dic\userdic\chacha.dic");
//MeCabParamにセット
mPara.UserDic = wkList.ToArray();

↑はプログラム内でやってるけど、app.configとかで外だしもできるっぽ。

で、ほぼやりたい感じには上記でなるんだけど、コストの指定とかもう少し理解したいところ。まだ困ってないのは、大した解析やってないからでしょうし。。。

C# パフォーマンスカウンタもどき

システムの保守とかしてると、サーバのCPUとかメモリとかシステムのプロセスとかディスクの容量とか統計を取りたくなる。WindowsOSの話。パフォーマンスカウンタでいけるみたいな話はありますが、なんか使い勝手が悪いのと裏でずっと動かしてていいのかなんか微妙。あと結果のファイルもなんか微妙。

とゆーわけで自分でサービスを作った話。

パフォーマンスカウンタ使うには、最初にSystem.Managementを参照に追加しないとだめ。そしたら↓の感じで見たいカウンタ(項目)別に設定する。

//using System.Management;

//CPUの使用率
PerformanceCounter pc = new PerformanceCounter("Processor", "% Processor Time", "_Total", true);
Console.WriteLine(pc.NextValue());

で、どんな指定が出来るのかの一覧があんまない。環境によるってことかしらね。なので、何が使えるのか最初に↓の感じで一覧作ってCSVあたりにはいておく。

//PerformanceCounterCategoryを使う場合は↓のusingが必要
//using System.Diagnostics;
public static void ListUpCounters()
{
    using (StreamWriter wr = new StreamWriter(@".\パフォーマンスカウンタ一覧.csv", false, Encoding.GetEncoding(932)))
    {
        //カテゴリの一覧
        PerformanceCounterCategory[] pccates = PerformanceCounterCategory.GetCategories();
        wr.WriteLine(String.Format("カテゴリ,タイプ,オブジェ,単位,説明"));//一覧用ヘッダ
        foreach (PerformanceCounterCategory tmpcate in pccates)
        {
            //カテゴリのオブジェクト名
            wr.WriteLine(String.Format("\"{0}\",,,,\"{1}\"", tmpcate.CategoryName, tmpcate.CategoryHelp));
            try
            {
                //カテゴリのインスタンス一覧を取得
                string[] instnames = tmpcate.GetInstanceNames();
                //インスタンス名を出力
                foreach (string instanceName in instnames)
                {
                    wr.WriteLine(String.Format("{0},INSTANCE,\"{1}\"", tmpcate.CategoryName, instanceName));
                }

                //カウンタの一覧
                PerformanceCounter[] counters;
                if (instnames.Length > 0) counters = tmpcate.GetCounters(instnames[0]);
                else counters = tmpcate.GetCounters();

                //パフォーマンスカウンタ名を表示
                foreach (PerformanceCounter cnt in counters)
                {
                    wr.WriteLine(String.Format("{0},COUNTER,{1},\"{2}\",\"{3}\"", tmpcate.CategoryName, cnt.CounterName, cnt.CounterType, cnt.CounterHelp));
                }
            }
            //インスタンス情報なしはInvalidOperationExceptionらしい
            catch (InvalidOperationException)
            {
                wr.WriteLine(String.Format("インスタンス情報なし"));
            }
        }
    }

}

動かしたときのプロセスとかスレッドとかも全部出てくるので結構多いかも。。。

基本的にはCPUとメモリとディスクあたりを見る感じかしら。

カテゴリ カウンタ インスタンス 中身
Processor % Processor Time _Total CPU使用率(%)
Memory Available Bytes 利用可能メモリ(B)
Memory Pages/sec ページ数
LogicalDisk % Free Space _Total ディスク全体空率(%)
LogicalDisk Free Megabytes _Total ディスク全体空容量(MB)
LogicalDisk Current Disk Queue Length _Total ディスク待ちキュー数

インスタンスないやつは指定するときは引数無しで。

あと、カテゴリで「Process」の奴は個別のプロセスのCPU使用率とかも見れる。 インスタンスはプロセスの名前。同じ名前で何個もできる場合は「プロセス名#2」みたいな番号のつけ方される。固定で名称指定は無理だと思うので、↓の感じで、そのカテゴリのインスタンスを全部取って、プロセス名が含まれてたら使うみたいなやり方。

public static void proccessval()
{
    var catelist = PerformanceCounterCategory.GetCategories().ToList();
    foreach (PerformanceCounterCategory tmp in catelist.Where(n=>n.CategoryName=="Process"))
    {
        try
        {
            string[] instnames = tmp.GetInstanceNames();
            foreach (string instanceName in instnames)
            {
                if (instanceName.Contains("w3wp"))//例えばIIS
                {
                    PerformanceCounter pc = new PerformanceCounter("Process", "% Processor Time", instanceName, true);//CPU使用率(%)
                    Console.WriteLine(pc.NextValue());
                    pc = new PerformanceCounter("Process", "Working Set", instanceName, true);//使用メモリ(B)
                    Console.WriteLine(pc.NextValue());
                }
            }
        }
        catch
        { }
    }
}

ディスクについてはドライブ単位に見たい場合も↑と同じノリで出来る。あと、プロセスの場合、そのプロセスのコマンドラインが欲しかったりする場合がある。その場合は↑の奴に↓の感じで追加。

public static void proccessval()
{
    var catelist = PerformanceCounterCategory.GetCategories().ToList();
    foreach (PerformanceCounterCategory tmp in catelist.Where(n=>n.CategoryName=="Process"))
    {
        try
        {
            string[] instnames = tmp.GetInstanceNames();
            foreach (string instanceName in instnames)
            {
                if (instanceName.Contains("w3wp"))
                {
                    PerformanceCounter pc = new PerformanceCounter("Process", "% Processor Time", instanceName, true);//CPU使用率(%)
                    Console.WriteLine(pc.NextValue());
                    pc = new PerformanceCounter("Process", "Working Set", instanceName, true);//使用メモリ(B)
                    Console.WriteLine(pc.NextValue());

                    //プロセスIDをとってコマンドライン取得する
                    PerformanceCounter tmppc = new PerformanceCounter("Process", "ID Process", instanceName, true);
                    tmppc.NextValue();
                    Process ps = Process.GetProcessById(Convert.ToInt32(tmppc.NextValue()));
                    string proccessid = Convert.ToInt32(tmppc.NextValue()).ToString();

                    ManagementClass mc = new ManagementClass(new ManagementPath("Win32_Process"));
                    foreach (ManagementObject obj in mc.GetInstances())
                    {
                        if (obj["ProcessId"] != null && obj["CommandLine"] != null)
                        {
                            if (obj["ProcessId"].ToString() == proccessid)
                            {
                                string commandline = obj["CommandLine"].ToString();
                                string proccaption = obj["Caption"].ToString();
                            }
                        }
                    }

                }
            }
        }
        catch
        { }
    }
}

で、あとはこれをWindowsサービスで作って、とりたいカウンタの値を10秒単位とか1分単位とかでログファイルに書くみたいな感じ。

あーあと、毎回新しくカウンタ作るでもいいかもだけど重そう。。。最初に初期化だけして使いまわす場合、最初は存在したプロセスが途中でいなくなったりすると例外で落ちるので、その場合は初期化からまたやり直しみたいなロジック入れないとダメかな。あと途中でプロセス増えた場合もダメなので、定期的に初期化し直すように組まないとだめかしらね。

自分のIPまわりの情報が欲しいとき

タイトルまま。

//using System.Net;

//Dns.GetHostAddresses()の戻りはIPAddress[]
//IPv6含めて全部出てくる
foreach (var tmp in Dns.GetHostAddresses(Dns.GetHostName()))
{
    Console.WriteLine("{0}:{1}", tmp.AddressFamily, tmp.ToString());
    //IPv4が欲しい時はこれで
    //if (tmp.AddressFamily == AddressFamily.InterNetwork)
    //{
    //    Console.WriteLine(tmp);
    //    break;
    //}
}

C# ラムダ式で単項目のListを作る

とってきたテーブルから1列分のListを作りたい場合。たまにやりたくなるんだけど、地味に忘れるってかハマル。

var tmp = hogeTable.Select(n => n.HogeID ).Cast<int>().ToList();

上の感じ。Selectで1列だけとって必要な型にCastつけてからToList。

キャスト入れないと、anonymous?になって色々使い勝手が悪い。

C# .netMVC で MySQL使う

タイトル通り。ついでにEFから呼べるようにもする。まずはNugetで↓をインストール。

sample

そしたら、まず、SQL直接書くやつ。

using MySql.Data.MySqlClient;
public void test()
{
    string constr = @"Database=hogedb;Data Source=fugaserver;User Id=root;Password=passpass";
    string error = "";
    string title = "";
    using (MySqlConnection cn = new MySqlConnection(constr))
    {
        try
        {
            cn.Open();// 接続
            //クエリ発行
            MySqlDataAdapter da = new MySqlDataAdapter("select title from hogetables limit 1;", cn);
            DataTable dt = new DataTable();
            da.Fill(dt);
            foreach (DataRow row in dt.Rows)
            {
                title = row[0].ToString();
            }
            cn.Close();//クローズ
        }
        catch (Exception e)
        {
            error = e.Message;
        }
    }
}

次はEF。多分↑のインストールとは別に、EFを入れないとダメ。

最初はWeb.Config。まーこれでやる必要はないけど何となく。

<connectionStrings>
  <add name="RealConnection" connectionString="Database=hogedb;Data Source=fugaserver;User Id=root;Password=passpass" providerName="MySql.Data.MySqlClient" />
</connectionStrings>

テーブル用のPOCO。

[Table("hogetables")]
public class hogetable
{
    [Key]
    public int id { get; set; }
    public string title { get; set; }
}

public class MOGEModels : DbContext
{
    public MOGEModels() : base("RealConnection")
    {
    }
    public DbSet<hogetable> hogetables{ get; set; }
    
    public void test()
    {
       foreach (var tmp in this.hogetables.Where(n => n.id == 10))
       {
          tmp.title;
       }
    }
}

とゆーことで、普通に使えますねって当たり前か。。。

正直、RedmineとかのDBをちょっと他から触りたくなっただけです。Rubyでプラグインとか作るのめんどー。

C# 正規表現のパフォーマンス

最近、正規表現でちょろちょろ判定をしてたんだけど、書き方によって相当パフォーマンスに影響あるんですね。当たり前か。

ホントにかなり変わる。例えば、

string reg1 = @"(パンダ|ライオン|デザイン)(。|\d|\s)";

みたいな正規表現はクソ重い。何となく、「(a|b|c)(x|y|z)」みたいな組合せがすごい増えるとやれる感じがする。上の例でいうと、

string reg2 = @"パンダ(。|\d|\s)";
string reg3 = @"ライオン(。|\d|\s)";
string reg4 = @"デザイン(。|\d|\s)";

って書いてそれぞれ個別に判定した方が数段早かったりする。上の例で計測してみると、だいたい2.6倍早くなった。

組合せを何個も使うような正規表現は考え物ですね。当たり前なのかもしれませんが。。。

問合せ