StructuredStringLexer 0.0.1
dotnet add package StructuredStringLexer --version 0.0.1
NuGet\Install-Package StructuredStringLexer -Version 0.0.1
<PackageReference Include="StructuredStringLexer" Version="0.0.1" />
<PackageVersion Include="StructuredStringLexer" Version="0.0.1" />
<PackageReference Include="StructuredStringLexer" />
paket add StructuredStringLexer --version 0.0.1
#r "nuget: StructuredStringLexer, 0.0.1"
#:package StructuredStringLexer@0.0.1
#addin nuget:?package=StructuredStringLexer&version=0.0.1
#tool nuget:?package=StructuredStringLexer&version=0.0.1
StructuredStringLexer
概要
StructuredStringLexer<TEnum> は、正規表現を利用して、書式を持つ文字列を安全かつ確実にトークン列へ分解するための汎用文字列レクサーです。
日付書式、ファイル名テンプレート、ログファイル名テンプレートなど、語順や区切りが固定ではないが一定の書式規則を持つ文字列を、正規表現にマッチする「抽出対象トークン」と、それ以外の「解析継続トークン」に分割します。
特徴・主な機能
- 任意の列挙型
TEnumによるトークン種別管理 - 正規表現に一致した部分だけを指定トークン種別として抽出
- 未一致部分を解析継続用トークンとして保持
- 複数段階の字句解析に対応しやすい設計
- トークン列の複製による副作用抑制
RegexOptions.RightToLeftを明示的に非対応化- 空文字マッチや逆行・重複マッチを検出する安全設計
SafeRegex利用を前提にしつつ、コンパイル条件で標準Regexに切替可能
目的
- 書式付き文字列を、後続処理しやすいトークン列へ変換します。
- 正規表現による部分抽出を、汎用的かつ再利用しやすい形で共通化します。
- 日付書式、環境変数、アプリケーション独自プレースホルダーなどを段階的に解析するための基盤として利用できます。
背景
通常の Regex.Match() や Regex.Matches() は、マッチした部分の抽出には便利ですが、未マッチ部分を含めて「元文字列の順序を保ったトークン列」として扱う用途では、周辺処理を自前で実装する必要があります。
StructuredStringLexer<TEnum> は、次のような処理を簡潔に扱うための内部ユーティリティです。
- マッチ部分と未マッチ部分を交互に保持する
- 未マッチ部分を次段階の解析対象として残す
- トークン種別を列挙型で明示的に管理する
- 不正な正規表現や想定外のマッチ挙動を早期に検出する
主要クラス
CharacterStringToken<TEnum>
文字列トークンを表す内部クラスです。
このクラスは、次の2つだけを保持します。
TokenType
書式トークン種別LexemeFormat
書式トークンの原文
正規表現パターン化、レンダリング、出力値生成などの責務は持ちません。
StructuredStringLexer<TEnum>
文字列または既存のトークン列を、正規表現に基づいて分割する内部静的クラスです。
TEnum は Enum 制約を持ち、利用側で定義した列挙型をトークン種別として使用します。
基本的な考え方
最初に、入力文字列全体を「基底トークン」として1件のトークンにします。
その後、指定した正規表現に一致する部分だけを「抽出トークン」として分離し、未一致部分は元の「解析対象トークン」として残します。
たとえば、次のような入力があるとします。
log_%yyyy%_%MM%_%dd%.txt
日付書式部分を抽出する正規表現を適用すると、概念的には次のようなトークン列になります。
[Literal] log_
[DatePattern] %yyyy%
[Literal] _
[DatePattern] %MM%
[Literal] _
[DatePattern] %dd%
[Literal] .txt
このように、元文字列の順序を保ったまま、意味のある単位に分割できます。
使い方
トークン種別の例
internal enum TokenType
{
Literal,
DatePattern,
EnvironmentVariable,
AppDomainValue,
}
初期トークンの作成
var tokens = StructuredStringLexer<TokenType>.CreateInitialTokens(
"log_%yyyy%_%MM%_%dd%.txt",
TokenType.Literal);
正規表現による一次トークン化
var regex = new Regex("%[a-zA-Z]+%");
var tokens = StructuredStringLexer<TokenType>.Tokenize(
"log_%yyyy%_%MM%_%dd%.txt",
TokenType.Literal,
TokenType.DatePattern,
regex);
既存トークン列への追加解析
var nextTokens = StructuredStringLexer<TokenType>.Tokenize(
tokens,
TokenType.Literal,
TokenType.EnvironmentVariable,
environmentVariableRegex);
この形式により、以下のような段階的解析が可能になります。
- 日付書式を抽出する
- 環境変数書式を抽出する
- アプリケーション固有書式を抽出する
- 最終的なレンダリング処理へ渡す
処理仕様
CreateInitialTokens()
入力文字列全体を、指定された基底トークン種別の1トークンとして返します。
internal static List<CharacterStringToken<TEnum>> CreateInitialTokens(
string parseTokenSequence,
TEnum baseTokenType)
Tokenize(string, TEnum, TEnum, Regex)
文字列を初期トークン化した後、指定された正規表現で一次パースします。
internal static List<CharacterStringToken<TEnum>> Tokenize(
string parseTokenSequence,
TEnum baseTokenType,
TEnum extractTokenType,
Regex regexRawChip = null)
Tokenize(List<CharacterStringToken<TEnum>>, TEnum, TEnum, Regex)
既存のトークン列に対して、指定されたトークン種別だけを解析対象にします。
internal static List<CharacterStringToken<TEnum>> Tokenize(
List<CharacterStringToken<TEnum>> parseTokenList,
TEnum targetTokenType,
TEnum extractTokenType,
Regex regexRawChip = null)
正規表現の扱い
マッチ順序
正規表現のマッチ順序は重要です。
たとえば、yyyy と yy のように部分一致するパターンがある場合は、より長いパターンである yyyy を先にマッチさせる必要があります。
yy を先に抽出すると、yyyy の一部が誤って yy として分割される可能性があります。
空文字マッチ
regexRawChip が null の場合、または空文字にマッチする正規表現の場合は、解析対象トークン全体を抽出トークンとして扱います。
一方、分割処理中に match.Length == 0 となる場合は、空文字位置へのマッチとして ArgumentException を送出します。
RightToLeft 非対応
RegexOptions.RightToLeft はサポートしません。
右から左への探索では、トークン列の順序管理や未マッチ領域の扱いが複雑化し、想定した左から右への字句解析と整合しないためです。
例外仕様
| 条件 | 例外 |
|---|---|
parseTokenList が null |
ArgumentNullException |
トークン列に null が含まれる |
InvalidOperationException |
LexemeFormat が未設定または空文字 |
InvalidOperationException |
regexRawChip が必要な場面で null |
ArgumentException |
RegexOptions.RightToLeft が指定されている |
NotSupportedException |
| 正規表現が空文字位置にマッチする | ArgumentException |
| マッチ範囲が重複または逆行する | InvalidOperationException |
設計上の責務分離
StructuredStringLexer<TEnum> は、字句解析だけを担当します。
このクラスが担当するものは次の通りです。
- 入力文字列の分割
- トークン種別の付与
- マッチ部分と未マッチ部分の保持
- トークン列の順序維持
- 不正なマッチ状態の検出
一方で、次の責務は持ちません。
- トークン値のレンダリング
- 日付や環境変数などの値解決
- 出力パスの生成
- 正規表現パターンそのものの構築
- ユーザー向け API の提供
想定用途
- ファイルパステンプレートの解析
- ログファイル名テンプレートの解析
- 日付書式を含む文字列の分解
- 環境変数プレースホルダーの抽出
- アプリケーション固有トークンの段階的解析
- 複数種類の書式チップを順番に抽出する前処理
既知の制約・注意事項
RegexOptions.RightToLeftは非対応です。- 空文字にマッチする正規表現は、扱いに注意が必要です。
- 部分一致する正規表現を複数使う場合は、長いパターン、具体的なパターンを先に適用してください。
- 本クラスは
internalのため、ライブラリ内部の実装部品として利用する想定です。 - トークン種別が同一の場合は、解析を行わずクローンしたトークン列を返します。
実装メモ
CloneTokenList()
既存トークン列を複製します。
解析対象外のトークンや、トークン種別が同一で解析不要な場合に、元のトークン列への副作用を避ける目的で利用されます。
AppendSplitTokens()
正規表現のマッチ位置を基準に、未マッチ部分とマッチ部分を順番に追加します。
処理の流れは次の通りです。
- 正規表現の妥当性を確認する
- 最初のマッチを取得する
- マッチしない場合は、全体を解析対象トークンとして追加する
- マッチ前の未一致部分を追加する
- マッチ部分を抽出トークンとして追加する
- 次のマッチへ進む
- 最後に残った未一致部分を追加する
AppendToken()
空文字以外のトークンだけをリストへ追加します。
lexemeFormat が null の場合は例外を送出しますが、長さ0の文字列は追加対象外として処理されます。
ライセンス
- 本プロジェクトは MIT LICENSE の下で公開されています。
免責事項
- 本ソフトウェアは現状のまま提供され、利用に伴う責任は利用者に帰属します。
- 開発者の所属や契約形態は、本ソフトウェアの利用条件や責任に影響しません。
貢献方法
- 本プロジェクトは、主に個人の技術検証・公開を目的としています。恐れ入りますが、現時点では外部からのIssueやPull Requestによる貢献は受け付けておりません。ご理解のほどよろしくお願いいたします。
- 尚、対応済機能として挙げている項目についてのバグ報告は、歓迎しております。
作者情報
- Rikou Natsuki (夏木 梨好)
- GitHub: RikouNatsuki
- お問い合わせは Issues まで
| Product | Versions Compatible and additional computed target framework versions. |
|---|---|
| .NET | net5.0 was computed. net5.0-windows was computed. net6.0 was computed. net6.0-android was computed. net6.0-ios was computed. net6.0-maccatalyst was computed. net6.0-macos was computed. net6.0-tvos was computed. net6.0-windows was computed. net7.0 was computed. net7.0-android was computed. net7.0-ios was computed. net7.0-maccatalyst was computed. net7.0-macos was computed. net7.0-tvos was computed. net7.0-windows was computed. net8.0 was computed. net8.0-android was computed. net8.0-browser was computed. net8.0-ios was computed. net8.0-maccatalyst was computed. net8.0-macos was computed. net8.0-tvos was computed. net8.0-windows was computed. net9.0 was computed. net9.0-android was computed. net9.0-browser was computed. net9.0-ios was computed. net9.0-maccatalyst was computed. net9.0-macos was computed. net9.0-tvos was computed. net9.0-windows was computed. net10.0 was computed. net10.0-android was computed. net10.0-browser was computed. net10.0-ios was computed. net10.0-maccatalyst was computed. net10.0-macos was computed. net10.0-tvos was computed. net10.0-windows was computed. |
| .NET Core | netcoreapp2.0 was computed. netcoreapp2.1 was computed. netcoreapp2.2 was computed. netcoreapp3.0 was computed. netcoreapp3.1 was computed. |
| .NET Standard | netstandard2.0 is compatible. netstandard2.1 was computed. |
| .NET Framework | net461 was computed. net462 was computed. net463 was computed. net47 was computed. net471 was computed. net472 was computed. net48 was computed. net481 was computed. |
| MonoAndroid | monoandroid was computed. |
| MonoMac | monomac was computed. |
| MonoTouch | monotouch was computed. |
| Tizen | tizen40 was computed. tizen60 was computed. |
| Xamarin.iOS | xamarinios was computed. |
| Xamarin.Mac | xamarinmac was computed. |
| Xamarin.TVOS | xamarintvos was computed. |
| Xamarin.WatchOS | xamarinwatchos was computed. |
-
.NETStandard 2.0
- No dependencies.
NuGet packages
This package is not used by any NuGet packages.
GitHub repositories
This package is not used by any popular GitHub repositories.
| Version | Downloads | Last Updated |
|---|
Early preview release.