Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@
## テスト

- `dotnet test` で実行(xUnit v3、net10.0)。`global.json` で Microsoft.Testing.Platform を指定しているため、`Microsoft.NET.Test.Sdk` や `xunit.runner.visualstudio` は追加しない。
- `TestData/cases.tsv` と `v1-*.png` は 1.x(.NET Framework 版)の出力で、シンボル列と画像がこれとピクセル単位で一致することを確認している。出力が変わる修正をした場合は、意図した変更か確認してから期待値を更新する。
- `TestData/cases.tsv` と `v1-*.png` は 1.x(.NET Framework 版)の出力で、シンボル列と画像がこれとピクセル単位で一致することを確認している。1.x がマニュアルと異なっていたケース(補足ルール対象など)は除いてあり、それらは `SupplementaryRuleTests` でマニュアルの「バーコード・データ」を期待値にしている。出力が変わる修正をした場合は、マニュアルに照らして意図した変更か確認してから期待値を更新する。
- `cases.tsv` はタブ区切り(郵便番号・住所・シンボル列・画像ファイル名)。住所が空の行があるので、シェルで加工するときは連続するタブがまとめられないよう注意する。
- System.Drawing のテストは Windows 以外ではスキップされる。

Expand All @@ -29,8 +29,8 @@

## バーコード処理

