"プログラムから開く"にNoUIEntryPoints-DesignModeが大量に並ぶ

Windowsストアアプリは、デスクトップアプリと同様にファイル拡張子と関連付けることができる。

f:id:boredbone:20150720181116p:plain

しかし、開発を続けているといつの間にか"プログラムから開く"メニューに謎の項目が大量に登録されている。

f:id:boredbone:20150720181054p:plain

調べてもいまいち情報が出てこない。xamlエディタのバグらしい?
VS2013の頃からあって2015RCでも発生したのでたぶん2015正式版でも発生しそう。

あるいはアプリ側を修正すれば治るのかもしれないけど、とりあえずこの大量のいらん項目を消したかったので、レジストリを検索して関連付け情報のキーを全部消した。
実行前にレジストリ全バックアップ必須。

var names = Registry.ClassesRoot.GetSubKeyNames();
var keysToDelete = new List<string>();

foreach (var name in names.Where(x => x.IndexOf("AppX") == 0))
{
    using (var subKey = Registry.ClassesRoot
        .OpenSubKey(name + @"\Application", false))
    {
        try
        {
            var appName = (string)subKey?
                .GetValue("ApplicationName");
            if (appName != null
                && appName.Equals("NoUIEntryPoints-DesignMode"))
            {
                keysToDelete.Add(name);
            }
        }
        finally
        {
            subKey?.Close();
        }
    }
}



foreach (var name in keysToDelete)
{
    Console.WriteLine(name);
    try
    {
        Registry.ClassesRoot.DeleteSubKeyTree(name);
    }
    catch (Exception e)
    {
        Console.WriteLine(e.ToString());
    }
}

Console.ReadLine();

(Windowsストアアプリ)タグ管理可能な画像ビューア"Mikan Viewer"リリース

ストアアプリ出しました。

Mikan Viewerapps.microsoft.com


機能は画像管理・検索・表示。
画像でiTunesのプレイリスト作成みたいなことがしたかった。

画像にタグやレーティングを設定して、複数のフォルダ内から条件に一致する画像を抽出できる。
事前にタグを設定する必要はあるけど、複数画像に一括でタグ設定とかもできるので割と便利。なはず。

もちろん画像の作成・更新日時や大きさ、ファイル名なんかでの検索も可能。
肝心の画像表示画面もタッチ・マウス・キーボードそれぞれで使いやすいように気を配った。つもり。



作り始めてから8か月近くかかった。もっと開発スピード上げたい。
それでもReactive ExtensionsとReactivePropertyでかなり楽々書けた。
Observable万歳!Linq万歳!


ストアに提出してから認証されるまでは30分、そこからストアにページが作られるまでも30分で終わった。
数日かかると聞いてたのであっという間でびっくり。
ただそこからストアでの検索結果に表示されるようになるまではちょっと時間かかった。
一日か二日後には検索でも出てくるようになってたかな?


ただwindows10上で動かすと広告が配信されない。アプリ内購入もできない。
10の問題なのか、アプリの作り方が悪いのか。
広告の方はまだしも、アプリの購入とかできないのは厄介。
CurrentAppクラスのメソッド・プロパティを呼ぶと全部ArgumentExceptionが出る。
やむなく例外は握りつぶさせてリリースしたけど、
調べても似たような問題が起こってる人いなさそうだし、どっかミスしてる?


リリース後の修正にもケリがついたらいくつかのコードはGitHubに上げよう。
コメントは日本語のままでも…いいよね。
今回のアプリでも文字列リソースやアプリ紹介の英訳に地味に苦労した。
いまだに冠詞がよくわかっていない。

連番を含む文字列のソート

連番で生成された複数のファイルを番号順に処理したい。

この連番が、こんな形式だったら特に問題ない。

var list1 = new List<string> { 
	"file00.ext", "file01.ext", "file02.ext",
	"file03.ext", "file08.ext", "file10.ext",
	"file12.ext", "file20.ext", "file30.ext"
};


Linqでサクッと処理できる。

