Programming」カテゴリーアーカイブ

Fake System.IO

System.IO.File.ReadLines は .NET 3.5 や mono で使えない。
だったら自分で作ってしまえ、と思ったのでやってみた。

どうでもいいけど gmcs も csc.exe と同じオプションが使えるんだね。
/out:FILENAME は -out:FILENAME みたくハイフンにするだけ。

いや、yield を使ったことが無いのでやってみようかと。
こんな強引というよりアホなメソッド追加なんてできるのかな?

using System;
using System.Collections.Generic; //IEnumerable

namespace System.IO
{
    public static partial class File
    {
        public static IEnumerable ReadLines(string path)
        {
            using(var sr = new System.IO.StreamReader(path))
            {
                string buf;
                while ((buf = sr.ReadLine()) != null)
                {
                    yield return buf;
                }
            }
        }
    }
}

File クラス (System.IO)
File クラスは partial class では無いみたいだけど。

コンフリクトか、やっぱりダメだった。
Windows でもやってみたけど同じ、こんなことをやろうと考えるほうがおかしい。
しかたがない、名前空間には追加できるはずだから違うクラス名にしてみる。

fakeio.cs

using System;
using System.Collections.Generic; //IEnumerable

namespace System.IO
{
    public static class FakeFile
    {
        public static IEnumerable ReadLines(string path)
        {
            using(var sr = new System.IO.StreamReader(path))
            {
                string buf;
                while ((buf = sr.ReadLine()) != null)
                {
                    yield return buf;
                }
            }
        }
    }
}

readlines.cs

using System;
using System.IO;

class __main__
{
    [STAThread]
    public static void Main(string[] args)
    {
        if (args.Length == 0)
        {
            Console.WriteLine("no arg...");
            return;
        }
        int n = 0;
        foreach (var line in FakeFile.ReadLines(args[0]))
        {
            n++;
            Console.WriteLine("{0}: {1}", n.ToString("000"), line);
        }
    }
}

で、cat.exe という実行ファイルを作るコマンド。

gmcs -out:cat.exe *.cs

何故か exe という拡張子を付けないとエラーになる。
Linux でも強制するんかい、まぁ解りやすいと思えばいいし。
アスタリスクでディレクトリ内をまるごとコンパイルしてくれるのはありがたい。

なるほど、yield はこういう時に使うと確かに便利だ。
C#4.0 の ReadLines って上記を自作する以上のメリットがあるのかな?

Default argument and ReadLines

何を今頃なのだが C# 4.0 を調べてみた。
なんと 4.0 では関数にデフォルト引数を指定できるようになっていた。

てか、今まで無かったのかよ…

Default argument
で可能とかウソを書いてしまった、はずかしい。
引数に数の差だけのためにオーバーロードをする必要はコレで無くなるわけだ。

それと、System.IO.File.ReadLines() というイテレーション関数も追加らしい。
StreamReader.ReadLine() じゃダメなの?と思うんだが…

他は興味がわかないので今回はパス。

ということで 3.5 と 4.0 の csc.exe で試しコードを書く。

default_param.cs

using System;

class Test
{
    public static int Func(int x=5, int y=7)
    {
        return x+y;
    }
}

class __main__
{
    [STAThread]
    public static void Main(string[] args)
    {
        int n = Test.Func(y:20);
        Console.WriteLine(n);
    }
}

readlines.cs

using System;
using System.IO;

class __main__
{
    [STAThread]
    public static void Main(string[] args)
    {
        int n = 0;
        //foreach (var line in File.ReadAllLines(args[0]))
        foreach (var line in File.ReadLines(args[0]))
        {
            n++;
            Console.WriteLine("{0}: {1}", n.ToString("000"), line);
        }
    }
}

ちなみに私がパスを通しているのは 3.5 のみなのでこうなる。
うん、やっぱりどちらも 4.0 でしかビルドできないや。

これだけではツマラナイので Ubuntu でもやってみる。

あれ?デフォルト引数のほうだけ問題なくビルドできちゃった。
どっちなんだよ Ubuntu に最初から入っている mono は。

Readlines は「そんなメソッドありません」ですね、そりゃそうだ。
ReadAlllines とコメントアウトを変更すれば普通にビルドできた。
StreamReader で while ループしたほうが圧倒的効率だけどテストです。

つまり何が言いたいかというと。
コンパイルするのがメンドイけど実用無視で遊ぶなら C# は最強。
Linux で Python は単なる実用ツールでしかないもの、私はだが。

Using wget

結局 Windows に戻ったのは正月だけだった、あぁ Linux は快適。

Linux で不便なのは Opera 11 の検索バーで日本語変換を行うと死ぬことだけだ。
フォントは pre と monospace を VL ゴシックに変更しただけで綺麗になった。

