Windowsにおいて、ファイル名の比較するならComparing Unicode file names the right way – Sorting it all Out – MSDN Blogsに従うのが良いでしょう。そこで、それをC++で実装してみました。

#include <string>
#include <boost/numeric/conversion/cast.hpp>
#include <boost/range/as_literal.hpp>
#include <boost/range/iterator_range.hpp>
#include <windows.h>
 
bool FilenameEqualsImpl(std::wstring& lhs, std::wstring& rhs)
{
  ::CharUpperBuffW(&lhs[0], boost::numeric_cast<DWORD>(lhs.size()));
  ::CharUpperBuffW(&rhs[0], boost::numeric_cast<DWORD>(rhs.size()));
  return lsh == rhs;
}
 
template<typename Range1, typename Range2>
bool FilenameEquals(Range1&& lhs, Range2&& rhs)
{
  auto lvec = boost::copy_range<std::wstring>(
    boost::as_literal(std::forward<Range1>(lhs)));
  auto rvec = boost::copy_range<std::wstring>(
    boost::as_literal(std::forward<Range2>(rhs)));
  return FilenameEqualsImpl(lvec, rvec);
}
 
#include <iostream>
 
int main()
{
  std::cout << std::boolalpha;
  std::cout << FilenameEquals("A.TXT", "A.TXT") << std::endl;
  std::cout << FilenameEquals(std::wstring("B.Dat", "b.dAT") << std::endl;
  std::cout << FilenameEquals("c.html", "c.htm") << std::endl;
}

上記ページで推奨されている手段はこうです。

  1. CharUpperCharUpperBuffLCMapStringのLCMAP_UPPERCASE(間違ってもLCMAP_LINGUISTIC_CASINGはダメ)のいずれかで大文字化する。
  2. memcmpやwmemcmpなどでバイナリ比較する。

このコードではstd::wstringに入れた後、CharUpperBuffWとstd::wstringの==演算子で実装しました。

FilenameEquals関数は、const wchar_t*とstd::wstringの両方を受け付ける目的でテンプレート化しました。boost::as_literalを使っているのはBoost.StringAlgoの実装にならったものです。しかし、”hoge”のようなマルチバイト文字を受け付けるので良くないですね。boost::wstring_ref (Boost 1.53)などを使えばよかったです。

お試しのmain関数です。

#include <iostream>
 
int main()
{
  std::cout << std::boolalpha;
  std::cout << FilenameEquals("A.TXT", "A.TXT") << std::endl; // true
  std::cout << FilenameEquals("B.Dat", "b.dAT") << std::endl; // true
  std::cout << FilenameEquals("c.html", "c.htm") << std::endl; // false
}

おまけ1: Windows Vista以上であればCompareStringOrdinal関数を使えばよいと思います。そのため、このようなものを使う必要は無いでしょう。

おまけ2: そもそもファイル名でファイルが同じか判断しようというのはやめたほうがよいです: Don’t compare file names – greggm’s WebLog – MSDN Blogs。同じファイルパスなのに異なる文字列になる要因が多すぎて怖いです。シンボリックリンク・ハードリンク・ジャンクション、8.3ファイル名、UNCパスとドライブ、SUBSTコマンド、デバイス形式、\\?\プレフィックス、”.”と”..”(C:\foo\barとC:\foo\hoge\..\bar)など、ぱっと考えるだけでこんなにあります(たぶんまだあると思う)。

なるべくなら、そこのページに書いてあるようにファイルインデックス(inode的な値)とボリュームシリアルナンバーの一致で判断すべきです。


スポンサード リンク

この記事のカテゴリ

  • ⇒ Windowsのファイル名の比較をC++で
  • ⇒ Windowsのファイル名の比較をC++で