foreach (var item in list1.OrderBy(x => x))
{
	Console.WriteLine(item);
}


結果

file00.ext
file01.ext
file02.ext
file03.ext
file08.ext
file10.ext
file12.ext
file20.ext
file30.ext


だけどこんな連番だと困る。

var list2 = new List<string> { 
	"file.ext", "file0.ext", "file1.ext",
	"file2.ext", "file3.ext", "file8.ext",
	"file10.ext", "file12.ext",
	"file20.ext", "file30.ext"
};


上と同じように並べ替えた結果

file.ext
file0.ext
file1.ext
file10.ext
file12.ext
file2.ext
file20.ext
file3.ext
file30.ext
file8.ext

なので正規表現で連番っぽい部分を抜き出して数値に変換してから並び替える。
そんな構造体をつくった。

struct SequenceNumber : IComparable<SequenceNumber>
{
    private string preName;
    private string numberString;
    private string postName;
    private string extension;
    private int number;

    /// <summary>
    /// 文字列の後ろから数字の塊を検索
    /// </summary>
    private static Regex regex
     = new Regex(@"\d+", RegexOptions.RightToLeft);

    /// <summary>
    /// ファイル名を連番とそれ以外の文字列に分割して比較可能な形式にする
    /// </summary>
    /// <param name="str">ファイル名またはパス</param>
    public SequenceNumber(string str)
    {
       
        extension = Path.GetExtension(str);//文字列から拡張子を取り出す
        
        //拡張子以外を取得
        var name = str.Substring(0, str.Length - extension.Length);

        //文字列中の連続した数字の塊のうち、一番最後のものを検索
        numberString = regex.Match(name).ToString();

        Int32.TryParse(numberString, out this.number);//数値に変換

        var sp = regex.Split(name, 2);//数字の前後に分割

        if (sp.Length > 0)
        {
            preName = sp[0];//数字より前の文字列
        }
        else
        {
            preName = "";
        }

        if (sp.Length > 2)
        {
            postName = string.Join("", sp.Skip(1));
        }
        else if (sp.Length > 1)
        {
            postName = sp[1];//数字より後の文字列
        }
        else
        {
            postName = "";
        }
    }

    /// <summary>
    /// 連番を含む文字列の大小比較
    /// </summary>
    /// <param name="other"></param>
    /// <returns></returns>
    public int CompareTo(SequenceNumber other)
    {
        var result = this.preName.CompareTo(other.preName);
        if (result != 0)
        {
            return result;
        }

        result = this.number.CompareTo(other.number);
        if (result != 0)
        {
            return result;
        }

        result = this.numberString.CompareTo(other.numberString);
        if (result != 0)
        {
            return result;
        }

        result = this.postName.CompareTo(other.postName);
        if (result != 0)
        {
            return result;
        }

        result = this.extension.CompareTo(other.extension);
        if (result != 0)
        {
            return result;
        }
        return 0;
    }

    public override string ToString()
    {
        return preName + "<" + number.ToString()
         + ">" + postName + extension;
    }
}


並べ替えの時にこの構造体を生成してやる。
こんな感じで使える。

foreach (var item in list2.OrderBy(x => new SequenceNumber(x)))
{
	Console.WriteLine(item);
}


結果

file.ext
file0.ext
file1.ext
file2.ext
file3.ext
file8.ext
file10.ext
file12.ext
file20.ext
file30.ext


ファイル名に使うつもりだったんで拡張子は取り除いたけど
(.mp3みたいに数字が入った拡張子だと困るので)、
この辺は別のクラスにしといてもっと汎用的に使えるようにしてもいいかも。

あとCompareToの中がコピペ祭りでダサいけどうまいやり方も思いつかなかった。

浮動小数点数を最小桁まで文字列化

浮動小数点数を丸めずに文字列化したかった。

class IEEE754
{
    private FloatingPointDefinition mode;


    private readonly FloatingPointDefinition DefFloat
        = new FloatingPointDefinition()
        {
            ExpLength = 8,
            FracLength = 23,
        };
    private readonly FloatingPointDefinition DefDouble
        = new FloatingPointDefinition()
        {
            ExpLength = 11,
            FracLength = 52,
        };

