私はあなたのためにパーサーを書こうとしました。
バルクがUTF-8のテストであることに同意します。これは、文字列に値が既に含まれている場合は冗長です(UTF-8はファイルシステムのエンコーディングであり、Unicodeは有効なUTF-8の内部表現です)。それは確かに物事を非常に単純化します。
私が理解しているように、BNFは次のように述べています。
- locale-folder(オプション)は、文字列'locale/'の後にlang-tagが続きます
- lang-tagの形式は、「en」、「en-us」、「en-123」、「en-us-1」などです。
- '-'文字で区切られた少なくとも1つのトークン
- 各トークンは1〜8文字です
- 最初のトークンには小文字のみを含めることができます
- 次のトークンは小文字と数字の組み合わせです
- オプションのロケールの後に、次のことができます。
- 単一のファイル名または
- パス( 「/」で区切られた一連のフォルダー名)または
- パスの後にファイル名が続く
- folder-nameとfile-nameは一種のユニコードです。各キャラクターはどちらかです
- AZ、az、0-9、または
- 「$%'-_ @〜()&+、=[]」のいずれか。
- u007Fより上の任意の文字(UTF8の2、3、および4バイト文字)
とは言うものの、ここに簡単な実装があります(デバッグの目的で、解析からの出力をキャプチャします。これはデバッグのために行いましたが、不要な場合は削除してください)。パスにエラーがあると、ZipRelPathコンストラクターがValueErrorを発生させます。
import re
class ZipRelPath:
FILE_NAME_RE = re.compile(u"^[a-zA-Z0-9 \$\%\'\-_@~\(\)&+,=\[\]\.\u0080-\uFFFF]+$")
LANG_TAG_RE = re.compile("^[a-z]{1,8}(\-[a-z0-9]{1,8})*$")
LOCALES = "locales/"
def __init__(self, path):
self.path = path
self.lang_tag = None
self.folders = []
self.file_name = None
self._parse_locales()
self._parse_folders()
def _parse_locales(self):
"""Consumes any leading 'locales' and lang-tag"""
if self.path.startswith(ZipRelPath.LOCALES):
self.path = self.path[len(ZipRelPath.LOCALES):]
self._parse_lang_tag()
def _parse_lang_tag(self):
"""Parses, consumes and validates the lang-tag"""
self.lang_tag, _, self.path = self.path.partition("/")
if not self.path:
raise ValueError("lang-tag missing closing /")
if not ZipRelPath.LANG_TAG_RE.match(self.lang_tag):
raise ValueError(u"'%s' is not a valid language tag" % self.lang_tag)
def _parse_folders(self):
"""Handles the folders and file-name after the locale"""
while (self.path):
self._parse_folder_or_file()
if not self.folders and not self.file_name:
raise ValueError("Missing folder or file name")
def _parse_folder_or_file(self):
"""Each call consumes a single path entry, validating it"""
folder_or_file, _, self.path = self.path.partition("/")
if not ZipRelPath.FILE_NAME_RE.match(folder_or_file):
raise ValueError(u"'%s' is not a valid file or folder name" % folder_or_file)
if self.path:
self.folders.append(folder_or_file)
else:
self.file_name = folder_or_file
def __unicode__(self):
return u"ZipRelPath [lang-tag: %s, folders: %s, file_name: %s" % (self.lang_tag, self.folders, self.file_name)
そして、テストの短いセット:
GOOD = [
"$%'-_@~()&+,=[].txt9",
"my/path/to/file.txt",
"locales/en/file.txt",
"locales/en-us/file.txt",
"locales/en-us-abc123-xyz/file.txt",
"locales/abcdefgh-12345678/file.txt",
"locales/en/my/path/to/file.txt",
u"my\u00A5\u0160\u039E\u04FE\u069E\u0BCC\uFFFD/path/to/file.txt"
]
BAD = [
"",
"/starts/with/slash",
"bad^file",
"locales//bad/locale",
"locales/en123/bad/locale",
"locales/EN/bad/locale",
"locales/en-US/bad/locale",
]
for path in GOOD:
print unicode(ZipRelPath(path))
for path in BAD:
try:
zip = ZipRelPath(path)
raise Exception("Illegal path {0} was accepted by {1}".format(path, zip))
except ValueError as exception:
print "Incorrect path '{0}' fails with: {1}".format(path, exception)
生成するもの:
ZipRelPath [lang-tag: None, folders: [], file_name: $%'-_@~()&+,=[].txt9
ZipRelPath [lang-tag: None, folders: ['my', 'path', 'to'], file_name: file.txt
ZipRelPath [lang-tag: en, folders: [], file_name: file.txt
ZipRelPath [lang-tag: en-us, folders: [], file_name: file.txt
ZipRelPath [lang-tag: en-us-abc123-xyz, folders: [], file_name: file.txt
ZipRelPath [lang-tag: abcdefgh-12345678, folders: [], file_name: file.txt
ZipRelPath [lang-tag: en, folders: ['my', 'path', 'to'], file_name: file.txt
ZipRelPath [lang-tag: None, folders: [u'my\xa5\u0160\u039e\u04fe\u069e\u0bcc\ufffd', u'path', u'to'], file_name: file.txt
Incorrect path '' fails with: Missing folder or file name
Incorrect path '/starts/with/slash' fails with: '' is not a valid file or folder name
Incorrect path 'bad^file' fails with: 'bad^file' is not a valid file or folder name
Incorrect path 'locales//bad/locale' fails with: '' is not a valid language tag
Incorrect path 'locales/en123/bad/locale' fails with: 'en123' is not a valid language tag
Incorrect path 'locales/EN/bad/locale' fails with: 'EN' is not a valid language tag
Incorrect path 'locales/en-US/bad/locale' fails with: 'en-US' is not a valid language tag
テストケースのいずれかが失敗した場合はお知らせください。修正できるかどうかを確認します。