challenge 初期設定の読み書き

 初期設定を読み書きするプログラムを書いてください。

 保存先や形式は問いませんが,OS,ライブラリ,言語等の環境で標準的なものがあれば,なるべくそちらを用いてください。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
package org.doukaku.ja.preference;

import java.util.prefs.Preferences;
import java.util.prefs.BackingStoreException;

public class HelloPreference {
    private static final String MESSAGE_KEY = "message";
    private static final String DEFAULT_MESSAGE = "Hello, Preference.";

    private String message;
    private Preferences pref;

    public HelloPreference() {
        loadPreference();
    }

    public void loadPreference() {
        setPreference(Preferences.userNodeForPackage(this.getClass()));
        setMessage(pref.get(MESSAGE_KEY, DEFAULT_MESSAGE));
    }

    public void setMessage(String message) { this.message = message; }
    public String getMessage() { return this.message; }
    public void setPreference(Preferences pref) { this.pref = pref; }
    public Preferences getPreference() { return this.pref; }

    public void showMessage() {
        System.out.println(getMessage());
    }
    public void storePreference() throws BackingStoreException {
        Preferences pref = getPreference();
        pref.put(MESSAGE_KEY, getMessage());
        pref.flush();
    }

    public static void main(String[] args) {
        try {
            HelloPreference    hello = new HelloPreference();
            if (args.length > 0) {
                hello.setMessage(args[0]);
            }
            hello.showMessage();
            hello.storePreference();
        } catch (Exception ex) {
            ex.printStackTrace();
        }
    }
}

Posted feedbacks - Nested

Flatten Hidden

「初期設定」というのがなにを想定しているのか全くわからないです。出題の意図も見えません。もうすこし具体的な設定にならないでしょうか。

java.util.prefs.Preferencesでググってきました。どうやら、アプリケーション固有の設定情報(httpd.confや.vimrcのような)を読み書きするものらしいと理解しました。

というわけで、WindowsではINIファイルやレジストリ ("HKCUSoftware会社名アプリ名"以下)を専用のAPIで扱えば題意を満たすと考えました。この投稿では、Win16の老害、INIファイルを読み書きするGetPrivateProfileStringとWritePrivateProfileString関数を使っています。言語はActiveBasic 4です。

プログラムの内容は、(保存してある)前回のコマンドライン引数を表示した後、今回のコマンドライン引数を保存するというものです。ただし、初回起動時には(none)と表示されます。このプログラムで、例えばmyapp.exe hoge foo barと実行すると、次のようなMYAPP.INIが作成されます:

[option]
LastCommandLineArgs=hoge foo bar

なお、ファイル名は必ず絶対パスで指定します。そうしないと、Windowsディレクトリを基準とした相対パスになるはずです。NT系だとユーザ別のフォルダ(Document and Settings以下)になったような気もします。

さらに余談ですが、Program Files以下へインストールされるアプリでは、実行ファイルと同じフォルダにINIを置いてはいけません。それをやると、いかにもVistaでまともに動かない(XPまででも制限ユーザでは使えない)というダメダメなアプリの一丁上がりです。個人的には、アプリと同じフォルダに設定ファイルを置くのはインストーラ無しのフリーソフトの特権だと思っています。長々とした文章ですみませんでした。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
#console

Declare Function WritePrivateProfileString Lib "kernel32" Alias "WritePrivateProfileStringA" (
    lpAppName As *Byte,
    pKeyName As *Byte,
    lpString As *Byte,
    lpFileName As *Byte) As BOOL
Declare Function GetPrivateProfileString Lib "kernel32" Alias "GetPrivateProfileStringA" (
    lpAppName As *Byte,
    lpKeyName As *Byte,
    lpDefault As *Byte,
    lpReturnedString As *Byte,
    nSize As DWord,
    lpFileName As *Byte) As DWord

' PathGetArgsの利用にはIE4以上またはWindows 98/2000以上が必要。
Declare Function PathGetArgs Lib "shlwapi" Alias "PathGetArgsA" (pszPath As *Byte) As *Byte

Const BUF_SIZE = 1024

Dim lastCmdLine As String
lastCmdLine = ZeroString(BUF_SIZE)
GetPrivateProfileString("option", "LastCommandLineArgs", "(none)", StrPtr(lastCmdLine), BUF_SIZE , "H:\MYAPP.INI")
' "(none)"はファイル自体や中のエントリが見付からなかったときにlastCmdLineに格納する内容を指定する引数

