C++ Boost::program_options 解析命令行参数

最近要写一个exe命令行参数的东西。类似于:

abc.exe -t -input D:\abc.txt

这样的东西。因此网上搜了一下,真的有不少现成的库呢。大家说,Boost就有相关的东西,于是我决定就用Boost的program_option来做argument parsing了。

他们的库用起来也不是那么复杂,Tutorial请点此。关键是有一份Getting Started code, 就在网站上

#include <boost/program_options.hpp>
namespace po = boost::program_options;

int main(int ac, char* av[])
{
	// Declare the supported options.
	// Also noted that "help" definition line only got two string parameters,
	//	so it don't have following argument
	// But "compression" got a following 'int' argument, receiving argument
	//	input like '--compression 1'
	po::options_description desc("Allowed options");
	desc.add_options()
		("help", "produce help message")
		("compression", po::value<int>(), "set compression level")
		;	

	// Parsing the user input argument, stored in
	//  a map 'vm' (hash-map alike structure)
	po::variables_map vm;
	po::store(po::parse_command_line(ac, av, desc), vm);
	po::notify(vm);    

	// For "--help" option, determined if it appearred in para list
	if (vm.count("help")) {
		cout << desc << "\n";
		return 1;
	}

	// For "--compression" opition, first need determine its existence,
	//	then will need to retrive its value using ".as<int>()" function.
	if (vm.count("compression"))
	{
		cout << "Compression level was set to "
			 << vm["compression"].as<int>() << ".\n";
	} else {
		cout << "Compression level was not set.\n";
	}

}

这行code还是很简单的。大致来说分为三步:

  1. 定义
    你需要首先定义一个options list, 如代码里的 desc 变量。然后你可以通过.add_options()方法,来快速为这个list添加支持的定义。你可以定义一个后面不需要跟参数的option,比如”–help”;也可以定义一个需要跟参数的option,比如上文中需要后面跟着一个int的”–compression”参数。
    还有个很好的东西,就是你可以为每个option添加一段说明文字,然后你可以直接在程序中用

    cout << desc << endl;

    来输出这段说明文字。很不错

  2. 解析
    定义好之后,调用

    po::variables_map vm;
    po::store(po::parse_command_line(ac, av, desc), vm);
    po::notify(vm);

    这三句话,将main函数的输入参数 int main(int ac, char* av[]) 中的 ac 和 av进行解析,将解析结果存入到一个 variables_map 类型的叫做 vm 的字典 中去。

  3. 判断是否存在某参数
    这就跟hashtable的用法一样了。用.count()方法来判断vm是否包含某个option, 用 [] 操作符来取相应option的值(当然必须要在定义中定义了这是个有值的option,比如”–help”就不行!)

 

 

注意,在这个例子中,需要用户输入在参数前面输入两个横线(double dash)作为参数的引导,比如–help, 比如–compression,这很不习惯啊。于是我又查了查,以关键字 boost program_options single dash 搜索了一下,找打了>>这个<<网址,然后看了看原来这是为了符合Unix规范。

当然也可以告诉boost去解析单横线(single dash)引导的命令,只需要申明一个option style,然后在刚刚解析参数的时候,把这个style作为参数传入即可:

po::command_line_style::style_t style = po::command_line_style::style_t(
	po::command_line_style::unix_style ¦
	po::command_line_style::case_insensitive ¦
	po::command_line_style::allow_long_disguise );

po::store(po::parse_command_line(ac, av, desc, style), vm);

然后我就把这个command line utility写完了!Good!

 

 

C++ 遍历目录(未整理)

#define     TYPE_FILE                   0x0001
#define     TYPE_DIRECTORY         0x0002
#define     TYPE_DOT                     0x0004 

int   FileType(LPWIN32_FIND_DATA   lpfd)
{
        BOOL   bDir;
        int   ft=TYPE_FILE;
        bDir=(lpfd-> dwFileAttributes   &   FILE_ATTRIBUTE_DIRECTORY)   !=   0;
        if(bDir)
        {
                  if(  (lstrcmp(lpfd-> cFileName, ". ")==0)  ¦¦
                       (lstrcmp(lpfd-> cFileName, ".. ")==0))
				  {
					  ft=TYPE_DOT;
				  }
				  else
				  {
				      ft=TYPE_DIRECTORY;
				  }
        }
        return   ft;
} 

