44525


VS2015上のC++でコーディングしています。  
起動したいbatファイルまでの絶対パスに日本語名が含まれている時に、  
system()又は_wsystem()で起動しようとするとstringの変数又はstringの変数を  
wstringに変換して渡しているのですが、文字化けしてしまっていてうまく起動できません。

[code]

    #include <windows.h>
    #include <tchar.h>
    #include <iostream>
    #include <algorithm>
    #include <filesystem> 
    #include <Windows.h>
    #include <string>
    #include <codecvt> 
    #include <cstdio>
    #define BatName "Do.bat"
    using namespace std;
    
    wstring _bacePath;
    void convert_DobatPath( wstring filePath ){
        string fullPath;
        wstring wFullPath;
        //batパス作成
        fullPath = "\\";
        fullPath += BatName;
        //string→wstring変換
        wchar_t *wcs = new wchar_t[fullPath.length() + 1];
        mbstowcs( wcs, fullPath.c_str(), fullPath.length() + 1 );
        wFullPath = wcs;
        delete[] wcs;
        //フルパス作成
        wFullPath = _bacePath + filePath + wFullPath;
        // 起動
        _wsystem( wFullPath.c_str() );
    }
    
    int main(){
        namespace sys = std::tr2::sys;
        string startFolder;
        cin >> startFolder;
        wchar_t *wcs = new wchar_t[startFolder.length() + 1];
        mbstowcs( wcs, startFolder.c_str(), startFolder.length() + 1 );
        _bacePath = wcs;
        sys::path p( startFolder.c_str() );
        std::for_each( sys::recursive_directory_iterator( p ),     sys::recursive_directory_iterator(),
            []( const sys::path& p ) {
            // 特定ファイル見つかったら
            // そこまでのパスを取得し、bat起動に投げる
            if( sys::is_regular_file( p ) ) {
                if( p.filename() == "hoge.txt" ){
                    sys::path& parentPath = p.parent_path();
                    convert_DobatPath( parentPath.generic_wstring() );
                }
            }
        } );
    }
    
[/code]

wstringに変換せずsystem()で呼んでも文字化けしてしまい、同じ結果でした。  
wstringで統一しようとしましたが"\"とBatNameを合わせることが出来ませんでした。  
C++でbatファイルを起動できないでしょうか。よろしくお願いいたします。


CC BY-SA 4.0
2018-06-04T09:19:10.253
44514
2018-06-05T13:15:04.957
2018-06-05T08:03:37.050
27009
27009
question
c++
C++日本語名を含むディレクトリ上のファイル起動


`char` と `wchar_t` が混在していたり、その結果不要と思われる変換が入っていたりと異様に複雑なコードと化しています。全部 `wchar_t`
に統一すれば簡単になって

[code]

    int ExecCmd(wchar_t const* dir, wchar_t const* fn) {
        // この程度のことに <string> 使うのは牛刀鶏割
        std::wstring p(dir);
        p+=L'\\';
        p+=fn;
        return _wsystem(p.c_str());
    }
    
    int wmain() {
        // SJIS ダメ文字を試してみる
        return ExecCmd(L"D:\\能力ーソ", L"Do.bat");
    }
    
[/code]

で十分なのではないか・・・と。

Windows 固有の機能を積極的に使う / 使ってよいのなら `PathAppend` 等を使えば `<string>` は不要となり `L'\\'`
とか書かなくてよいし問題が減るかもしれません（別の問題が生じる可能性もありますが）

* * *

もしかして `wchar_t` 文字(列)リテラルをご存じないとか？

ソースコード上 `"hoge.txt"` と書くと、これは `char` 文字列リテラルで Windows 上では ASCII (日本語 Windows
なら CP932 ) で符号化されます。円マーク単一文字なら `'\\'` で OK 。これは MBCS と呼ばれる表記です。 `mbstowcs` の
mbs 側に相当します。

ソースコード上 `L"hoge.txt"` と書くと、これは `wchar_t` 文字列リテラルで Windows 上では (仕向け地設定に関係なく)
UTF-16 で符号化されます。円マーク単一文字なら `L'\\'` で OK 。これは WideChar と呼ばれる表記です。 `mbstowcs` の
wcs 側に相当します。

現在の Windows の API 的には `wchar_t` つまり WCS を渡すのが標準仕様なので、ソースコード上の文字リテラル、文字列リテラルは全て
`wchar_t` に統一しましょう。そうするには提示ソースコード上 `'\\'` あるいは `"\\"` の代わりに `L'\\'` や `L"\\"`
を、同様 `BatName` は `L"Do.bat"` とすれば `mbstowcs` の類は一切不要になり単純に文字列結合だけで動くようになります。

Win9x 系が絶滅済みである 2018 年現在 Windows native プログラムでは常に `wchar_t`
でソースコードを書くと幸せになれると思います。


CC BY-SA 4.0
2018-06-05T01:20:42.650
44523
2018-06-05T04:51:37.953
2018-06-05T04:51:37.953
8589
8589
44514
answer


774RRさんが

> Windows 固有の機能を積極的に使う / 使ってよいのなら `PathAppend` 等を使えば `<string>` は不要となり `L'\\'`
> とか書かなくてよいし問題が減るかもしれません（別の問題が生じる可能性もありますが）

と書かれていますが、Visual C++ 2015以降であれば
[`<filesystem>`](https://msdn.microsoft.com/ja-jp/library/hh874694.aspx)
をお勧めします。  
[`operator/()`](https://msdn.microsoft.com/ja-
jp/library/dn823757.aspx#Anchor_6)
でパスの結合もできます。このため`'\\'`を操作する必要もなくなり、`std::wstring`や`std::string`も必要なくなり、`mbstowcs()`による文字コード変換も必要なくなります。

[code]

    #include <filesystem>
    namespace fs = std::experimental::filesystem;
    
    int ExecCmd(fs::path const& dir, fs::path const& fn) {
        auto const path = dir / fn;
        return _wsystem(path.c_str());
    }
    
    int wmain() {
        // SJIS ダメ文字を試してみる
        return ExecCmd(L"D:\\能力ーソ", L"Do.bat");
    }
    
[/code]

ただし、Visual Studio 2015～Visual Studio 2017
15.6はN4100という古い仕様に基づく実装であり名前空間も`std::experimental::filesystem`となっています。Visual
Studio 2017
15.7からはC++17に準拠した新しい実装となっており名前空間も`std::filesystem`となっています（互換のため`std::experimental::filesystem`も残されています）。

* * *

`recursive_directory_iterator`を使うのであれば、`for`ループの方がラムダ式なしでシンプルに書けます。

[code]

    int main(){
        namespace fs = std::experimental::filesystem;
        // TODO: 質問文ではstartFolderが未定義
        for (fs::recursive_directory_iterator it{ startFolder }, end; it != end; ++it)
            if (fs::is_regular_file(*it)) {
                auto p = it->path();
                if (p.filename() == "hoge.txt")
                    _wsystem(p.replace_filename(BatName).c_str());
            }
    }
    
[/code]

`"hoge.txt"`や`BatName`については上記コードでも動作はしますが、774RRさんも指摘されているように`wchar_t`で記述することを推奨します。


CC BY-SA 4.0
2018-06-05T01:53:41.987
44525
2018-06-05T13:15:04.957
2018-06-05T13:15:04.957
4236
4236
44514
answer