Print "Last command line:"; lastCmdLine

Dim cmdLine As BytePtr
' PathGetArgsでコマンドライン引数から自身の実行ファイル名(Cでいうargv[0])の部分を取り除いている。
cmdLine = PathGetArgs(GetCommandLine())

WritePrivateProfileString("option", "LastCommandLineArgs", cmdLine, "H:\MYAPP.INI")

ああorz、投稿と間違えました。ごめんなさい。

設定ファイルを読み込むお題であれば過去に出ていますが、それとは違くて?

参考ページを見ると、設定ファイルがなくても設定を読み書きできる方法のことを言っている?
バッチファイルでレジストリの値を取得するコードを書きました。reg.exe呼んでるだけですが。
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
===== getreg.bat =====
@echo off
rem reg.exeの結果を1行ごと%rに格納する
for /F "usebackq delims=" %%r in (`reg.exe QUERY %1 /v %2`) do (
  rem %rをタブ区切りデータとみなし、3列目を%tに格納する
  for /F "tokens=3 delims=    " %%t in ("%%r%") do (
    rem %tが空でなければ、レジストリ値だとみなす
    if not "%%t%" == "" (
      echo %%t%
      goto e
    )
  )
)
:e

===== 使用例 caller.bat =====
@echo off
for /F "usebackq delims=" %%v in (`call getreg "HKEY_CURRENT_USER\Control Panel\Desktop" Wallpaper`) do set WALLPAPER=%%v%
echo 壁紙=[%WALLPAPER%]
for /F "usebackq delims=" %%v in (`call getreg "HKEY_CURRENT_USER\Control Panel\Colors" ActiveTitle`) do set ACTIVETITLE=%%v%
echo タイトルバーの色=[%ACTIVETITLE%]
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
import pickle

class Setting: pass

setting = Setting()
setting.message = 'Hello'
with open(r'.setting', 'w') as f:
    pickle.dump(setting, f)

with open('.setting') as f:
    s = pickle.load(f)
    print s.message

Squeak Smalltalk で。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
"設定の作成(真偽値の場合)"
Preferences
    addBooleanPreference: #myBoolPref
    categories: #(unclassified)
    default: false
    balloonHelp: 'This is a sample boolean property.'


"設定値の変更(真偽値の場合)"
Preferences enable: #myBoolPref.
Preferences disable: #myBoolPref.


"現在の全設定の状態をファイルへ保存"
Preferences storePreferencesToDisk

"ファイルから全設定の状態を読み込み"
Preferences restorePreferencesFromDisk

お題のJavaを.NETに移植した感じ。 System.Configuration.dllを参照してビルドすること。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
using System;
using System.Configuration;

class HelloPreference
{
  const string MESSAGE_KEY = "message";
  const string DEFAULT_MESSAGE = "Hello, Preference.";

  public string Message { get; set; }
  public Configuration Configuration { get; set; }

  public HelloPreference()
  {
    LoadPreference();
  }

  public void LoadPreference()
  {
    Configuration conf = ConfigurationManager
      .OpenExeConfiguration(ConfigurationUserLevel.None);
    this.Configuration = conf;
    KeyValueConfigurationElement element
      = conf.AppSettings.Settings[MESSAGE_KEY];
    this.Message = element != null
      ? element.Value
      : DEFAULT_MESSAGE;
  }

  public void StorePreference()
  {
    Configuration conf = this.Configuration;
    KeyValueConfigurationCollection settings
      = conf.AppSettings.Settings;
    KeyValueConfigurationElement elem
      = settings[MESSAGE_KEY];
    if (elem != null)
    {
      elem.Value = this.Message;
    }
    else
    {
      settings.Add(MESSAGE_KEY, this.Message);
    }
    conf.Save();
  }

  public void ShowMessage()
  {
    Console.WriteLine(this.Message);
  }

  static void Main(string[] args)
  {
    try
    {
      HelloPreference hello = new HelloPreference();
      if (args.Length > 0)
      {
        hello.Message = args[0];
      }
      hello.ShowMessage();
      hello.StorePreference();
    }
    catch (Exception ex)
    {
      Console.WriteLine(ex);
    }
  }
}

