CommandLineToArgvW()でのコマンドライン解析について – プログラミング – Home

通知
すべてクリア

[解決済] CommandLineToArgvW()でのコマンドライン解析について


なつ
 なつ
(@なつ)
ゲスト
結合: 22年前
投稿: 8
Topic starter  

環境:VS2010、MFC

exeに渡されたコマンドラインを表題のAPIで解析しようと思っていますが、
文字列の最後に'\'が付いていると正常に読み取れません。
エスケープシーケンスとして扱われてしまい、次のパラメータとくっついてしまいま
す。
何か良い回避方法は無いでしょうか?

・コマンド
aaa.exe AAA C:\BBB\ CCC

・コマンド解析結果
1番目:AAA
2番目:C:\BBB CCC
※2番目が「C:\BBB\ CCC」になっていたかもしれません

・期待値
1番目:AAA
2番目:C:\BBB\
3番目:CCC

コマンドライン解析部分の
ソースは以下のように書いています。

TCHAR **pArgs = NULL;
int nArgs = 0;

pArgs = CommandLineToArgvW( GetCommandLine(), &nArgs );


引用未解決
トピックタグ
YuO
 YuO
(@YuO)
ゲスト
結合: 22年前
投稿: 320
 

バックスラッシュに連なる二重引用符は特別扱いするのがCommandLineToArgvWの規則です。
VC++のargvも,同一の規則が適用されるようです。

[MSDN] CommandLineToArgvW function
http://msdn.microsoft.com/en-us/library/windows/desktop/bb776391.aspx
[MSDN] Parsing C++ Command-Line Arguments
http://msdn.microsoft.com/en-us/library/windows/desktop/17w5ykft.aspx

なので,自分で解析するか,\を\\に置換してからCommandLineToArgvWを呼び出すとよいで
しょう。


返信引用
forty-five
 forty-five
(@forty-five)
ゲスト
結合: 19年前
投稿: 22
 

::PathGetArgs() という API もありますよ。