    private string decimalStrSmall;//小数部文字列
    private string decimalStrBig;//整数部文字列
    public int Digit { get; private set; }//10進指数

    public static bool EnableDenormal { get; set; }//指数部==0のとき非正規化数を扱うか?
    public static bool EnableInfinity { get; set; }//指数部が最大値の時Inf,NaNを扱うか?

    public double Value { get; private set; }//元になった浮動小数点値
    public ulong Code { get; private set; }//バイナリ値

    private bool Sign { get { return (Bit.Check(this.Code, mode.BitSize - 1)); } }
    private string SignStr { get { return this.Sign ? "-" : "+"; } }
    private int ExpWithBias
    {
        get
        {
            return (int)((this.Code >> mode.FracLength) & Bit.FillTo(mode.ExpLength));
        }
    }
    public int ExpWithoutBias
    {
        get
        {
            return
                ((EnableDenormal && ExpWithBias == 0) ? 1 : ExpWithBias)
                - (int)Bit.FillTo(mode.ExpLength - 1);
        }
    }

    private ulong Frac//仮数部のバイナリ
    {
        get
        {
            return (this.Code & Bit.FillTo(mode.FracLength))
                + ((EnableDenormal && ExpWithBias == 0) ? 0 : (1UL << mode.FracLength));
        }
    }
    static IEEE754()
    {
        EnableDenormal = true;
        EnableInfinity = true;
    }

    public IEEE754(float value)
    {
        mode = DefFloat;
        SetValue(value, Float2Bin(value) & mode.MaxBits);
    }
    public IEEE754(uint code)
    {
        mode = DefFloat;
        SetValue(Bin2Float(code), code);
    }
    public IEEE754(double value)
    {
        mode = DefDouble;
        SetValue(value, Double2Bin(value) & mode.MaxBits);
    }
    public IEEE754(ulong code)
    {
        mode = DefDouble;
        SetValue(Bin2Double(code), code);
    }
    private void SetValue(double value, ulong code)
    {
        this.Value = value;
        this.Code = code;

        DecodeDecimal();

    }



    private uint Float2Bin(float f)
    {
        return BitConverter.ToUInt32(BitConverter.GetBytes(f), 0);
    }

    private float Bin2Float(ulong i)
    {
        return BitConverter.ToSingle(BitConverter.GetBytes((uint)(i & 0xFFFFFFFF)), 0);
    }
    private ulong Double2Bin(double f)
    {
        return BitConverter.ToUInt64(BitConverter.GetBytes(f), 0);
    }

    private double Bin2Double(ulong i)
    {
        return BitConverter.ToDouble(BitConverter.GetBytes(i), 0);
    }

    public string GetBinCode()
    {
        var sb = new StringBuilder(mode.BitSize);
        for (int i = mode.BitSize - 1; i >= 0; i--)
        {
            sb.Append(Bit.Check(this.Code, i) ? "1" : "0");
        }
        return sb.ToString();
    }


