python で Windows の My Pictures フォルダのパスを取得(OneDrive対応)

python でピクチャフォルダのパスを取得したくて調べました。

OneDrive を使用しているとリダイレクトされてるので、環境変数から組み立てるのではだめでした。

ついでに Local AppData のパスも取得してます。

import ctypes.wintypes

CSIDL_LOCAL_APPDATA = 0x1c # SDK の ShlObj_core.h で定義されてる
CSIDL_MYPICTURES = 0x27

buf = ctypes.create_unicode_buffer(ctypes.wintypes.MAX_PATH)

ctypes.windll.shell32.SHGetFolderPathW(0, CSIDL_LOCAL_APPDATA, 0, 0, buf)
print('LOCAL_APPDATA={}'.format(buf.value))

ctypes.windll.shell32.SHGetFolderPathW(0, CSIDL_MYPICTURES, 0, 0, buf)
print('MYPICTURES={}'.format(buf.value))

出力

LOCAL_APPDATA=C:\Users\perce-neige\AppData\Local
MYPICTURES=C:\Users\perce-neige\OneDrive\画像

spirit x3 double_ パーサの精度が悪い件

boost 1.72 の spirit x3 の double_ パーサを使用していたところ、どうも値がちょっとおかしい。
円周率の小数点以下16桁までの文字列 "3.1415926535897932" で試してみました。
VC14.16.27023 (Visual Studio 2017 15.9.22) 使用。

#include <boost/spirit/home/x3.hpp>
#include <iomanip>
#include <iostream>
#include <string>

double atof_x3_double_(std::string const& str)
{
    std::string::const_iterator iter = str.begin();
    double result;
    bool success = boost::spirit::x3::parse(iter, str.end(), boost::spirit::x3::double_, result);
    if (!success || iter != str.end())
        throw std::runtime_error("Parse failed.");
    return result;
}

void dump(std::ostream& os, double const& x)
{
    typedef unsigned __int64 uint64;
    BOOST_STATIC_ASSERT(sizeof(uint64) == sizeof(double));
    os << std::hex << reinterpret_cast<uint64 const&>(x);
}

int main()
{
    using namespace std;
    try {
        char const org[] = "3.1415926535897932"; // 円周率の小数点以下16桁まで
        double x = atof_x3_double_(org);
        double a = atof(org);
        
        cout << setprecision(17);
        cout << "org:    \t" << org << endl;
        cout << "double_:\t" << x << '\t';
        dump(cout, x);
        cout << endl;
        cout << "atof:   \t" << a << '\t';
        dump(cout, a);
        cout << endl;
    }
    catch (exception const& x) {
        cerr << x.what() << endl;
    }
}

spirit に合わせて全体的に const は後置になってますが、どっちでも大丈夫です。

出力

org:    	3.1415926535897932
double_:	3.1415926535897927	400921fb54442d17
atof:   	3.1415926535897931	400921fb54442d18

atof を通した場合と比べて、下位1ビットの違いだけですが精度が悪いです。

なので、double_ でパースした部分を文字列として取り出して、それを std::stof に通した結果を返すパーサ my_double を定義してみました。

namespace x3 = boost::spirit::x3;

struct my_double_class {};
typedef x3::rule<my_double_class, double> my_double_type;
my_double_type const my_double = "my_double";

static std::string::const_iterator my_double_pos;

auto const my_double_save_pos =
    [] (auto const& ctx) { my_double_pos = _where(ctx).begin(); };

auto const my_double_act =
    [] (auto& ctx) {
        _val(ctx) = std::stod(std::string(my_double_pos, _where(ctx).begin()));
    };

auto const my_double_def =
        x3::eps     [my_double_save_pos]
    >>  x3::double_ [my_double_act]
    ;

BOOST_SPIRIT_DEFINE(my_double);

これで boost::spirit::x3::double_ の代わりに my_double を使えば atof と同じ値になります。効率はともかくとして。

[2020/06/12] stof だったところを stod に書き換えました。

JaneStyle 4.00 で migemo 検索したい場合の注意点

PC を入れ替えたので JaneStyle 4.00 (4.0.0.5) で migemo 検索を使えるようにしようとしてうまくいかず、一か月放置してたのですが、やっと解決法を見つけたのでメモ。

http://egg.5ch.net/test/read.cgi/software/1535090252/700

最近の cmigemo DLL では動かないんですね。Ver.1.2 なら動きました。

以下の web アーカイブから取得できます。

http://web.archive.org/web/20070822171258/http:/www.kaoriya.net/dist/cmigemo-1.2-dll.tar.bz2

最近の辞書は utf-8 と cp932 で公開されてますが、cp932 の方を使います。