一昔前では考えられないほどマトモ、新参の WebKit 軍団にボロ負けだったしね。
しかしデフォルトショートカットキーをコロコロ変更しないでよ。

ネットをウロウロしていると自作漫画配布サイトに。
最近は Blog 機能を利用しているせいか連番で落とせないトコばかり。
だったけど連番で一枚一枚配布している所もまだ存在するんだね。

これは久々に新規スクリプトを書かないと。
何のために wget コマンドが存在するというのか。
wget だけでリンクをたどるのではなく連番ファイル以外はいらないし。

連番最初は 0 か 1 だし確実に 100 ファイル以下だと決め付けでいいだろう。
パラメータ 2 つ指定を強制するけど自分で使うんだから問題無い。
一応ゼロ詰めしているかどうかで振り分けしている。

#!/usr/bin/env python
#-*- coding:utf-8 -*-

"""
    連番ファイルの一括ダウンロード
    
    コマンド
    $ download url num
    
    url 連番最初のファイルのフルパス
    num 最後のファイル番号の整数
"""

import sys
import os

if len(sys.argv) < 3:
    print "URL と 数を指定してください"
else:
    # ファイル名分離
    pathname, filename = os.path.split(sys.argv[1])
    try:
        n = filename.rindex(".")
    except ValueError:
        print "拡張子が見つかりません"
        sys.exit()
    # 連番最初の数値(0 or 1) と拡張子
    begin = int(filename[n-1])
    ext = filename[n:]
    # ゼロ詰めかどうかで振り分け
    if filename[n-2] == "0":
        name = "wget " + pathname + "/" + filename[:n-2] + "%02d" + ext
    else:
        name = "wget " + pathname + "/" + filename[:n-1] + "%d" + ext
    # 連番数のチェック
    try:
        end = int(sys.argv[2]) + 1
    except ValueError:
        print "二番目の引数は整数でお願いします"
        sys.exit()
    # 開始
    for i in range(begin, end):
        os.system(name % i)
        #print name % i

~/bin に download という名前で置いて chmod で +x して。
覚書ページに書いているけど ~/bin を作れば Ubuntu は勝手にパスが通る。

使ってみる、よしよし動く。
端末を普通に使うようになると「コレで十分」になって退化する私であった。
てゆーか、アッサリこんなのを作って使えるから Linux のほうが楽なんだよね。

実は Python での文字列のスライス方法を忘れていたので何か作ろうと…
こんなしょーもないスクリプトあたりをたまには作らないと忘れるねん。

関係無いけどこんなのを見つけた。

[ Dolphinで右クリックメニューを使う方法 Dolphin用Script] by ひねもすLinux

Dolphin でもスクリプト拡張ができるんだ、知らなかった。
しかし面倒くさいな、Nautilus がこうならないことを祈る。

WPF4 Text Rendering

WPF 4 をコードで使う方法は前回で解ったので今回は XAML を。

System.Xaml が新たに作られたということは xmlns 名前空間が変わる?
というか System.Windows.Markup から書き換えも必要なのだろうか?

WPF から System.Xaml に移行した型

難しく説明しないでくれよ…
WPF の XAML 名前空間は以前と同じでイイということは解ったけど本当かな。

[XAML] WPF 4 を使用して、Windows 7 のジャンプ リストにカスタム タスクを追加する | 逆引きサンプル コード

上記とかでも 3.5 と同じだしイケそうだ。

後は IronPython で自分で書いた下記のようにソースコードに埋め込みしたいのだが。
IronPython で XAML

C# では Python の docstring みたく丸ごと XML とはいかない。
@”リテラル” で改行付き文字列はイケるけど ” を “” にする必要あり。