void ShowDir(char* dir)
{
	HANDLE hFile;
	WIN32_FIND_DATA   fd;
	BOOL ret;
	char parent[_MAX_PATH];
	char fullpath[_MAX_PATH];
	int i;
	int filetype; 

	for(i=0;   dir[i];   i++); 

	if(dir[i-1]   !=   '\\ ')
		strcat(dir, "\\ "); 

	strcpy(parent,dir);
	strcpy(fullpath,dir);
	strcat(dir, "*.* ");             

	ZeroMemory(&fd,sizeof(WIN32_FIND_DATA));
	hFile=FindFirstFile(dir,&fd);
	while(ret=FindNextFile(hFile,&fd))
	{
		filetype=FileType(&fd);
		if(filetype==TYPE_FILE)
		{
			printf( "%s\%s\n ",parent,fd.cFileName);
			strncpy(fullpath,parent,strlen(parent));
			fullpath[strlen(parent)]= '\0 ';
		}
		else if(filetype==TYPE_DOT)
		{
			continue;
		}
		else
		{
			strcat(fullpath,fd.cFileName);
			ShowDir(fullpath);
		}
	}
	FindClose(hFile);
}

 

C# WPF 弹出对话框(阻塞式)

遇到一个问题,WPF里面两个窗体,父窗体运行到某处会呼出子窗体。这个子窗体是等待用户输入,就是所谓的输入框。有两种方法:

调用VB输入框

如果在VB里面,直接一个aa=inputbox(title,content)就好了。那如果在C#里面,需要稍微麻烦一点,去调用VB版本的输入框: What is the C# version of VB.net’s InputDialog?

自己写一个

或者自己写一个窗体,放上输入框,让父窗体来调用。

_consoleWin.Owner = this;
_consoleWin.ShowDialog();

这两句足矣。主要是第二句,如果是 window.Show(),那么不会阻塞父窗体,必须要用ShowDialog()方法来进行弹出。其次,第一句话,是为了更好的用户体验,在Alt+Tab的时候不会很混乱。

详见以下文章:How do make modal dialog in WPF?

 

C# Thread.CurrentThread.Name 这个属性只能设置一次

今天遇到这么一个奇怪的问题:

System.InvalidOperationException
            Message="This property has already been set and cannot be modified."
            Source="mscorlib"
            StackTrace:
                 at System.Threading.Thread.set_Name(String value)
                 at TwitterRadio.TwitterRadioUI..ctor() in D:\Proj\TextNormalization_DEMO\TwitteRadio_WPF\UI\TwitterRadioUI.xaml.cs:line 41
            InnerException:

就是在我某一句更改当前线程名称的语句

Thread.CurrentThread.Name = "Thread_MainUI";

的时候发生了错误。说啥 “This property has already been set and cannot be modified.

于是查了查,有人说这是因为不能在线程Working的时候改名字(这个说法不准确!)。

我现在查到的答案是:

Thread.Name这个属性,只能更改一次。你可以在任意时刻更改这个属性,但只有一次改的机会。当你第二次改的时候,就会抛出这个错误,比如以下这一系列语句可以很轻易重现这个错误:

Thread.CurrentThread.Name = "Thread_MainUI1";   //OK
Thread.CurrentThread.Name = "Thread_MainUI2";   //Wrong! Exception will be thrown

为什么有这个规定?我也不是很清楚,不过查到这么一个帖子(Why is Thread.Name a write-once property?),说是频繁给线程改名字(确实啊,没事乱改什么名字),会混淆debugger之类的东西。

是为记。

Python 正则表达式入门笔记

写一个文章以后再要临时用python写正则,可以赶紧回忆起来:

初始设置:

写惯了Perl C#的语法,这Python的正则格式还是有点不同的,相当非主流啊,比如命名捕获,不是 (?<NAME> …) 而是 (?P<NAME> … ), 很奇怪啊……所有Python正则表达式格式点此查看

# import module
import re

#sample text
text = 'I5	IS_7.8777	IF_7.23205	II_5.58585'

#pre-compile a regex object.
#Noted that "Named Caputre 命名捕获" is "(?P<NAME>...pattern...)"
rexLine = re.compile('(?P<NS_TOK>[^\t]+)\t(?P<CANDS>.+)')

 匹配:

Python正则最奇怪的地方就是 search() 和 match(),一开始学的时候非常绕。直觉上,match()就是去匹配一个text,然后返回匹配了的对象;而search()看起来倒像是一个bool值函数,去寻找是否匹配。但完全不是这样, search()和match()的返回是一样的,其他功能基本都完全一样,唯一不同的是match()必须要求从第一个字符开始匹配,而search()就不用。所以一般用search()吧。

m = rex.search(text);

#use named capture
ns_tok = m.group('NS_TOK')
cands = m.group('CANDS')

 