    /// <summary>
    /// 整数部,小数部それぞれを10進整数文字列に変換
    /// 数値 XXX.xxx を 0.XXXxxxe+n の形に変換し,
    /// decimalStrBig = "XXX"
    /// decimalStrSmall = "xxx"
    /// Digit = n
    /// と設定する
    /// </summary>
    private void DecodeDecimal()
    {
        BigInteger bigPart;
        BigInteger smallPart;

        ulong smallBin;

        int minFracExp = mode.FracLength - ExpWithoutBias;//最小桁の指数
        int minDigit = 0;


        if (minFracExp <= 0)//整数
        {
            bigPart = (new BigInteger(Frac)) << (-minFracExp);
            smallBin = 0;
        }
        else if (ExpWithoutBias < 0)//value < 1
        {
            bigPart = 0;
            smallBin = Frac;
        }
        else//(0 < minFracExp <= FracLength)
        {
            bigPart = Frac >> minFracExp;
            smallBin = Frac & Bit.FillTo(minFracExp);
        }


        if (smallBin > 0)
        {
            for (int i = 0; i <= mode.FracLength; i++)
            {
                if ((smallBin & 1) == 1)
                {
                    minDigit = i;
                    break;
                }
                smallBin >>= 1;
            }

            //小数部を10^n倍した整数
            smallPart = BigInteger.Pow(5, minFracExp - minDigit) * smallBin;

            decimalStrSmall = smallPart.ToString();
            Digit = -minFracExp + minDigit + decimalStrSmall.Length;
            //Digitには元の数値の小数部のみを取り出して 0.xxxe+n と表記した場合の指数nが入る
        }
        else
        {
            smallPart = 0;
            decimalStrSmall = "0";
            Digit = 0;
        }

        decimalStrBig = bigPart.ToString();

        if (bigPart > 0)//整数部があるなら小数部を10^(-1)の桁まで0埋め
        {
            if (Digit < 0)
            {
                decimalStrSmall
                    = decimalStrSmall.PadLeft(decimalStrSmall.Length - Digit, '0');
                Digit = 0;
            }
            Digit += decimalStrBig.Length;
        }
        else
        {
            decimalStrBig = "";
        }
    }


    public override string ToString()
    {
        return this.ToString(null);
    }
    public string ToString(int? decimalPoint)
    {

        //指数部が最大値ならInfまたはNaN
        if (EnableInfinity
            && ExpWithBias == ((1U << mode.ExpLength) - 1))
        {
            string status;
            if ((this.Code & Bit.FillTo(mode.FracLength)) == 0)
            {
                status = "Inf";
            }
            else if (Bit.Check(Frac, mode.FracLength - 1))
            {
                status = "qNaN";
            }
            else
            {
                status = "sNaN";
            }
            return this.SignStr + status;
        }



        int point = 0;

        if (decimalPoint.HasValue)
        {
            point = decimalPoint.Value;
        }
        else
        {
            point = decimalStrBig.Length;//point>=0
            if (point == 0 && Digit < 0)//整数部==0かつ小数部が0始まり
            {
                point = Digit;
            }
        }

        int dispDigit = Digit - point;
        var numStr = decimalStrBig + decimalStrSmall;


        if (point >= numStr.Length)//小数点位置が文字列桁数より大きい
        {
            numStr = numStr.PadRight(point, '0') + ".0";
        }
        else if (point > 0)
        {
            numStr = numStr.Insert(point, ".");
        }
        else
        {
            numStr = "0.".PadRight(2 - point, '0') + numStr;
        }

        return this.SignStr + numStr
            + ((decimalPoint.HasValue || dispDigit != 0)
            ? ("e" + (dispDigit < 0 ? "" : "+") + dispDigit.ToString())
            : "");
    }
}

floatとかdoubleの引数のバイナリ値を読み込んで、定義されたフォーマットに従って指数部・仮数部を解釈。
整数部と小数部をそれぞれ文字列化してから最後にドッキング。
このときにBigIntegerを使ってる。もうちょいスマートにできないもんか。


浮動小数点数フォーマットの定義はこんな感じのクラスで表す。
ExpLengthに指数部の長さ、FracLengthに仮数部の長さをビット単位で入れておく。

//浮動小数点数フォーマットの定義
class FloatingPointDefinition
{
    public int ExpLength { get; set; }//指数部のbit長
    public int FracLength { get; set; }//仮数部のbit長//must be <64

    public int BitSize { get { return ExpLength + FracLength + 1; } }//全bit長
    public ulong MaxBits { get { return Bit.FillTo(BitSize); } }//ビットマスク
}


あとビット操作関連は別クラスにしておいた。

class Bit
{
    /// <summary>
    /// nビット目が立っているか調べる
    /// </summary>
    /// <param name="x">調査対象</param>
    /// <param name="n">LSBからのオフセット</param>
    /// <returns>立っていたらtrue</returns>
    public static bool Check(int x, int n)
    {
        return !((x & (0x01 << n)) == 0);
    }
    public static bool Check(ulong x, int n)
    {
        return !((x & (0x01UL << n)) == 0);
    }

