ISO 9660のファイル名変換
一遍に実装しようとして混乱してきたので、とりあえずファイル名の置換部分だけ書いてみました。
作業記録として、コードを貼っときます。
#include <algorithm> #include <functional> #include <iostream> #include <iterator> #include <locale> #include <sstream> #include <stdexcept> #include <string> #include <vector> // boost::lexical_cast<std::string>(int)の代わり (適当) std::string unsigned_to_string(unsigned n) { std::ostringstream os; os.imbue(std::locale::classic()); os << n; return os.str(); } // d文字かどうかの判定をするファンクタ struct is_iso_d_char : std::unary_function<char,bool> { bool operator()(char c) const { return (c == '_') || ((c >= 'A') && (c <= 'Z')) || ((c >= '0') && (c <= '9')) ; } }; // ISO 9660の正当なファイルIDか? bool is_valid_iso_id(const std::string& s) { const std::size_t size = s.size(); if ((size < 2) || (size > (8+1+3))) return false; // ISO 9660では"."が必須 std::size_t delim = s.find('.'); if (delim == std::string::npos) return false; typedef std::string::const_iterator iter_type; iter_type di = s.begin() + delim; if (std::find_if(s.begin(), di, std::not1(is_iso_d_char())) != di) return false; if (di != s.end()) { iter_type ei = di + 1; if (std::find_if(ei, s.end(), std::not1(is_iso_d_char())) != s.end()) return false; } return true; } // ISO 9660で使用できない文字を適当に置き換える std::string make_iso_alt_name(const std::string& s) { std::string tmp; for (std::size_t i = 0, size = s.size(); i != size; ++i) { char c = s[i]; if (!is_iso_d_char()(c)) { if ((c >= 'a') && (c <= 'z')) c -= ('a' - 'A'); else c = '_'; } tmp += c; } return tmp; } // ファイルID文字列sをrenに含まれない正当な文字列に変換する std::string make_iso_alt_id( const std::vector<std::string>& ren, const std::string& s) { const std::size_t size = s.size(); std::size_t delim = s.rfind('.'); if (delim == std::string::npos) delim = size; // ベース名 std::string base(s, 0, (std::min)(delim, 8u)); // 拡張子 std::string ext; if (base.empty()) base = s.substr(0, 8); // "."始まりのファイルは全体をベース名にする else if (delim != size) ext = s.substr(delim+1, 3); base = ::make_iso_alt_name(base); ext = ::make_iso_alt_name(ext); std::string new_name; new_name.reserve(8+1+3); new_name = base; new_name += '.'; new_name += ext; unsigned n = 0; // ダブったときに使う連番 while (std::find(ren.begin(), ren.end(), new_name) != ren.end()) { // ダブったので連番をベース名に追加 const std::string& num = unsigned_to_string(n++); if (num.size() > 8u) throw std::runtime_error("cannot generate alternative file ID"); new_name.assign(base, 0, (std::min)(8u-num.size(), base.size())); new_name += num; new_name += '.'; new_name += ext; } return new_name; } // org内のファイルID文字列をISO 9660 validな文字列に変換してrenにセットする void iso_rename( std::vector<std::string>& ren, const std::vector<std::string>& org) { // 使ったかどうかフラグ (適当) std::vector<bool> used(org.size()); std::vector<std::string> tmp; tmp.reserve(org.size()); // validなものはそのまま for (std::size_t i = 0; i < org.size(); ++i) { if (::is_valid_iso_id(org[i])) { tmp.push_back(org[i]); used[i] = true; } } // invalidなものだけ変換 for (std::size_t i = 0; i < org.size(); ++i) { if (!used[i]) tmp.push_back(::make_iso_alt_id(tmp, org[i])); } ren.swap(tmp); } int main() { std::vector<std::string> org; org.push_back("abc.txt"); org.push_back("ABC.TXT"); org.push_back("MAKEFILE"); org.push_back("Makefile"); org.push_back("LONGLONG.TXT"); org.push_back("LONGLONG1.TXT"); org.push_back("LONGLONG2.TXT"); org.push_back("LONGLONG3.TXT"); org.push_back("LONGLONG4.TXT"); org.push_back("LONGLONG5.TXT"); org.push_back("LONGLONG6.TXT"); org.push_back("LONGLONG7.TXT"); org.push_back("LONGLONG8.TXT"); org.push_back("LONGLONG9.TXT"); org.push_back("LONGLONGA.TXT"); org.push_back("LONGLONGB.TXT"); org.push_back("あいうえ.TXT"); org.push_back("あいうえお.TXT"); org.push_back("A-B.TXT"); org.push_back(".hoge"); org.push_back("a.b.c"); std::vector<std::string> ren; iso_rename(ren, org); std::copy( ren.begin(), ren.end(), (std::ostream_iterator<std::string>(std::cout, "\n")) ); }
実行結果
ABC.TXT
LONGLONG.TXT
ABC0.TXT
MAKEFILE.
MAKEFIL0.
LONGLON0.TXT
LONGLON1.TXT
LONGLON2.TXT
LONGLON3.TXT
LONGLON4.TXT
LONGLON5.TXT
LONGLON6.TXT
LONGLON7.TXT
LONGLON8.TXT
LONGLON9.TXT
LONGLO10.TXT
________.TXT
_______0.TXT
A_B.TXT
_HOGE.
A_B.C
スクラッチコードなので、
- ISO 9660 Level-1(8.3ファイル名)前提
- ASCII前提
- size_tがunsigned int前提
- ディレクトリ名("."が付かない)に未対応
などなど適当なところがたくさんありますが、アルゴリズムはこれでOKっぽいです。
vectorだとダブり検索に時間がかかるので、setに置き換えたほうがよいですかね。