IDLE输出:

>>> m = rex.search(line)
>>> m
<_sre.SRE_Match object at 0x000000000328EA48>
>>> dir(m)
['__class__', '__copy__', '__deepcopy__', '__delattr__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__', '__le__', '__lt__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', 'end', 'endpos', 'expand', 'group', 'groupdict', 'groups', 'lastgroup', 'lastindex', 'pos', 're', 'regs', 'span', 'start', 'string']
>>> m.group(1)
'I5'
>>> m.group(0)
'I5\tIS_7.8777\tIF_7.23205\tII_5.58585'
>>> m.group(2)
'IS_7.8777\tIF_7.23205\tII_5.58585'
>>> m.group(3)
Traceback (most recent call last):
  File "<pyshell#41>", line 1, in <module>
    m.group(3)
IndexError: no such group
>>> m.group('NS_TOK')
'I5'
>>> m.group('CANDS')
'IS_7.8777\tIF_7.23205\tII_5.58585'

 

测试是否匹配了某个group括号

rex = re.compile('(?P<NS_TOK>[^\t]+)(\t(?P<CANDS>.+))?')
m = rex.match('AB')
>>> m.groups
<built-in method groups of _sre.SRE_Match object at 0x00000000034A46B8>
>>> m.groups()
('AB', None, None)
>>> m.groups()[2] is None
True
>>> m = rex.match(line)
>>> m.groups()[2] is None
False
>>>

 

依次匹配处理每一个匹配:

首先要用 it = rex.finditer() 来返回一个iterator,然后用 for m in it 来处理每个match就好了

>>> it = rexCand.finditer(txtCands)
>>> for m in it:
	      m 

<_sre.SRE_Match object at 0x00000000034B4A58>
<_sre.SRE_Match object at 0x00000000034B4AC0>
<_sre.SRE_Match object at 0x00000000034B4A58>

 

native C++ 和 C# 在dll层面的互相调用

很多时候,项目里面既有Native C++ (unmanaged)的代码,也有C#.Net平台(managed)的代码。注意Native C++就是最正常的C++,没有ref class, ^小帽子符号等等。

搞了半个月这个东西,转过来,转过去,搜了很多资料都不是很轻松,特此留日志存照,以便将来回顾阅读,顺便提高一下访问量。

本文将要涉及:

  • 如何在C#中使用封装在dll中的native C++的函数,甚至类
  • 如何在C++中使用封装在C#中的类

首先是放上一些有用的信息,本文大部分内容来源于此:

 

 

【注】遇到一个莫名其妙的问题,是所谓的 BadImageFormatException, 于是我摸索了几个小时,最后是把C#程序的编译平台目标由 Any CPU 改为 x86 ……这都可以……是为记

C# WPF Xaml Parse Exception

做WPF工程的时候遇到一个很奇怪的问题,

An unhandled exception of type 'System.Windows.Markup.XamlParseException' occurred in PresentationFramework.dll
Additional information: Cannot create instance of 'Window1' defined in assembly 'GalaSoftLb.Wpf.MyApplication, Version=1.0.2629.15160, Culture=neutral, PublicKeyToken=null'. Exception has been thrown by the target of an invocation. Error in markup file 'Window1.xaml' Line 1 Position 9.

说我的窗体UI的XAML文件(就是上文的Window1.xaml) 解析错误??!

我打开这个XAML文件看了看倒是没发现XAML编辑器提示任何错误。非常奇怪,无从下手。

搜了一下,首先搜到的是微软WPF组的Product Manager给了个建议,说是有一些属性编辑器无法在非运行时发现。需要在Visual Studio里面的Debug -> Exceptions 菜单,添加某个Exception的选项,让VS能抛出这个选项的详细信息。然后再按F5调试启动程序,才会正确跑出这个异常。(原文在此:http://robrelyea.wordpress.com/2007/02/10/debugging-xaml-errors-at-runtime/

但我试了一下没用!

 

 

然后又找到另外一个解决问题的方法:

WPF trick: Debugging the XamlParseException

说是这个Xaml的解析过程,是由函数 InitializeComponent() 来处理的,如下所示:

public Window1()
{
    InitializeComponent();
    Thread.CurrentThread.Name = "Thread_MainUI";
    SomeOtherInitFunction();    // Some function added by myself
}

然后我就单步调进去,发现这个函数没有问题啊,结果往后运行的时候,运行到我自己定义的一个初始化函数

SomeOtherInitFunction()

时发生了问题。

原来这样也会抛出 XamlParseException 的异常啊!非常奇怪。改掉这个就没问题正常启动

是为记