- 住所の変換(`BarCode.cs`)は日本郵便のバーコードマニュアル(http://www.post.japanpost.jp/zipcode/zipmanual/p19.html)に従う。コード中の番号付きコメント(1.〜6.)はマニュアルの手順番号に対応している。
- 住所の変換(`BarCode.cs`)は日本郵便のバーコードマニュアル(http://www.post.japanpost.jp/zipcode/zipmanual/p19.html)に従う。コード中の番号付きコメント(1.〜6.、補足ルール1〜5)はマニュアルの手順番号に対応している。
- マニュアルでは住所を「住所A(町域名まで)」と「住所B(町域名以降)」に分け、住所Bだけから抜き出す。`Create(郵便番号, 住所)` は住所全体を住所Bとして扱うため、町域名に特定文字の前の漢数字を含む住所(一番町、八丁堀など)では誤る。これは既知の制限で、`Create(郵便番号, 住所A, 住所B)` を推奨している。
- バーコードは常に 23 シンボル: スタート + 郵便番号 7 桁 + 住所表示番号 13 枠(不足分は `CC4` で埋める)+ チェックデジット + ストップ。
- バーの形は `CustomerBarCode.cs` の `BarPatternDic` で定義(1 シンボル = 幅 12px に 3 本、x=0/4/8、幅 2px、上・中・下段 各 4px)。この表は旧バージョンの GIF 画像とピクセル単位で一致することを確認済みなので、変更するときは既存出力と比較する。
- 描画パッケージは `GetBars()` の矩形を白地に黒で塗るだけにし、バーコードのロジックはコアに置く。
- 住所中の漢数字には未対応(README に記載の既知の制限)。
16 changes: 13 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,11 +23,22 @@ SkiaSharp を Linux で使う場合は、利用側で `SkiaSharp.NativeAssets.Li
```csharp
using ZipConvertCustomerBarCode;

// 郵便番号,住所
CustomerBarCode code = BarCode.Create("0140113", "秋田県大仙市堀見内 南田茂木 添60-1");
// 郵便番号, 住所A(町域名までの住所), 住所B(町域名以降の住所)
CustomerBarCode code = BarCode.Create("1000013", "東京都千代田区霞が関", "1丁目3番2号 郵便プラザ503室");
```
郵便番号が7桁の数値でない場合は `ArgumentException` になります。

住所表示番号は、[郵便番号・バーコードマニュアル](https://www.post.japanpost.jp/zipcode/zipmanual/p19.html)の抜き出しルール(補足ルールを含む)に従って住所Bから抜き出します。
「丁目・番地・号」などの前の漢数字は算用数字に変換し(例:十一丁目 → 11)、「2F」のような階数の F は抜き出しません。

住所を分けずに渡すこともできます。

```csharp
CustomerBarCode code = BarCode.Create("1000013", "東京都千代田区霞が関1丁目3番2号 郵便プラザ503室");
```

ただしこの場合は住所全体を住所Bとして扱うため、町域名に「丁目・番・ノ」などの前の漢数字を含む住所(一番町、八丁堀、三ノ輪 など)では、町域名の漢数字も抜き出されて誤ったバーコードになります。住所A・住所Bに分けて渡すことをおすすめします。

### SVG(依存ライブラリなし)
```csharp
string svg = code.ToSvg();
Expand Down Expand Up @@ -69,5 +80,4 @@ foreach (var bar in code.GetBars())
| `BarCode.Export(zip, address, path)` | `BarCode.Create(zip, address).Save(path, ImageFormat.Gif)`(Drawing) |

## 備考
* 漢数字未対応
* 住所→郵便番号変換辞書検討中(郵便番号の入力が不要に)
134 changes: 134 additions & 0 deletions ZipConvertCustomerBarCode.Tests/SupplementaryRuleTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,134 @@
using System.Text;

namespace ZipConvertCustomerBarCode.Tests
{
// 抜き出しの基本ルール・補足ルール
// 期待値はマニュアルの「バーコード・データ」
// http://www.post.japanpost.jp/zipcode/zipmanual/p19.html
public class SupplementaryRuleTests
{
[Theory]
// 基本ルール(p17、p19)
[InlineData("1000013", "東京都千代田区霞が関1丁目3番2号 郵便プラザ503室", "1-3-2-503")]
[InlineData("2630023", "千葉市稲毛区緑町3丁目30-8 郵便ビル403号", "3-30-8-403")]
[InlineData("2730102", "千葉県鎌ヶ谷市右京塚 東3丁目-20-5 郵便・A&bコーポB604号", "3-20-5B604")]
// 補足ルール1: 特定文字の前の漢数字は算用数字に変換する
[InlineData("1980036", "東京都青梅市河辺町十一丁目六番地一号 郵便タワー601", "11-6-1-601")]
[InlineData("0800831", "北海道帯広市稲田町南七線 西28", "7-28")]
// 補足ルール2: 算用数字に続く"F"は抜き出さない
[InlineData("3170055", "茨城県日立市宮田町6丁目7-14 ABCビル2F", "6-7-14-2")]
// 補足ルール3: "F"以降に抜き出す文字があれば"F"はハイフンにする
[InlineData("3170055", "茨城県日立市宮田町6丁目7-14 ABCビル2F201号室", "6-7-14-2-201")]
// 補足ルール4: アルファベット前後のハイフンは取り除く
[InlineData("6500046", "神戸市中央区港島中町9丁目7-6 郵便シティA棟1F1号", "9-7-6A1-1")]
// 補足ルール5: その結果2文字以上連続したアルファベットはそのまま
[InlineData("6230011", "京都府綾部市青野町綾部6-7 LプラザB106", "6-7LB106")]
// 13けたで切り捨て(p20)
[InlineData("0640804", "札幌市中央区南四条西29丁目1524-23 第2郵便ハウス501", "29-1524-23-2-")]
public void マニュアルの例どおりに抜き出す(string zipCode, string address, string expected)
{
var code = BarCode.Create(zipCode, address);

Assert.Equal(expected, ToBarCodeData(code));
AssertCheckDigit(code);
}

[Theory]
[InlineData("二〇五号", "205")]
[InlineData("十番地", "10")]
[InlineData("十一丁目", "11")]
[InlineData("二十三番", "23")]
[InlineData("百二番地", "102")]
[InlineData("千二百三十四番", "1234")]
[InlineData("三ノ二番", "3-2")]
[InlineData("一の三号", "1-3")]
[InlineData("第二地割", "2")]
[InlineData("四丁", "4")]
// 特定文字の前でない漢数字は抜き出さない
[InlineData("北六条東4丁目", "4")]
public void 漢数字を算用数字に変換する(string address, string expected)
{
var code = BarCode.Create("1234567", address);

Assert.Equal(expected, ToBarCodeData(code));
}

[Theory]
[InlineData("2F", "2")]
[InlineData("2F201", "2-201")]
[InlineData("2F-3", "2-3")]
// 数字に続かない"F"、2文字以上連続する"F"は対象外
[InlineData("F棟2", "F2")]
[InlineData("2FG棟3", "2-3")]
public void 数字に続くFの扱い(string address, string expected)
{
var code = BarCode.Create("1234567", address);

Assert.Equal(expected, ToBarCodeData(code));
}

[Fact]
public void 住所Bだけから抜き出す()
{
// 町域名に特定文字の前の漢数字を含む
Assert.Equal("5-3", ToBarCodeData(BarCode.Create("1020082", "東京都千代田区一番町", "5-3")));
Assert.Equal("2-1-1", ToBarCodeData(BarCode.Create("1040032", "東京都中央区八丁堀", "2丁目1-1")));
Assert.Equal("1-2", ToBarCodeData(BarCode.Create("1100011", "東京都台東区三ノ輪", "1丁目2")));
}

[Fact]
public void 住所Bが住所Aで始まる場合は住所Aを取り除く()
{
var code = BarCode.Create("1040032", "東京都中央区八丁堀", "東京都中央区八丁堀2丁目1-1");

Assert.Equal("2-1-1", ToBarCodeData(code));
}

[Fact]
public void 住所全体を渡すと町域名の漢数字も抜き出される()
{
// 2引数版の既知の制限(XMLコメントとREADMEに記載)
var code = BarCode.Create("1020082", "東京都千代田区一番町5-3");

Assert.Equal("1-5-3", ToBarCodeData(code));
}

[Fact]
public void 住所Aと住所Bがnullなら例外()
{
Assert.Throws<ArgumentNullException>(() => BarCode.Create("1020082", null!, "5-3"));
Assert.Throws<ArgumentNullException>(() => BarCode.Create("1020082", "東京都千代田区一番町", null!));
}

// 住所表示番号のシンボルをバーコード・データの文字列に戻す(CC4 の埋め草は除く)
private static string ToBarCodeData(CustomerBarCode code)
{
var symbols = code.Symbols.Skip(8).Take(13).ToList();
var sb = new StringBuilder();
for (int i = 0; i < symbols.Count && symbols[i] != "CC4"; i++)
{
var s = symbols[i];
if (s.StartsWith("CC"))
{
// 英字用制御コード + 数字でアルファベット1文字
var control = s[2] - '1';
var digit = symbols[++i][0] - '0';
sb.Append((char)('A' + control * 10 + digit));
}
else
{
sb.Append(s);
}
}
return sb.ToString();
}

// 郵便番号・住所表示番号・チェックデジットの合計が19の倍数
private static void AssertCheckDigit(CustomerBarCode code)
{
var sum = code.Symbols.Skip(1).Take(21).Sum(s =>
s == "-" ? 10 : s.StartsWith("CC") ? 10 + (s[2] - '0') : s[0] - '0');
Assert.Equal(0, sum % 19);
}
}
}
5 changes: 0 additions & 5 deletions ZipConvertCustomerBarCode.Tests/TestData/cases.tsv
Original file line number Diff line number Diff line change
@@ -1,15 +1,10 @@
0140113 秋田県大仙市堀見内 南田茂木 添60-1 STC 0 1 4 0 1 1 3 6 0 - 1 CC4 CC4 CC4 CC4 CC4 CC4 CC4 CC4 CC4 CC8 SPC v1-0.png
2630023 千葉市稲毛区緑町3丁目30-8 郵便ビル403号 STC 2 6 3 0 0 2 3 3 - 3 0 - 8 - 4 0 3 CC4 CC4 CC4 5 SPC v1-1.png
0140113 秋田県大仙市堀見内南田茂木添60-1 STC 0 1 4 0 1 1 3 6 0 - 1 CC4 CC4 CC4 CC4 CC4 CC4 CC4 CC4 CC4 CC8 SPC v1-2.png
1100016 東京都台東区台東5-6-3 ABCビル10F STC 1 1 0 0 0 1 6 5 - 6 - 3 - 1 0 CC1 5 CC4 CC4 CC4 2 SPC v1-3.png
0600906 北海道札幌市東区北六条東4丁目 郵便センター6号館 STC 0 6 0 0 9 0 6 4 - 6 CC4 CC4 CC4 CC4 CC4 CC4 CC4 CC4 CC4 CC4 9 SPC v1-4.png
0650006 北海道札幌市東区北六条東8丁目 郵便センター10号館 STC 0 6 5 0 0 0 6 8 - 1 0 CC4 CC4 CC4 CC4 CC4 CC4 CC4 CC4 CC4 9 SPC v1-5.png
4070033 山梨県韮崎市龍岡町下條南割 韮崎400 STC 4 0 7 0 0 3 3 4 0 0 CC4 CC4 CC4 CC4 CC4 CC4 CC4 CC4 CC4 CC4 - SPC v1-6.png
2730102 千葉県鎌ケ谷市右京塚 東3丁目-20-5 郵便・A&bコーポB604号 STC 2 7 3 0 1 0 2 3 - 2 0 - 5 CC1 1 6 0 4 CC4 CC4 0 SPC v1-7.png
1980036 東京都青梅市河辺町十一丁目六番地一号 郵便タワー601 STC 1 9 8 0 0 3 6 6 0 1 CC4 CC4 CC4 CC4 CC4 CC4 CC4 CC4 CC4 CC4 CC6 SPC v1-8.png
0270203 岩手県宮古市大字津軽石第二地割原 郵便番号CD-1 STC 0 2 7 0 2 0 3 1 CC4 CC4 CC4 CC4 CC4 CC4 CC4 CC4 CC4 CC4 CC4 CC4 7 SPC v1-9.png
5900016 大阪府堺市堺区中田出井町四丁 AB 101-1 STC 5 9 0 0 0 1 6 1 0 1 - 1 CC4 CC4 CC4 CC4 CC4 CC4 CC4 CC4 6 SPC v1-10.png
0800831 北海道帯広市稲田町南七線 西28 STC 0 8 0 0 8 3 1 2 8 CC4 CC4 CC4 CC4 CC4 CC4 CC4 CC4 CC4 CC4 CC4 6 SPC v1-11.png
3170055 茨城県日立市宮田町6丁目7-14 ABC建設A棟 STC 3 1 7 0 0 5 5 6 - 7 - 1 4 CC1 0 CC4 CC4 CC4 CC4 CC4 CC2 SPC v1-12.png
6500046 神戸市中央区港島中町9丁目7-6 郵便シティ A-1101 STC 6 5 0 0 0 4 6 9 - 7 - 6 CC1 0 1 1 0 1 CC4 CC4 9 SPC v1-13.png
6230011 京都府綾部市青野町綾部6-7 LプラザB106 STC 6 2 3 0 0 1 1 6 - 7 CC2 1 CC1 1 1 0 6 CC4 CC4 CC4 4 SPC v1-14.png
Expand Down
Binary file removed ZipConvertCustomerBarCode.Tests/TestData/v1-10.png
Binary file not shown.
Binary file removed ZipConvertCustomerBarCode.Tests/TestData/v1-11.png
Binary file not shown.
Binary file removed ZipConvertCustomerBarCode.Tests/TestData/v1-3.png
Binary file not shown.
Binary file removed ZipConvertCustomerBarCode.Tests/TestData/v1-8.png
Binary file not shown.
Binary file removed ZipConvertCustomerBarCode.Tests/TestData/v1-9.png
Binary file not shown.
Loading