Java
C#
Properties

C# で Java の properties ファイルを読む

はじめに

Stack Overflow に Can .NET load and parse a properties file equivalent to Java Properties class?” (January, 2009) という質問があり、Java の properties ファイルを C# で読む方法を尋ねている。質問者のように、既存の properties ファイルに変更を加えずにそのまま C# で読みたいというケースは存在する。

当該質問にはコード付きの回答が幾つか寄せられているが、私の回答を除き、いずれの実装も実用には耐えられない。存在しない仕様を勝手に追加しているものまである。;' をコメント行開始文字として扱ったり、プロパティー値を囲む引用符を勝手に削除したり。

やはり自作するしかない。

Properties ファイルの仕様

Java properties ファイルの仕様は java.util.Properties.load(java.io.Reader) の JavaDoc に書かれている。問題は、みんなが想像するよりも少し仕様が複雑だということだ。全てを網羅しているわけではないが、特に考慮すべき点は次のとおり。

  1. 行には、自然行論理行の二種類がある。
  2. 自然行は \n, \r, \r\n もしくはストリームの末尾で区切られる。
  3. 論理行は、バックスラッシュを使用して行末記号シーケンスをエスケープすることで、隣接する複数の自然行にまたがることがある。
  4. 論理行内の二番目以降の自然行の先頭の空白文字群は捨てられる。
  5. 空白文字とは、スペース (, \u0020)、タブ (\t, \u0009)、ラインフィード (\f, \u000C) である。
  6. 仕様で明確に述べられているように、「行末記号がエスケープされているかどうかを判定する場合、行末記号シーケンスの前の文字を調べるだけでは十分ではありません。行末記号がエスケープされるためには、連続した奇数のバックスラッシュが存在する必要があります。入力は左から右に処理されるため、行末記号の前(またはほかの場所)に連続したバックスラッシュが2n (ゼロでない偶数)個存在する場合、エスケープ処理後にn個のバックスラッシュがエンコードされます。」
  7. = がキーと値の区切り文字として使用される。
  8. : もキーと値の区切り文字として使用される。
  9. キーと値の間の区切り文字は省略可能。
  10. コメント行は、最初の非空白文字として # もしくは ! を持つ。つまり、#! の前の空白文字群は許容される。
  11. 行末記号をバックスラッシュでエスケープしても、コメント行を複数行にまたがらせることはできない。
  12. 仕様で明確に述べらているとおり、=:、空白文字もバックスラッシュでエスケープすることによりキーに埋め込むことができる。
  13. 行末記号でさえ、\r\n というエスケースシーケンスを用いることで含めることができる。
  14. 値が省略された場合、空文字列として扱う。
  15. \uxxxx は Unicode 文字をあらわす。
  16. 有効なエスケープ文字を構成しない文字の前に置かれたバックスラッシュはエラーとして扱われず、単にドロップされる。

Properties ファイルの例

そういうわけで、例えば test.properties が次の内容を持っている場合

# A comment line that starts with '#'.
   # This is a comment line having leading white spaces.
! A comment line that starts with '!'.

key1=value1
  key2 : value2
    key3 value3
key\
  4=value\
    4
\u006B\u0065\u00795=\u0076\u0061\u006c\u0075\u00655
\k\e\y\6=\v\a\lu\e\6

\:\ \= = \\colon\\space\\equal

次のキーバリューペア群として解釈されるべきである。

キー
key1 value1
key2 value2
key3 value3
key4 value4
key5 value5
key6 value6
: = \colon\space\equal

コード例

NuGet パッケージ「Authlete.Authlete」に含まれる PropertiesLoader クラスは Properties ファイルの仕様を解釈することができる。次のコード例は

using System;
using System.IO;
using System.Collections.Generic;
using Authlete.Util;

namespace MyApp
{
    class Program
    {
        public static void Main(string[] args)
        {
            string file = "test.properties";
            IDictionary<string, string> properties;

            using (TextReader reader = new StreamReader(file))
            {
                properties = PropertiesLoader.Load(reader);
            }

            foreach (var entry in properties)
            {
                Console.WriteLine($"{entry.Key} = {entry.Value}");
            }
        }
    }
}

次の出力を生成する。

key1 = value1
key2 = value2
key3 = value3
key4 = value4
key5 = value5
key6 = value6
: = = \colon\space\equal

Java での同様のコードは次のとおり。

import java.util.*;
import java.io.*;

public class Program
{
    public static void main(String[] args) throws IOException
    {
        String file = "test.properties";
        Properties properties = new Properties();

        try (Reader reader = new FileReader(file))
        {
             properties.load(reader);
        }

        for (Map.Entry<Object, Object> entry : properties.entrySet())
        {
            System.out.format("%s = %s\n", entry.getKey(), entry.getValue());
        }    
    }
}

ソースコード

PropertiesLoader.cs のソースコードは authlete-csharp 内に存在する。また、PropertiesLoader 用の xUnit テスト群は PropertiesLoaderTest.cs 内に記述されている。