設定の仕方は JaneStyle のオンラインヘルプ

http://janesoft.net/janestyle/help/ref-name/searchbar.html

を見ましょう。

bzip2 と tar は私は cygwin のを使いましたが、今はいろいろあるみたい。

改元対応の記録

私の作ってる Windows アプリケーションの改元対応の記録です。

  • 2018年8月 改元対応にともなうバグが発覚して修正した。
  • 2019年4月1日(改元1月前) 次の元号は「令和」であることが発表される。
  • 2019年4月12日 Windows Update を待っていると令和対応版の出荷ができないので、元号選択肢の取得はレジストリのデータに、無ければ「令和」も加え、日付の和暦への変換は独自実装に切り替える。ついでに「元年」にも対応する。
  • 2019年4月16日 令和対応版リリース

結局のところ Windows API 関数ってのはその時点の Windows にアクセスするものであって、ライブラリ関数ではないということなのでしょう。日付の和暦への変換などは Windows に頼らずに、アプリケーション独自で実装するか、別のライブラリを利用すべきということがわかりました。

追記
  • 2019年4月26日(改元5日前; 米国25日) Windows 10 version 1803 までの Update が発行された。Windows 8.1 以前は preview なので、普通に適用されるのは 5月15日になる模様。Windows 10 version 1809 用は "Coming soon!" (日本語サイトでは「近日公開予定」)となっている。

struct と class はマングリングが異なるので混同してはいけない。

BCC では大丈夫だったので、不完全な struct の宣言にも class を使う習慣になってしまっていたのですが、VC ではリンク時にエラーになります。

一つ目のモジュール

struct Foo {
};

Foo* CreateFoo()
{
    return new Foo;
}

void PrintFoo(const Foo&)
{
}

二つ目のモジュール

class Foo; // 問題の不完全な宣言
Foo* CreateFoo();
void PrintFoo(const Foo&);

int main()
{
    Foo* foo = CreateFoo();
    PrintFoo(*foo);
    delete foo;
}

コンパイルしてリンクするとエラー

error LNK2019: 未解決の外部シンボル "class Foo * __cdecl CreateFoo(void)" (?CreateFoo@@YAPAVFoo@@XZ) が関数 _main で参照されました。
error LNK2019: 未解決の外部シンボル "void __cdecl PrintFoo(class Foo const &)" (?PrintFoo@@YAXABVFoo@@@Z) が関数 _main で参照されました。

class と struct を入れ替えて(デフォルト コンストラクタとデストラクタも必要)エラーメッセージを見ると、マングル後が1文字違うのがわかります。

error LNK2019: 未解決の外部シンボル "struct Foo * __cdecl CreateFoo(void)" (?CreateFoo@@YAPAUFoo@@XZ) が関数 _main で参照されました。
error LNK2019: 未解決の外部シンボル "void __cdecl PrintFoo(struct Foo const &)" (?PrintFoo@@YAXABUFoo@@@Z) が関数 _main で参照されました。

class Foo が ...VFoo... で、struct Foo が ...UFoo... です。

文字列の類似度

文字列の類似度を文字バイグラムの頻度ベクトルのなす角のコサインや、文字バイグラムの重複集合のダイス係数で計算する件について書こうと思っているのですが、なかなか進みませんw

VC141+OWLNext 6.44.2 用の CMakeLists.txt

VC141+OWLNext 6.44.2 用の CMakeLists.txt の例です。

pdf.cpp と pdf.rc から pdf.exe を生成する場合です。ランタイムライブラリは静的リンクです。

cmake_minimum_required(VERSION 3.11)
project(pdf CXX)
set(OWL "C:/lib/owl/6.44.2") ### OWLパス
set(CMAKE_CXX_FLAGS "/EHsc")
include_directories(${OWL}/include)
link_directories(${OWL}/lib)
add_executable(pdf pdf.cpp pdf.rc)

# マルチスレッド対応CRTライブラリを静的リンクさせるため、
# /MDx オプションを /MTx に置き換える。
string(REPLACE "/MD" "/MT" CMAKE_CXX_FLAGS_DEBUG          ${CMAKE_CXX_FLAGS_DEBUG})
string(REPLACE "/MD" "/MT" CMAKE_CXX_FLAGS_MINSIZEREL     ${CMAKE_CXX_FLAGS_MINSIZEREL})
string(REPLACE "/MD" "/MT" CMAKE_CXX_FLAGS_RELEASE        ${CMAKE_CXX_FLAGS_RELEASE})
string(REPLACE "/MD" "/MT" CMAKE_CXX_FLAGS_RELWITHDEBINFO ${CMAKE_CXX_FLAGS_RELWITHDEBINFO})