曲径通幽论坛

 找回密码
 立即注册
搜索
查看: 3985|回复: 0
打印 上一主题 下一主题

简单模拟 getopt() 的 Options()

[复制链接]

4918

主题

5880

帖子

3万

积分

GROAD

曲径通幽,安觅芳踪。

Rank: 6Rank: 6

积分
34397
跳转到指定楼层
楼主
发表于 2011-9-10 03:41:32 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式
使用 Options() 函数实现对 getopt() 函数的简单模拟,该函数接收形如 '-c' (一个破折号加一单字符)的命令行选项。该函数原型为:
DWORD Options (int argc, LPCTSTR argv [], LPCTSTR OptStr, ...)

下面分析该函数的代码和使用方法举例(Codes Come From WSP4)

代码如下:
[C++] 纯文本查看 复制代码
#include <stdarg.h>
#include <memory.h>
DWORD Options (int argc, LPCTSTR argv [], LPCTSTR OptStr, ...)
{
    va_list pFlagList;
    LPBOOL pFlag;
    int iFlag = 0, iArg;

    va_start (pFlagList, OptStr);

    while ((pFlag = va_arg (pFlagList, LPBOOL)) != NULL && iFlag < (int)_tcslen (OptStr)) 
       {
        *pFlag = FALSE;
        for (iArg = 1; !(*pFlag) && iArg < argc && argv [iArg] [0] == _T('-'); iArg++)
            *pFlag = wmemchr (argv [iArg], OptStr [iFlag], _tcslen (argv [iArg])) != NULL;
        iFlag++;
    }

    va_end (pFlagList);

    for (iArg = 1; iArg < argc && argv [iArg] [0] == _T('-'); iArg++);

    return iArg;
}

函数的命令参数显示指定的共有 3 个,分别是 argc, argv 和 OptStr ,其中 argc 和 argv 和 main() 中的一样,而 OptStr 是本程序所支持的命令选项的合集字符串。比如我们这个程序一共支持 3 个命令选项,如 -a, -m,-c ,那么 OptStr 就写成 "amc"。

从函数体中可以看到 "..." 部分只接收 BOOL 指针类型。

在 while() 循环的条件里,(pFlag = va_arg (pFlagList, LPBOOL)) 在每次循环时得到 "..." 中的一个参数,然后指向下一个,一直迭代直到遇到 NULL 为止。

在 for() 循环里,会遍历整个 argv 命令行选项,以查看是否遇到我们所支持的命令选项。从中可以看出,命令选项必须是以 '-' 破折号开始,并后接一个单字符的形式。因为是遍历整个命令行,所以像 -m 这样的命令选项写在命令行中的哪个位置都是没问题的。如果找到了支持的命令选项,则  wmemchr() 函数返回非 NULL(实际返回的是指向 OptStr 中选项字符所在的位置指针),所以 *pFlag 会被置为 1 。注意, pFlag 是指向 "..." 中的每次迭代到的参数的,*pFlag == 1 也就是对应的参数为 1,否则仍然为 0。这样 "..." 中的参数值就和命令选项对应起来。也就是说,当命令行中给出某个命令选项时,Options() 中的 "..." 参数列表里就有某一项对应的被置为 1 (这个参数在调用 Options() 的函数里另有他用)。

另外,iFlag 变量是对每一次循环的计数,也就是说你即使指定 "..." 里的参数的数目大于 OptStr 里给出的字符个数,那么由于 iFlag < (int)_tcslen (OptStr) 条件的限制,while() 循环在遍历完 OptStr() 的选项后也会结束循环。

最后一个 for() 循环用来查找第 1 个非命令选项的命令行参数,如果找到则返回该参数的位置。

下面是一个具体的示例:
[C++] 纯文本查看 复制代码
// setFileTime.cpp : 定义控制台应用程序的入口点。
//

#include "stdafx.h"


DWORD Options (int argc, LPTSTR argv [], LPCTSTR OptStr, ...)
{
    va_list pFlagList;
    LPBOOL pFlag;
    int iFlag = 0, iArg;

    va_start (pFlagList, OptStr);

    while ((pFlag = va_arg (pFlagList, LPBOOL)) != NULL && iFlag < (int)_tcslen (OptStr))
    {
        *pFlag = FALSE;
        for (iArg = 1; !(*pFlag) && iArg < argc && argv [iArg] [0] == _T('-'); iArg++)
            *pFlag = wmemchr (argv [iArg], OptStr [iFlag], _tcslen (argv [iArg])) != NULL;
        iFlag++;
    }

    va_end (pFlagList);

    for (iArg = 1; iArg < argc && argv [iArg] [0] == _T('-'); iArg++);

    return iArg;
}

