WMAファイルのID3タグを取得する

動機

楽天の期間限定ポイントの期限が切れそうだったのでサクラクエストのサントラCDを購入した。
音楽をパソコンに取り込んだがID3タグが付いていなかったのでGroove Musicのアルバム情報検索で自動設定した。
タグは更新されたが、ファイル名はトラック1,2,3...のままなのでファイル名をID3タグのタイトルにリネームしたい。
80曲ほどあるので手動ではやりたくない。

環境

・Windows10 Pro
Visual Studio Community 2017
・.NET4.6.1

実装

参考にしたサイト:
MP3ファイルからタイトルやアーティスト名などを取得するには?[C#、VB] - @IT

上記のコードをコピペしただけではエラーが発生して実行できなかったので以下の2点を修正した。
・Shell32の参照プロパティの相互運用型の埋め込みをfalseに設定
・ShellClassの型をdynamicに変更

コード

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.IO;
using Shell32;
using System.Runtime.InteropServices;

namespace ConsoleApp1
{
    class Program
    {
        static void Main(string[] args)
        {
            dynamic shell = new ShellClass();
            try
            {
                foreach (var dir in args)
                {
                    if (!Directory.Exists(dir))
                    {
                        Console.WriteLine($"Not exists : {dir}");
                        continue;
                    }
                    Console.WriteLine($"Directory : {dir}");
                    var folder = shell.NameSpace(dir);
                    try
                    {
                        foreach (var filename in Directory.GetFiles(dir))
                        {
                            var ext = Path.GetExtension(filename);
                            if (ext.ToUpper() != ".WMA")
                            {
                                continue;
                            }

                            var fi = folder.ParseName(Path.GetFileName(filename));
                            try
                            {
                                //21がタイトル情報
                                var title = folder.GetDetailsOf(fi, 21);
                                var renamed = Path.Combine(dir, title + ext);
                                File.Move(filename, renamed);
                                Console.WriteLine($"Rename {filename} {renamed}");
                            }
                            finally
                            {
                                Marshal.ReleaseComObject(fi);
                            }
                        }
                    }
                    finally
                    {
                        Marshal.ReleaseComObject(folder);
                    }
                }

            }
            finally
            {
                Marshal.ReleaseComObject(shell);
            }
        }
    }
}

まとめ

最高なので買いましょう。

www.amazon.co.jp

Chokudai Contest 001

問題はこちら(Chokudai Contest 001の問題リンクに飛びます)。

考察

平成23年度のデータベーススペシャリストの午後Ⅱを解きながら方針を考える。
採点する。合格点に届いたっぽい。うれしい。
・最大値のマスから削っていけば良さそう
・最大値のマスが複数にある場合は連鎖数が大きいものから削った方が良さそう
・連鎖数を調べるとTLEする?(計算量の見積もりができないので、1手・2手・・・と読む手数を増やして行く方向で進める)

実装

とりあえず点数を取るために最大値のマスを選択して連鎖するだけのコードを書く。
最大値のマスが複数ある場合は左から右、上から下の順に調べて最初に見つかったものを選択。
左右上下の順に調べて最初に見つかった連鎖可能なマスを選択。
これで806989点。意外に良いスコアが出た。

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace Chokucon
{
    class Program
    {
        static void Main(string[] args)
        {
            var map = new Map(30, 30);
            map.Read();
            map.Solve();
            Console.ReadLine();
        }
    }

    class Map
    {
        private int[,] map;
        private int xnum { get { return map.GetLength(0); } }
        private int ynum { get { return map.GetLength(1); } }
        private int posx;
        private int posy;
        private int val;
        public int Step;

        public Map(int x, int y)
        {
            map = new int[x, y];
            Step = 0;
        }

        public void Read()
        {
            for (int i = 0; i < ynum; i++)
            {
                var line = Console.ReadLine();
                var a = line.Split(' ');
                for (int j = 0; j < xnum; j++)
                {
                    map[i, j] = int.Parse(a[j]);
                }
            }
        }

        public void Print()
        {
            for (int i = 0; i < xnum; i++)
            {
                for (int j = 0; j < ynum; j++)
                {
                    Console.Write(map[i, j] + " ");
                }
                Console.WriteLine();
            }
        }

        public void PrintLine()
        {
            Console.WriteLine((posy + 1).ToString() + " " + (posx + 1).ToString());
        }

        public void Solve()
        {
            while (!Finished())
            {
                var m = Max();
                Select(m.X, m.Y);
            }
        }

        public void Select(int x, int y)
        {
            posx = x;
            posy = y;
            Update();
            Search();
            Step++;
        }

        public bool Search()
        {
            PrintLine();
            if (Left() || Right() || Up() || Down()) return Search();
            return false;
        }

        public void Update()
        {
            map[posy, posx]--;
            val = map[posy, posx];
        }

        public Point Max()
        {
            int px = 0, py = 0, val = -1;
            for (int y = 0; y < xnum; y++)
            {
                for (int x = 0; x < ynum; x++)
                {
                    if (val < map[y, x])
                    {
                        py = y;
                        px = x;
                        val = map[y, x];
                    }
                }
            }
            return new Point(px, py, val);
        }

        public bool Finished()
        {
            return Max().Value == 0;
        }

        public bool Left()
        {
            if (val == 0) return false;
            if (posx <= 0) return false;
            if (map[posy, posx - 1] != val) return false;
            posx--;
            Update();
            return true;
        }

        public bool Right()
        {
            if (val == 0) return false;
            if (posx >= xnum - 1) return false;
            if (map[posy, posx + 1] != val) return false;
            posx++;
            Update();
            return true;
        }

        public bool Up()
        {
            if (val == 0) return false;
            if (posy <= 0) return false;
            if (map[posy - 1, posx] != val) return false;
            posy--;
            Update();
            return true;
        }

        public bool Down()
        {
            if (val == 0) return false;
            if (posy >= ynum - 1) return false;
            if (map[posy + 1, posx] != val) return false;
            posy++;
            Update();
            return true;
        }
    }

    struct Point
    {
        public int X, Y, Value;
        public Point(int x, int y, int val)
        {
            X = x;
            Y = y;
            Value = val;
        }
    }
}

平成24年度のAMⅠを解く。ストラテジ系の問題が多い。AMⅠで初めて6割を切ってしまい危機感を覚える。
競プロやっている場合では無いと思いつつ次の実装を開始。
最大値のマスが複数ある場合はランダムに選ぶようにして上下左右の選択順を手動で適当に変更して投げたら819691点になった。

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace Chokucon
{
    class Program
    {
        static void Main(string[] args)
        {
            var map = new Map(30, 30);
            map.Read();
            map.Solve();
            Console.WriteLine(map.Step);
            Console.ReadLine();
        }
    }

    class Map
    {
        private int[,] map;
        private int xnum { get { return map.GetLength(0); } }
        private int ynum { get { return map.GetLength(1); } }
        private int posx;
        private int posy;
        private int val;
        public int Step;

        public Map(int x, int y)
        {
            map = new int[x, y];
            Step = 0;
        }

        public void Read()
        {
            for (int i = 0; i < ynum; i++)
            {
                var line = Console.ReadLine();
                var a = line.Split(' ');
                for (int j = 0; j < xnum; j++)
                {
                    map[i, j] = int.Parse(a[j]);
                }
            }
        }

        public void Print()
        {
            for (int i = 0; i < xnum; i++)
            {
                for (int j = 0; j < ynum; j++)
                {
                    Console.Write(map[i, j] + " ");
                }
                Console.WriteLine();
            }
        }

        public void PrintLine()
        {
            Console.WriteLine((posy + 1).ToString() + " " + (posx + 1).ToString());
        }

        public void Solve()
        {
            var r = new Random(100000000);
            while (!Finished())
            {
                var m = MaxPoints().OrderBy(p => p.X).Select(p => p).ToList();
                int i = r.Next(m.Count() - 1);
                Select(m[i].X, m[i].Y);
            }
        }

        public void Select(int x, int y)
        {
            posx = x;
            posy = y;
            Update();
            Search();
            Step++;
        }

        public bool Search()
        {
            PrintLine();
            if (val <= 0) return false;
            if (Up() || Right() || Left() || Down()) return Search();
            return false;
        }

        public int Try(int dx, int dy)
        {
            int ret = 0;

            return ret;
        }
        
        public void Update()
        {
            map[posy, posx]--;
            val = map[posy, posx];
        }

        public Point MaxPoint()
        {
            int px = 0, py = 0, val = -1;
            for (int y = 0; y < ynum; y++)
            {
                for (int x = 0; x < xnum; x++)
                {
                    if (val < map[y, x])
                    {
                        py = y;
                        px = x;
                        val = map[y, x];
                    }
                }
            }
            return new Point(px, py, val);
        }

        public List<Point> MaxPoints()
        {
            var ret = new List<Point>();
            var m = MaxPoint();
            for (int y = 0; y < ynum; y++)
            {
                for (int x = 0; x < xnum; x++)
                {
                    if (m.Value == map[y, x])
                    {
                        ret.Add(new Point(x, y, map[y, x]));
                    }
                }
            }
            return ret;
        }

        public bool Finished()
        {
            return MaxPoint().Value == 0;
        }

        public bool Left()
        {
            if (posx <= 0) return false;
            if (map[posy, posx - 1] != val) return false;
            posx--;
            Update();
            return true;
        }

        public bool Right()
        {
            if (posx >= xnum - 1) return false;
            if (map[posy, posx + 1] != val) return false;
            posx++;
            Update();
            return true;
        }

        public bool Up()
        {
            if (posy <= 0) return false;
            if (map[posy - 1, posx] != val) return false;
            posy--;
            Update();
            return true;
        }

        public bool Down()
        {
            if (posy >= ynum - 1) return false;
            if (map[posy + 1, posx] != val) return false;
            posy++;
            Update();
            return true;
        }
    }

    struct Point
    {
        public int X, Y, Value;
        public Point(int x, int y, int val)
        {
            X = x;
            Y = y;
            Value = val;
        }

        public override string ToString()
        {
            return X.ToString() + ", " + Y.ToString() + ", " + Value.ToString();
        }
    }
}

疲れて頭が回らなくなる。上下左右をのうち連鎖するマスを優先して選ぶような気がするコードを投げる(バグってそう)。823276点。ここで時間切れ。

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace Chokucon
{
    class Program
    {
        static void Main(string[] args)
        {
            var map = new Map(30, 30);
            map.Read();
            map.Solve();
            Console.WriteLine(map.Step);
            Console.ReadLine();
        }
    }

    class Map
    {
        private int[,] map;
        private int xnum { get { return map.GetLength(0); } }
        private int ynum { get { return map.GetLength(1); } }
        private int posx;
        private int posy;
        private int val;
        public int Step;

        public Map(int x, int y)
        {
            map = new int[x, y];
            Step = 0;
        }

        public void Read()
        {
            for (int i = 0; i < ynum; i++)
            {
                var line = Console.ReadLine();
                var a = line.Split(' ');
                for (int j = 0; j < xnum; j++)
                {
                    map[i, j] = int.Parse(a[j]);
                }
            }
        }

        public void Print()
        {
            for (int i = 0; i < xnum; i++)
            {
                for (int j = 0; j < ynum; j++)
                {
                    Console.Write(map[i, j] + " ");
                }
                Console.WriteLine();
            }
        }

        public void PrintLine()
        {
            Console.WriteLine((posy + 1).ToString() + " " + (posx + 1).ToString());
        }

        public void Solve()
        {
            var r = new Random(100000000);
            while (!Finished())
            {
                var m = MaxPoints().OrderBy(p => p.X).Select(p => p).ToList();
                int i = r.Next(m.Count() - 1);
                Select(m[i].X, m[i].Y);
            }
        }

        public void Select(int x, int y)
        {
            posx = x;
            posy = y;
            Update();
            Search();
            Step++;
        }

        public bool Search()
        {
            PrintLine();
            if (val <= 0) return false;
            if (Try(-1, 0)) //Left
            {
                if (Left() || Down() || Right() || Up()) return Search();
            }
            else if (Try(1, 0)) //Right
            {
                if (Right() || Down() || Left() || Up()) return Search();
            }
            else if (Try(0, -1)) //Up
            {
                if (Up() || Down() || Left() || Right()) return Search();
            }
            else if (Try(0, 1)) //Down
            {
                if (Down() || Right() || Left() || Up()) return Search();
            }
            return false;
        }

        public bool Left()
        {
            if (posx <= 0) return false;
            if (map[posy, posx - 1] != val) return false;
            posx--;
            Update();
            return true;
        }

        public bool Right()
        {
            if (posx >= xnum - 1) return false;
            if (map[posy, posx + 1] != val) return false;
            posx++;
            Update();
            return true;
        }

        public bool Up()
        {
            if (posy <= 0) return false;
            if (map[posy - 1, posx] != val) return false;
            posy--;
            Update();
            return true;
        }

        public bool Down()
        {
            if (posy >= ynum - 1) return false;
            if (map[posy + 1, posx] != val) return false;
            posy++;
            Update();
            return true;
        }

        public bool Try(int dx, int dy)
        {
            int x = posx + dx;
            int y = posy + dy;
            if (x < 0 || x >= xnum) return false;
            if (y < 0 || y >= ynum) return false;

            int v = map[y, x] - 1;
            if (x - 1 >= 0)
            {
                if (map[y, x - 1] == v) return true;
            }
            if (x + 1 < xnum)
            {
                if (map[y, x + 1] == v) return true;
            }
            if (y - 1 >= 0)
            {
                if (map[y - 1, x] == v) return true;
            }
            if (y + 1 < ynum)
            {
                if (map[y + 1, x] == v) return true;
            }
            return false;
        }

        public void Update()
        {
            map[posy, posx]--;
            val = map[posy, posx];
        }

        public Point MaxPoint()
        {
            int px = 0, py = 0, val = -1;
            for (int y = 0; y < ynum; y++)
            {
                for (int x = 0; x < xnum; x++)
                {
                    if (val < map[y, x])
                    {
                        py = y;
                        px = x;
                        val = map[y, x];
                    }
                }
            }
            return new Point(px, py, val);
        }

        public List<Point> MaxPoints()
        {
            var ret = new List<Point>();
            var m = MaxPoint();
            for (int y = 0; y < ynum; y++)
            {
                for (int x = 0; x < xnum; x++)
                {
                    if (m.Value == map[y, x])
                    {
                        ret.Add(new Point(x, y, map[y, x]));
                    }
                }
            }
            return ret;
        }

        public bool Finished()
        {
            return MaxPoint().Value == 0;
        }
    }

    struct Point
    {
        public int X, Y, Value;
        public Point(int x, int y, int val)
        {
            X = x;
            Y = y;
            Value = val;
        }

        public override string ToString()
        {
            return X.ToString() + ", " + Y.ToString() + ", " + Value.ToString();
        }
    }
}

感想

78位でした。
はじめての長時間コンテストでしたが、初心者の私にも理解しやすい問題でとても楽しめました。
@chokudaiさんが死んでしまうらしいので定期開催とは言いませんが、是非また開催していただきたいです。

AMⅠ対策をしなければ...。

【SQL】DELETE文の代替

データベーススペシャリストの過去問にDELETE文でロック待ちが発生した際の対策として記載されていたテク。
普段からデータベースを使っている人にとっては常識なのかもしれない。

  1. 残したいレコードを取得しファイルに書き出す
  2. TRUNCATE TABLE文でテーブルを空にする
  3. 1で書き出したファイルをテーブルに読み込む

問題では「1で取得したレコードをバックアップとして保管する」という制約があったのでファイルに書き出すとしているが、普通に使う分にはメモリに読み込めば良さそう。

  • DELETE文は1レコード毎に削除及びログ書き込みが行われるため遅い。
  • TRUNCATE TABLE文の場合はログ書き込みを行わないためDELETE文より高速。ただし全行削除しかできない。
ということなので、テーブルのレコード数が多く且つ削除対象レコードの数>>削除対象外のレコード数の場合は効果がありそう。