    /// <summary>
    /// 指定bitまで埋めたバイナリを取得
    /// </summary>
    /// <param name="length">ビット長n</param>
    /// <returns>2^n-1</returns>
    public static ulong FillTo(int length)
    {
        return (length >= 64) ? ~0UL : ((1UL << length) - 1);
    }
}


使うときはコンストラクタにfloatかdoubleの値を渡す。

var d = new IEEE754(Math.PI);
var f = new IEEE754((float)Math.PI);

Console.WriteLine(d.ToString());
Console.WriteLine(f.ToString());


結果はこんな感じ。

+3.141592653589793115997963468544185161590576171875
+3.1415927410125732421875

改めてfloatの精度が低いのがわかる。


整数部の長さを指定することもできる。

Console.WriteLine(f.ToString(0));
Console.WriteLine(f.ToString(-1));
Console.WriteLine(f.ToString(2));

結果

+0.31415927410125732421875e+1
+0.031415927410125732421875e+2
+31.415927410125732421875e-1


あとはバイナリコードを取得できるようにしてみたり。

Console.WriteLine(f.GetBinCode());

結果

01000000010010010000111111011011

四捨五入

最近、二種類の四捨五入する関数を見た。

これと

static long round(float x)
{
	long ans = (long)x;
	if (x - (float)ans >= 0.5)
		return ++ans;
	else if (x - (float)ans <= -0.5)
		return --ans;
	return ans;
}

これ。

static long round(float x)
{
	return (x > 0.0f) ? (long)(x + 0.5f) : -(long)(-x + 0.5f);
}


得られる結果は同じのはずだけど、
前者はなんだか技巧的?というか無駄に複雑で後者は一行に圧縮
いろんな書き方があるもんだ。

PythonとSciPyを使ってみた

MATLABの代替になることを期待してSciPyにチャレンジ。
というかPythonに初チャレンジ。


まずPython,NumPy,SciPy,matplotlibインストーラをダウンロード、インストール。
f:id:boredbone:20140215205536p:plain

ここを参考にEasy_installをインストール。
(ez_setup.pyをダウンロードして実行)


PythonのScriptsディレクトリ(C:\Python27\Scriptsとか)を環境変数のpathに追加。


コマンドプロンプトを開いて、必要なものをインストール。

> easy_install pip
> pip install python-dateutil
> easy_install pyparsing


これで準備完了。


試しに下のコードを実行してみた。

# -*- coding: utf-8 -*-
from pylab import *
import scipy.signal

w1=100*2*pi
w2=1000*2*pi

a1=w1+w2
a0=w1*w2

b1=w2
b0=0

Ts=0.001
t=arange(0, 100, Ts)

Ns=size(t)

u=sin(2*pi*10*t)

num=[b1,b0]
den=[1,a1,a0]

rt,y,rx=scipy.signal.lsim((num,den),u,t)

figure()
plot(t,u,'b-',label='Input')
plot(t,y,'r-',label='Output')
xlim(0,1)
ylim(-2,2)
xlabel('Time[sec]')
ylabel('Voltage[V]')
legend()
grid(True)
show()

f:id:boredbone:20140215205540p:plain


同じ内容をMATLABで書いて比較。

w1=100*2*pi;
w2=1000*2*pi;

a1=w1+w2;
a0=w1*w2;

b1=w2;
b0=0;

Ts=0.001;
t=0:Ts:100;

Ns=length(t);

u=sin(2*pi*10*t);

num=[b1 b0];
den=[1 a1 a0];

y=lsim(tf(num,den),u,t);

figure
hold on
plot(t,u,'b-')
plot(t,y,'r-')
xlim([0 1])
ylim([-2 2])
xlabel('Time[sec]')
ylabel('Voltage[V]')
legend('Input','Output')
grid on

f:id:boredbone:20140215205543p:plain


MATLABの方が実行速いんだけど…