int _tmain(int argc, LPTSTR argv[])
{
    FILETIME newFileTime;
    LPFILETIME pAccessTime = NULL, pModifyTime = NULL;

    HANDLE hFile;


    BOOL setAccessTime, setModTime, NotCreateNew, maFlag;

    DWORD createFlag;

    int i, fileIndex;

    fileIndex = Options (argc, argv, _T("amc"), &setAccessTime, &setModTime, &NotCreateNew, NULL);
    
    maFlag = setAccessTime || setModTime;

    createFlag = NotCreateNew ? OPEN_EXISTING : OPEN_ALWAYS;

    for (i = fileIndex; i < argc; i++) {
        hFile = CreateFile (argv[i], GENERIC_READ | GENERIC_WRITE, 0, NULL, createFlag, FILE_ATTRIBUTE_NORMAL, NULL);


        GetSystemTimeAsFileTime(&newFileTime);        /* 获取系统时间并转换为文件时间 */

        if (setAccessTime || !maFlag)
             pAccessTime = &newFileTime;

        if (setModTime || !maFlag)
             pModifyTime = &newFileTime;

        SetFileTime(hFile, (LPFILETIME)NULL, pAccessTime, pModifyTime);        /* 设置文件时间 */
    }
    return 0;
}

上面程序有几个功能:

1. 如果程序的当前目录下没有指定要操作的文件,那么运行程序接文件名参数,则会创建这个文件,这是因为此时 createFlag 为 OPEN_ALWASY 。如:
D:\WindowsAPP\setFileTime\Debug 的目录

2011/09/10  03:24    <DIR>          .
2011/09/10  03:24    <DIR>          ..
2011/09/10  02:44           315,080 setFileTime.ilk
2011/09/10  02:44         1,657,856 setFileTime.pdb
2011/09/10  02:44            29,184 touch.exe
               3 个文件      2,002,120 字节
               2 个目录 133,992,869,888 可用字节
运行下面命令后会生成一个名为 123.txt 的文件:
D:\WindowsAPP\setFileTime\Debug>touch.exe 123.txt

D:\WindowsAPP\setFileTime\Debug>dir
驱动器 D 中的卷是 WinSoft
卷的序列号是 6EAB-3C0C

D:\WindowsAPP\setFileTime\Debug 的目录

2011/09/10  03:24    <DIR>          .
2011/09/10  03:24    <DIR>          ..
2011/09/10  03:24                 0 123.txt
2011/09/10  02:44           315,080 setFileTime.ilk
2011/09/10  02:44         1,657,856 setFileTime.pdb
2011/09/10  02:44            29,184 touch.exe
               4 个文件      2,002,120 字节
               2 个目录 133,992,869,888 可用字节
如果 123.txt 文件已经存在,那么再运行一次时,程序会修改该文件的访问时间和修改时间:
D:\WindowsAPP\setFileTime\Debug>touch.exe 123.txt

D:\WindowsAPP\setFileTime\Debug>dir 123.txt
驱动器 D 中的卷是 WinSoft
卷的序列号是 6EAB-3C0C

D:\WindowsAPP\setFileTime\Debug 的目录

2011/09/10  03:28                 0 123.txt
               1 个文件              0 字节
               0 个目录 133,992,869,888 可用字节
这是因为,当不带任何命令选项时,setAccessTime, setModTime, NotCreateNew 3 个变量都会为 0;所以 createFlag  的值为 OPEN_ALWAYS,也就是”总是打开该文件“,那么此时 CreateFile() 执行是成功的,如果用 GetLastError() 来查看返回代码,那么会是 183 号的 ERROR_ALREADY_EXISTS,表示文件已存在。此时,在程序底下的两个 if 判断逻辑中,实际上通过 !maFlag 来强制要修改文件的访问时间和修改时间。

如果带 -c 命令选项,那么此时NotCreateNew 值为 1,则 createFlag 为 OPEN_EXISTING,如果打开一个已经存在的文件,那么仍然会修改其访问时间和修改时间。如果 -c 后接的文件名不存在,那么仅会提示找不到文件,如:
D:\WindowsAPP\setFileTime\Debug>dir 1234.txt
驱动器 D 中的卷是 WinSoft
卷的序列号是 6EAB-3C0C

D:\WindowsAPP\setFileTime\Debug 的目录

找不到文件
而 -m 和 -a 选项分别明确指定要修改文件的修改时间和访问时间。
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

小黑屋|手机版|Archiver|曲径通幽 ( 琼ICP备11001422号-1|公安备案:46900502000207 )

GMT+8, 2025-6-18 03:22 , Processed in 0.068386 second(s), 24 queries .

Powered by Discuz! X3.2

© 2001-2013 Comsenz Inc.

快速回复 返回顶部 返回列表