string (C#)

using System;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Markup;

class TextReader : Window
{
	private const string TXT = @"
		<TextBlock 
		xmlns=""http://schemas.microsoft.com/winfx/2006/xaml/presentation""
		xmlns:x=""http://schemas.microsoft.com/winfx/2006/xaml"">
	        Hello World
	   </TextBlock>";
	public TextReader()
	{
		this.Title = "XamlReader.Parse Test";
		this.Width = 400;
		this.Height = 400;
		try
		{
			var tt = XamlReader.Parse(TXT) as TextBlock;
			this.Content = tt;
			tt.Text = "こんにちは日本";
		}
		catch (Exception e)
		{
			MessageBox.Show(e.ToString());
		}
	}
}

class __main__
{
	[STAThread]
	public static void Main(string[] args)
	{
		var app = new Application();
		app.Run(new TextReader());
	}
}

これをビルドしても上手くいったけど XAML は通常 ” が多いし…
C# では別ファイルにして XamlReader.Load() のほうがいいな。

それと XAML から object を作ったら as で型を教えてあげないとメソッドが使えない。
IronPython は動的だからいらないけどコンパイルする C# では必須だね。

WPF 4.0 Text Stack Improvements – WPF Text Blog – Site Home – MSDN Blogs

とりあえずこの Text Rendering API ってのを試してみよう。
3.5 までは Windows XP での描写が変だったのがどうなるかだ。
私的にはもう XP はガン無視でいいけど、いつの OS だよ Linux 使えよ。

でも世間はそうじゃないし、しかたがないので XP に .NET 4.0 を入れる。
Windows Update に Client Profile ってのがあるんだね。

意外とサイズが小さいな、これなら仮想 OS な XP に入れてもいいや。
入れてみたら csc.exe なんかもしっかりあるのでコンパイルできると思う。

と思ったけど何このエラーは…

build した exe を持っていくと問題なく動くんだが、よくワカンネェ。
本当に XP って糞 OS だ、俺は XP で開発しないから無視しておこう。
てか Ubuntu 上の仮想マシンなので PC を行ったり来たりでメンドイ。
スクリーンショットも全部 Ubuntu から撮影してるし、関係無いか。

というわけでコード。

test.xaml

<StackPanel 
	xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
	xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
	TextOptions.TextFormattingMode="Display">
    <TextBlock Name="block">
        クリアータイプ ... ClearType
    </TextBlock>
    <TextBlock TextOptions.TextRenderingMode="Grayscale">
        白黒 ... Grayscale
    </TextBlock>
    <TextBlock TextOptions.TextRenderingMode="Aliased">
        ぎざぎざ ... Aliased
    </TextBlock>
</StackPanel>

src.cs

using System;
using System.IO;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Markup;

class TextRender : Window
{
	public TextRender()
	{
		this.Title = "てきすとれんだぁ!";
		this.Width = 300;
		this.Height = 200;
		try
		{
			var fs = new FileStream("test.xaml", FileMode.Open, FileAccess.Read);
			var sp = XamlReader.Load(fs) as StackPanel;
			this.Content = sp;
			(sp.FindName("block") as TextBlock).MouseUp += on_click;
		}
		catch (Exception e)
		{
			MessageBox.Show(e.ToString());
		}
	}
	private void on_click(object sender, RoutedEventArgs e)
	{
		MessageBox.Show("くりっく");
	}
}

class __main__
{
	[STAThread]
	public static void Main(string[] args)
	{
		var app = new Application();
		app.Run(new TextRender());
	}
}

まとめて ZIP
textrender.zip

描写だけじゃ寂しいので一番上の文字列をクリックしたらメッセージを出すように。
ココも as で型指定が必要だ、やはり XAML をコンパイルするのとは扱いが違うわな。

うん、XP でも綺麗なフォント表示が可能になっている。
XP だと個々の差は解りにくいけど 7 でははっきり違いが解る。
しかし画像を並べてみると本当に XP の UI って古臭いなぁ…
今日はこのへんで。

追記

ビルドするためのバッチに

build32.bat

C:\Windows\Microsoft.NET\Framework\v4.0.30319\csc.exe ^
/lib:C:\Windows\Microsoft.NET\Framework\v4.0.30319\WPF ^
/reference:System.dll ^
/reference:System.Xml.dll ^
/reference:System.Xaml.dll ^
/reference:PresentationCore.dll ^
/reference:PresentationFramework.dll ^
/reference:WindowsBase.dll ^
/target:winexe ^
/out:TextReader.exe ^
/win32icon:icon.ico ^
/optimize+ ^
src.cs
PAUSE

と System.dll と System.Xml.dll への参照を追記したら XP でもビルドできた。
何で 7 と違うのよ…

Windows 7 C# Build @ .NET4.0

正月といえば、やはりプログラミングですよね。

前回は .NET Framework 3.5 の WPF を使いました。
それは Windows 7 なら最初から必ず入っているからという理由もありますが。
実は .NET 4.0 でやってみたけど上手くいかなかったから…

やはり現在の最新技術である 4.0 を使ってみたいじゃないですか。
ということで色々と自分で実験してみた。
技術文書とかは読まず(解らないもん)自力で漁った結果と前置きして。

.NET 3.5 での reference はカレント及び csc.exe のディレクトリ
それと C:\Windows\assembly を探すようだ。

このファイルに見えるものは実態ではない、端末で見れば解る。
Linux ばかり使っていて良かった、以前なら端末で見ようなんて絶対に思わない。

サブディレクトリ内に実態がある、正直なんだかよく解らない。
とにかく 4.0 の csc.exe ではこの位置の PresentationCore.dll は見つけられない。

4.0 の csc.exe があるディレクトリをよく見ると、ぶっちゃけ下記だが
C:\Windows\Microsoft.NET\Framework64\v4.0.30319
に WPF というディレクトリがありソコにも PresentationCore.dll 等が。
ということはコッチを /lib オプションで指定すればいいようだ。

/lib 指定でビルド、System.Xaml.dll が見つからないとエラー…
って .NET 3.5 までは System.Xaml アセンブリなんて無かったような気がするんだが。
上記 assembly ディレクトリを漁ってもやはり見当たらないし。

WPF 4(VS 2010&.NET 4シリーズ) ? @IT

どうも .NET Framework 4.0 からのようで、XAML を使っていなくても必要なのね。
csc.exe のディレクトリ直下にある、ので System.Xaml.dll を参照追加指定だけ。
暗黙のリンクはやってくれない、何か事情があるのだろう。

ところで 64bit ディレクトリ指定にして配布は大丈夫なのだろうか?
exe に位置情報が埋め込まれていたりすると 32bit では動かないんだが。
32bit と 64bit の csc.exe でビルドして diff してみる。

exe 名以外は特に違いが見つからない、というかパス情報は無いみたい。
というかどちらも AnyCPU ビルドなんだからほとんど同じものが作られるんだね。
ネイティブで動くか WOW64 で動くかの違いだけなのかな?

どっちでも同じなら 32bit 位置指定のほうが心配が少なそうだから今後はそれで。
ということで 64 でないほうのディレクトリ指定でバッチを作る。

build32.bat

C:\Windows\Microsoft.NET\Framework\v4.0.30319\csc.exe ^
/lib:C:\Windows\Microsoft.NET\Framework\v4.0.30319\WPF ^
/reference:System.Xaml.dll ^
/reference:PresentationCore.dll ^
/reference:PresentationFramework.dll ^
/reference:WindowsBase.dll ^
/target:winexe ^
/out:TextReader.exe ^
/win32icon:icon.ico ^
/optimize+ ^
src.cs
PAUSE

v4.0.30319 というディレクトリ名は皆同じなんだろうか…
この Blog は自分で作って自分で使う(遊ぶ)人向けなのだからオケということで。

ということで今回は自分で復習のつもりでテキストリーダーを作ってみた。
UTF-8 以外は文字化けします、私は全部 UTF-8 保存しているので問題無し。
エントリポイントが解りにくいので __main__ という名前にしたw

src.cs

using System;
using System.IO;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
using Microsoft.Win32;

class TextReader : Window
{
	private TextBox _textbox;

	public TextReader()
	{
		this.Title = "てきすとりぃだぁ!";
		this.Width = 400;
		this.Height = 400;
		this.CreateControls();
	}
	private void CreateControls()
	{
		_textbox = new TextBox();
		_textbox.TextWrapping = TextWrapping.NoWrap;
		_textbox.AcceptsReturn = true;
		_textbox.VerticalScrollBarVisibility = ScrollBarVisibility.Auto;
		_textbox.HorizontalScrollBarVisibility = ScrollBarVisibility.Auto;
		// Button
		var button = new Button();
		button.Content = "Open the Text";
		//button.Click += new RoutedEventHandler(on_button_click);
		button.Click += on_button_click;
		DockPanel.SetDock(button, Dock.Top);
		// DockPanel
		var dockpanel = new DockPanel();
		// Append
		dockpanel.Children.Add(button);
		dockpanel.Children.Add(_textbox);
		this.Content = dockpanel;
	}
	void on_button_click(object sender, RoutedEventArgs e)
	{
		var dlg = new OpenFileDialog();
		var res = dlg.ShowDialog(this);
		if (res.Value)
		{
			var sr = new StreamReader(dlg.FileName, Encoding.UTF8);
			_textbox.Text = sr.ReadToEnd();
			sr.Close();
		}
	}
}

class __main__
{
	[STAThread]
	public static void Main(string[] args)
	{
		var app = new Application();
		app.Run(new TextReader());
	}
}

textreader.zip

new RoutedEventHandler() を使わずにイベントハンドラが追加できたんだが…
以前からそうなのかな、VisualStudio だと強制的に書き込まれるはずなんだが。
余計な new が少ないのは嬉しいよ。

.NET 4.0 なので Microsoft.Win32 名前空間のダイアログも最新だ。
それ以外は 4.0 の恩恵が何も無いという…

3.0 時のような劇的進化は特に無いし 3.5 でイイやとか思ってしまうよ。
とにかく .NET 4.0 でも何もインストールせずに遊ぶ方法が解ったのでよし。