void main()
{
TCHAR buffer[1024] = _T(aaa.exe \AAA\ \C:\\B BB\\\ \CCC\");

LPTSTR p = buffer;

while (*p)
{
_RPT1(_CRT_WARN, %s\n, p);

p = ::PathGetArgs(p);
}
}

出力:
aaa.exe AAA C:\B BB\ CCC
AAA C:\B BB\ CCC
C:\B BB\ CCC
CCC


返信引用
なつ
 なつ
(@なつ)
ゲスト
結合: 22年前
投稿: 8
Topic starter  

>YuOさん
\を\\に置き換えると、前以外の\も置き換わってしまって判別が難しそうです。
の前のみ置き換えない、という方法にすると文字列としてのが
認識できなくなってしまい、両立に悩んでいます。

AAA C:\BBB\ CCC.exe -D \param\"

このようなパラメータを上手く判別する方法が思いつかず悩んでいます。

>forty-fiveさん「
このPathGetArgs()というAPIで実現できそうだったのですが、
文字列としてのを認識してくれないようです。

また、MSDNなどに記述がありましたが、
渡せる文字列に260バイトという制限があるのでしょうか?
自分で試した限りでは520バイト以上でも渡せたので、
それを良しとして良いのかどうか・・・
(自分の英語の読解が間違ってたらすみません)

参考:
http://msdn.microsoft.com/en-us/library/windows/desktop/bb773602(v=vs.85).aspx


返信引用
forty-five
 forty-five
(@forty-five)
ゲスト
結合: 19年前
投稿: 22
 

エスケープさせたいのかさせたくないのか明確にした方が良いと思います。

AAA C:\BBB\ CCC.exe -D \param\"

この例だと最初の \ はエスケープさせたくないんですよね?
そして、2個目と3個目の \ はエスケープさせたいということなんですよね?

void PathUnescapeQuotes(LPTSTR string)
{
LPTSTR src = string;
LPTSTR dst = string;

while (true)
{
if (src[0] == _T('\\') && src[1] == _T('\'))
{
src++; // skip _T('\\')
}

*dst = *src;

if (*src == _T('\0'))
break;

src++;
dst++;
}
}

void main()
{
TCHAR buffer[1024] = _T(\AAA\ \C:\\BBB\\\ \CCC.exe\ \-D \\\param\\
\\");

LPTSTR p = buffer;

while (*p)
{
TCHAR string[1024];
::StrCpy(string, p);
::PathRemoveArgs(string);
::PathUnquoteSpaces(string);
::PathUnescapeQuotes(string);

_RPT1(_CRT_WARN, %s\n, string);

p = ::PathGetArgs(p);
}
}

出力:
AAA
C:\BBB\
CCC.exe
-D param

C:\BBB\ の \ は最初と最後に が付いているのでエスケープしていません。
-D \param\" の内部の \ はエスケープしています。


返信引用
なつ
 なつ
(@なつ)
ゲスト
結合: 22年前
投稿: 8
Topic starter  

>forty-fiveさん
すいません、書き方が悪くてわかりにくかったですね。
おっしゃる通り、以下の期待値を得たかったのです。

AAA C:\BBB\ CCC.exe -D \param\"

1番目:AAA
2番目:C:\BBB\
3番目:CCC.exe
4番目:-D param

※ ~で括られた中の場合のみ、を文字列として扱いたいと考えていました。

提示して頂いた内容で、希望の処理が出来そうです。
ありがとうございます。

ただ、以下のような\が~の中に一つだけのパターンだと
正常に取得出来ないようです。
※PathRemoveArgs や PathGetArgs で上手く認識出来てないみたいです。

\AAA BBB :NG ⇒くっついてしまう
AAA\" BBB :NG ⇒くっついてしまう
\AAA\" BBB :OK ⇒AAA、BBBとして取れる

ただ、~の中に\が一つだけのパターンはまず無いと思っていますので、
今回は目を瞑ることにしました。

もし、どなたか回避方法がわかる方がいらっしゃったら教えて頂けると幸いです。


返信引用
ITO
 ITO
(@ITO)
ゲスト
結合: 22年前
投稿: 1235
 

>もし、どなたか回避方法がわかる方がいらっしゃったら教えて頂けると幸いです。
ないと思います。(例外があれば別です)
バックスラッシュと¥の区別のための記号なのでないと思います。
後はどうしてもといわれれば、アスキーコードで判断するぐらいですね。


返信引用
forty-five
 forty-five
(@forty-five)
ゲスト
結合: 19年前
投稿: 22
 

根本的に仕様を変えられるのなら、

HTML 方式

bool replace(LPTSTR& src, LPTSTR& dst, LPCTSTR srcPattern, LPCTSTR dstPattern)
{
if (_tcsncmp(src, srcPattern, _tcslen(srcPattern)) == 0)
{
_tcscpy(dst, dstPattern);

src += _tcslen(srcPattern);
dst += _tcslen(dstPattern);

return true;
}

return false;
}

void HtmlUnescape(LPTSTR string)
{
LPTSTR src = string;
LPTSTR dst = string;

while (true)
{
if (!replace(src, dst, _T("), _T(\")) &&
!replace(src, dst, _T(&), _T(&)) &&
!replace(src, dst, _T(&lt;), _T(<)) &&
!replace(src, dst, _T(&gt;), _T(>)) &&
!replace(src, dst, _T(&nbsp;), _T( )))
{
*dst = *src;

if (*src == _T('\0'))
break;

#ifndef UNICODE

if (::IsDBCSLeadByte(*src))
{
src++;
dst++;

*dst = *src;
}

#endif

src++;
dst++;
}
}
}

void main()
{
TCHAR seps[1024] = _T( );
TCHAR buffer[1024] = _T(空白&nbsp;のテスト 二重引用符&quot;のテスト);

LPTSTR token = _tcstok(buffer, seps);

while (token)
{
TCHAR string[1024];
_tcscpy(string, token);
::HtmlUnescape(string);

_RPT1(_CRT_WARN, %s\n, string);

token = _tcstok(0, seps);
}
}

URL 方式

void main()
{
TCHAR seps[1024] = _T( );
TCHAR buffer[1024] = _T(空白%20のテスト 二重引用符%22のテスト タブ%09のテス
ト 改行%0Aのテスト);

LPTSTR token = _tcstok(buffer, seps);

while (token)
{
TCHAR string[1024];
_tcscpy(string, token);
DWORD c = 1024;
::UrlUnescape(string, 0, &c, URL_UNESCAPE_INPLACE);

_RPT1(_CRT_WARN, %s\n, string);

token = _tcstok(0, seps);
}
}

URL 方式ならタブやら改行やら何でも渡せます。


返信引用

返信する

投稿者名

投稿者メールアドレス

タイトル *

プレビュー 0リビジョン 保存しました
共有:
タイトルとURLをコピーしました