言語標準のやり方ということなので、Python標準添付のini書式設定ファイルのパーサを使った方法を示します。例とインターフェースを揃えるためにラッパを書いてますが、蛇足だったかも。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
import sys, os, ConfigParser

class HelloPreference:
    
    path_cfg = os.path.join(os.path.expanduser('~'),
        'org.doukaku.ja.preference.HelloPreference')
    cfg = ConfigParser.ConfigParser()
    
    def __init__(self):
        self.cfg.read(self.path_cfg)
    
    def __getattr__(self, attr):
        if self.cfg.has_option('default', attr):
            return self.cfg.get('default', attr)
        else:
            return None
    
    def __setattr__(self, attr, value):
        if not self.cfg.has_section('default'):
            cfg.add_section('default')
        self.cfg.set('default', attr, value)
    
    def store(self):
        f = file(self.path_cfg, 'w')
        self.cfg.write(f)
        f.close()

hello_prefs = HelloPreference()

if len(sys.argv) > 1:
    hello_prefs.message = sys.argv[1]

print hello_prefs.message
hello_prefs.store()

すみません、ログインせずに書いたうえ、バグありでした。__setattr__の正しい実装はこうです。

1
2
3
4
    def __setattr__(self, attr, value):
        if not self.cfg.has_section('default'):
            self.cfg.add_section('default')
        self.cfg.set('default', attr, value)
YAML形式の設定ファイルで。
設定をハッシュにして読み込み/保存しています。
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
require 'yaml'

MESSAGE_KEY = 'message'
DEFAULT_MESSAGE = 'Hello, Preference.'
filename = 'conf.yaml'

conf = File.exist?(filename) ? YAML.load_file(filename) : { MESSAGE_KEY => DEFAULT_MESSAGE }
conf[MESSAGE_KEY] = ARGV[0] if ARGV.size > 0
p conf
open(filename, 'w') { |f| f.puts YAML.dump(conf) }

Groovy標準の設定ファイル形式ConfigSlurperでConfigObjectに読みこんでWriterに書き出してみます。

http://groovy.codehaus.org/gapi/groovy/util/ConfigObject.html http://groovy.codehaus.org/gapi/groovy/util/ConfigSlurper.html

正規化されて以下のような出力となります。 -- $ groovy config.groovy grails {

System Message: ERROR/3 (<string>, line 10)

Unexpected indentation.
webflow {
stateless=true stateless2=true

System Message: WARNING/2 (<string>, line 13)

Definition list ends without a blank line; unexpected unindent.

}

System Message: WARNING/2 (<string>, line 14)

Block quote ends without a blank line; unexpected unindent.

} smtp {

System Message: ERROR/3 (<string>, line 16)

Unexpected indentation.
mail {
host="smtp.myisp.com" auth.user="server"

System Message: WARNING/2 (<string>, line 19)

Definition list ends without a blank line; unexpected unindent.

}

System Message: WARNING/2 (<string>, line 20)

Block quote ends without a blank line; unexpected unindent.

} resources.URL="http://localhost:80/resources"

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
def config = '''
   grails.webflow.stateless = true
   grails.webflow.stateless2 = true
   smtp {
       mail.host = 'smtp.myisp.com'
       mail.auth.user = 'server'
   }
   resources.URL = "http://localhost:80/resources"
'''
sw = new StringWriter()
new ConfigSlurper().parse(config).writeTo(sw)

println sw
(出し直し)
Groovy標準の設定ファイル形式ConfigSlurperでConfigObjectに読みこんでWriterに書き出してみます。
正規化されて以下のような出力となります。 

$ groovy config.groovy
grails {
	webflow {
		stateless=true
		stateless2=true
	}
}
smtp {
	mail {
		host="smtp.myisp.com"
		auth.user="server"
	}
}
resources.URL="http://localhost:80/resources"


 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
def config = '''
   grails.webflow.stateless = true
   grails.webflow.stateless2 = true
   smtp {
       mail.host = 'smtp.myisp.com'
       mail.auth.user = 'server'
   }
   resources.URL = "http://localhost:80/resources"
'''
sw = new StringWriter()
new ConfigSlurper().parse(config).writeTo(sw)

println sw

Index

Feed

Other

Link

Pathtraq

loading...