九天雁翎的博客
如果你想在软件业获得成功,就使用你知道的最强大的语言,用它解决你知道的最难的问题,并且等待竞争对手的经理做出自甘平庸的选择。 -- Paul Graham

几个关于随机数组产生的函数 包括各类可重复或不重复

几个关于随机数组产生的函数 包括各类可重复或不重复

 

 

使用方法都比较简单,也不多说了,无非就是用一个数组和一个表示数组大小的整数来调用,这里要说明的是,你必须保证数组的大小要足够,其他的函数的作用可以参考具体文件的注释,我个人觉得够详细了

 

rand.h

//-------Created By 九天雁翎(jtianling) Email:jtianling@gmail.com

//-------最后修改时间:.3.26

 

 

#ifndef RAND_H

#define RAND_H

 

namespace jtianling

{

    //此算法只保证范围,还是需要自己设定种子

    //此算法生成从lowBorderhighBorder的随机数,而且包括边界lowBorder,highBorder.

    //lowBorder=<n<=highBorder

    int RandIntInRange(int lowBorder, int highBorder);

 

    //算法描述如<<Data Structures and Algorithm Analysis in C++>>

    //By Mark Allen Weiss 题目.8算法所示,根据习惯,不对outArray[]的大小做任何检验,

    //调用此函数的人应该确保这一点,outArray的大小大于等于numOfArray

    //此算法的用途是高效地产生一个不重复的随机序列

    //且此序列正好包括(1,numOfArray)中所有自然数

    void RandArrayNoRepeatInN(int outArray[], int numOfArray);

 

    //此算法产生随机的不重复的数组,数组大小为N

    //利用set容器的特性来保证没有重复,因为set容器的查找远快于一个一个查找

    //所以此方法比<<Data Structures and Algorithm Analysis in C++>>

    //By Mark Allen Weiss 题目.8算法所示算法快,而且增长慢很多

    void RandArrayNoRepeat(int outArray[], int numOfArray);

 

    //产生一个的随机序列且此序列的数值只在(1,numOfArray)

    void RandArrayInN(int outArray[], int numOfArray);

 

    //产生一个随机的序列,且序列的值为完全随机,序列的大小由numOfArray指定

    void RandArray(int outArray[], int numOfArray);

 

}//end of namespace jtianling

 

 

 

 

 

#endif

 

rand.cpp

//-------Created By 九天雁翎(jtianling) Email:jtianling@gmail.com

//-------最后修改时间:.3.28

#include "rand.h"

#include <cstdlib>

#include <ctime>

#include <algorithm>

#include <set>

 

namespace jtianling

{

    //此算法生成从ij的随机数,而且包括边界i,j.i=<n<=j

    int RandIntInRange(int i, int j)

    {

       //确保i<j,不然就交换

       if(i > j)

       {

           int temp = i;

           i = j;

           j = temp;

       }

       //确保范围正确

       return rand()%(j-i+1) + i;

    }

 

 

    //算法描述如<<Data Structures and Algorithm Analysis in C++>>

    //By Mark Allen Weiss 题目.8算法所示,根据习惯,不对arr[]的大小做任何检验,

    //调用此函数的人应该确保这一点,arr的大小大于等于n

    //此算法的用途是高效地产生一个不重复的随机序列

    //且此序列正好包括(1,n)中所有自然数

    void RandArrayNoRepeatInN(int arr[], int n)

    {

       srand( time(NULL) );

       for(int i=0; i<n; ++i)

       {

           arr[i] = i + 1;

       }

       for(int i=0; i<n; ++i)

       {

           std::swap(arr[i], arr[RandIntInRange(0, i)] );

       }

    }

 

    //此算法产生随机的不重复的数组,数组大小为N

    //利用set容器的特性来保证没有重复,因为set容器的查找远快于一个一个查找

    //所以此方法比<<Data Structures and Algorithm Analysis in C++>>

    //By Mark Allen Weiss 题目.8算法所示算法快,而且增长慢很多

    void RandArrayNoRepeat(int arr[], int n)

    {

       srand( time(NULL) );

       std::set<int> intSet;

       int temp;

       for(int i=0; i<n; ++i)

       {

 

           while(true)

           {

              temp = rand();

              if(!intSet.count(temp))

              {

                  arr[i] = temp;

                  intSet.insert(temp);

                  break;

              }

           }

       }

    }

 

    //产生一个的随机序列且此序列的数值只在(1,n),序列的大小由n指定

    void RandArrayInN(int arr[], int n)

    {

       srand( time(NULL) );

       for(int i=0; i<n; ++i)

       {

           arr[i] = RandIntInRange(0, n);

       }

    }

 

    //产生一个随机的序列,且序列的值为完全随机,序列的大小由n指定

    void RandArray(int arr[], int n)

    {

       srand( time(NULL) );

       for(int i=0; i<n; ++i)

       {

           arr[i] = rand();

       }

    }

 

 

}//end of namespace jtianling

 

 

阅读全文....

Effective C++ 第3版 Item 26详尽研究 个人认为最后一些内容有待商酌

Effective C++ 3 Item 26详尽研究 个人认为最后一些内容有待商酌

Postpone variable definitions as long as possible? 真的应该这样?

 

当然,个人承认前面的一部分都没有错,而且是一贯的很对,很有道理,最后作者提到在循环的问题上应该怎么处理,他提出了两个Aproach.

Approach A: define outside loop – 1 constructor + 1 destructor + n assignments

Approach B: define inside loop – n constructors + n destructors

 

他的结论是假如你不确定实现A比实现B好,那么就都用B,从这个意义上讲,作者的意思是大部分情况下B都要更好,但是其实呢?经过我的测试,几乎绝大部分情况下,A要更好,而且要好的多,我的结论是,除了单纯的简单内建类型,比如int,bool,double等,其他任何情况,包括这些类型的数组,你都应该使用实现A,下面是详细测试代码。

 

 

//Create By 九天雁翎(jtianling Email:jtianling@gmail.com

//博客:blog.jtianling.cn

#include <vector>

#include <string>

#include "jtianling/jtianling.h"

using namespace std;

 

void ApproachA1(int n);

void ApproachB1(int n);

void ApproachA2(int n);

void ApproachB2(int n);

void ApproachA3(int n);

void ApproachB3(int n);

void ApproachA4(int n);

void ApproachB4(int n);

int main()

{

    const int n = 8;

    vector<jtianling::pfI> pfIVec;

    pfIVec.push_back(ApproachA1);

    pfIVec.push_back(ApproachB1);

    pfIVec.push_back(ApproachA2);

    pfIVec.push_back(ApproachB2);

    pfIVec.push_back(ApproachA3);

    pfIVec.push_back(ApproachB3);

    pfIVec.push_back(ApproachA4);

    pfIVec.push_back(ApproachB4);

    vector<int> iVec;

    iVec.push_back(10);

    for(int i=0; i<n; ++i)

    {

       iVec.push_back(iVec[i]*5);

    }

    jtianling::CompareAlgorithmI(pfIVec, iVec);

 

}

 

//比较int x

void ApproachA1(int n)  

{

    int x;

    for(int i=0; i<n; ++i)

    {

       x = i;

    }

}

 

void ApproachB1(int n)

{

    for(int i=0; i<n; ++i)

    {

       int x = i;

    }

}

 

//比较string

void ApproachA2(int n)

{

    string s;

    for(int i=0; i<n; ++i)

    {

       s = "good";

    }

}

 

void ApproachB2(int n)

{

    for(int i=0; i<n; ++i)

    {

       string s("good");

    }

}

 

//比较vector

void ApproachA3(int n)

{

    vector<int> vec(n);

    vector<int> vec2(n,1);

    for(int i=0; i<n; ++i)

    {

       vec[i] = vec2[i];

    }

}

 

void ApproachB3(int n)

{

    vector<int> vec2(n,1);      //抵消ApproachA3中的一次建构析构.

    for(int i=0; i<n; ++i)

    {

       vector<int> vec(1,i);

    }

}

 

void ApproachA4(int n)

{

    int *a = new int[n];

    int *b = new int[n];

    for(int i=0; i<n; ++i)

    {

       b[i] = i;

    }

    for(int i=0; i<n; ++i)

    {

       a[i] = b[i];

    }

    delete a;

    delete b;

}

 

void ApproachB4(int n)

{

    //以下过程抵消ApproachA4相关过程

    int *b = new int[n];

    for(int i=0; i<n; ++i)

    {

       b[i] = i;

    }

    for(int i=0; i<n; ++i)

    {

       int *a = new int();

       delete a;

    }

    delete b;

}

 

测试的一次结果如下:

parameter

ApproachA1

ApproachB1

ApproachA2

ApproachB2

ApproachA3

ApproachB3

ApproachA4

ApproachB4

10

1.60E-06

3.86E-07

1.51E-06

1.29E-06

2.77E-06

3.01E-06

9.12E-07

2.41E-06

50

3.66E-07

1.66E-06

3.57E-06

3.89E-06

2.49E-06

1.16E-05

8.42E-07

9.81E-06

250

3.71E-07

3.36E-07

1.43E-05

1.77E-05

3.68E-06

5.36E-05

1.25E-06

4.66E-05

1250

3.61E-07

3.31E-07

6.85E-05

8.82E-05

1.94E-05

2.68E-04

3.43E-06

2.29E-04

6250

3.56E-07

3.31E-07

3.38E-04

4.49E-04

8.39E-05

1.36E-03

3.32E-05

1.17E-03

31250

4.31E-07

3.46E-07

1.69E-03

2.18E-03

3.28E-04

6.74E-03

2.13E-04

5.90E-03

156250

4.31E-07

3.31E-07

8.44E-03

1.12E-02

1.72E-03

3.37E-02

1.03E-03

2.92E-02

781250

4.56E-07

3.31E-07

4.89E-02

5.54E-02

9.81E-03

1.74E-01

6.97E-03

1.47E-01

3906250

4.96E-07

3.41E-07

2.14E-01

2.76E-01

4.62E-02

8.41E-01

3.49E-02

7.39E-01

Compared time in all: 2.7045e+000

           

 

可以看到,除了第一个int以外,其他的都是ApproachA要更快,而且我对ApproachB的情况还进行了一定程度的简化,也就是说,我在设定vector也好,new一个数组也好,都是仅仅创建了一个大小为1的,而在实际的应用中,直接vector<int> vec(n),new int[n]的情况也是非常多的,这么说,在相当保守的情况下,也可以把作者的话反过来说,也就是说,假如你真的不知道到底哪个实现更快的话,你应该默认使用ApproachA,实际上,当仅仅是简单类型的时候,你用ApproachB更好。完毕,假如有错误,请更正。

阅读全文....

用汇编保存下CMOS内容后的不解(附源码),信息到底在哪里?

公开的I/O接口,70h,71h,甚至72h,73h我也试过,结果内容和70h,71h一样,在我改动BIOS设置后,发现两次的文件没有什么变化(时间变化除外),按国外的CMOS Memory Map ,应该每个时间位后面跟一个Alarm时间位,事实上我打开前后,此位一直为零,是不是一旦开机,BIOS自动为此位清零?目前具体的东西不太清除,还不该尝试在这些Alarm位上写东西,因为我测试过两台电脑,都是AMI的BIOS,在BIOS中打开Alarm后没有发现如期的改动。请高人指点。

以下为我保存CMOS信息的源代码,大家可以放心尝试一下,假如你的结果和我说的不一样,请告诉我,比如在改动BIOS中Alarm后,生成的文件中各时间位后是否真的有响应变化。

assume cs:code,ds:data;,ss:stack

 

;stack segment stack

;        db 2 dup(?)

;stack ends

 

data segment

         biosdata db 126 dup(?)

         filename db 'NowBios.txt',0

         strcantcreate  db 'Why can not create a file?','$'

         strcantclose     db 'Why can not close a file?','$'

         strcantwrite    db 'Why can not write the file?','$'

        

data ends

 

code segment

start:                mov ax,data

                   mov ds,ax;以上两句确定数据段ds

                   mov cx,7Dh;此句确定readbios循环次数

                   mov bx,offset biosdata;确定写入的地点

                   mov al,0;从端口此位置开始读

        

readbios: out 70h,al;写入要从71h开始读的地址

                   mov ah,al;保存al

                   mov al,0

                   in  al,71h;从端口71h(bios CMOS端口)读数据

                   mov ds:[bx],al;保存在地址段

                   mov al,ah;恢复al

                   inc al

                   inc bx;al++,bx++下一次读下一个

                   loop readbios

 

 

 

                   mov dx,offset filename;确定创建的文件名

                   mov cx,0

                   mov ah,3ch; 定义接口,创建文件

                   stc;CF位标志以确定以下中断成功进行

                   int 21h

                   jc errcantcreate;创建失败就输出错误信息

        

                   mov dx,offset biosdata;确定写入文件的信息从哪个地址开始

                   mov bx,ax;保存文件句柄

                   mov cx,7Dh;确定写入多少数据

                   stc;CF位标志以确定以下中断成功进行

                   mov ah,40h;将数据段的内容写入文件

                   int 21h

                   jc errcantwrite

 

                   mov ah,3eh;关闭文件

                   int 21h

                   jc errcantclose

                   jmp theend

 

errcantcreate:          mov dx,offset strcantcreate

                   mov ah,9

                   int 21h

                   jmp theend

 

errcantclose:   mov dx,offset strcantclose

                   mov ah,9

                   int 21h

                   jmp theend

 

errcantwrite:   mov dx,offset strcantwrite

                   mov ah,9

                   int 21h

                   jmp theend

 

theend:             mov ax,4c00h

                   int 21h

code ends

end start

不想编译的可以下载以下网址编译好的程序

http://disk24.sh.com/?vagr

http://groups.google.com/group/jiutianfile/files

最后生成的是16位的文件,通过UltraEdit查看就可以了,具体应该的含义可以参考CMOS Memory Map,希望你能告诉我你的电脑是否符合上述CMOS Memory Map。谢了

阅读全文....

关于使用汇编所想到的……IT公司企业文化和实力的问题,看看它的技术支持就可见一斑

大的IT公司都有他们专门维护的大型支持,如微软,IBM,SUN等,这些大家都知道吧,其实我发现Intel作为硬件厂商,它的技术文献也多的可以了,用罗云彬的话就是说不是没有你要的资料,而是不知道怎么找到。这点,Intel比起上面3家公司稍微欠缺一点,因为他们都是社区型的大型技术支持,Intel的文献资料虽然多,不过稍微显得乱了点,也许因为我对这方面了解少,所以搜索起来不是那么顺利吧。但是碰到再差一点的公司,你就要郁闷了,我在AMI(我的主板的BIOS)生产厂商,的主页上,只找到了主板查错码和叫声的含义,其他没有找到任何编程相关的帮助资料,更不用说像Intel那样的架构分析,中断列表什么的了。这点实在是够郁闷,最后只能在非官方找资料,最后果然感觉资料可能太老,然后有点不对。何其郁闷啊。这是昨晚搞了半天,想说又没有说的,我在这里一般都只发技术性的东西,但是这次突然想说一说。

阅读全文....

正则表达式测试器0.4(Boost Regex Tester 0.4) (最新版本)

这个死程序从年尾做到年头,中间过年休息了太久,结果就像分成两次来做一样。首次在正则表达式测试器中用了MFC的文档结构,实际上让这个本来很小的程序越来越大,而事实上功能稍微多了 一点而已。

本版本在利用Boost.Regex库实现正则表达式的功能上基本已经完整,而且使用方法上有点类似Windows内置的查找替换功能(其实那个东西就可以利用正则表达式)。余下的版本不准备再进行大的更新,仅仅准备加入常用表达式这一个内容,由于对于学习了一下OLE DB,但是感觉还比较麻烦,不知道怎么简单的插入此程序,以及其与VS 2005的联合使用,又不准备利用普通文档格式来实现,所以此功能准备等我真正学好了数据库编程后再实现。除非有重大BUG,不然我不准备在别的方面对此程序进行更新了。另外,讲此文档作为一个在线帮助文档长期更新,并在程序中加入博客链接。有任何BUG请在此文留言反应,建议也可以。

本程序的截图:)

 

界面基本还算满意。另外,不得不说说编写这个程序的感受,真的感到要写一个好程序有多么累了。特别是感觉到MFC视图文档结构的复杂性,因为用了太多分割视图,UpdateAllViews的我郁闷的要死。程序源码看起来也完全没有标准C++的命令行程序那么简洁优美,看到的都是非常丑陋的代码。。。。。当然,也许和我MFC的功力不够有关。。。。。

源代码和文件都在以下下载地址

下载地址:http://disk24.sh.com/?vagr

原下载地址也可用:http://groups.google.com/group/jiutianfile/files

阅读全文....

开始学习win32汇编

前段时间对于csdn的博客系统彻底失望,在我现在写的东西都还不知道能不能正常发出去,郁闷,加上过年有这么久没有来了。这段时间开始学习汇编,说到汇编,大学开过两门相关课程,微机原理和单片机都是学这方面的,不过那都是8086,51级别的汇编,感觉根本跟不上时代,那时候学的还不错:)现在想学习学习win32的汇编,首先用王爽的书复习一下8086的汇编,因为masm32的ide的编辑功能实在太弱,还是用vim做比较好,做了几个文件,鉴于很久什么也没有发,就发上来吧,以下是我加的vim配置文件

set helplang=cn
set expandtab
set autoindent
colorscheme desert
set fileencodings=ucs-bom,utf-8,cp936
set guifont=新宋体:h13
set encoding=cp936 "set encoding=utf-8
map <F6> :!debug %:r.exe<CR>
map <F5> :!cmd /K %:r.exe<CR>
map <F9> :!ml /Zm /Bl link16 % <CR>
set statusline=%F%m%r%h%w/ [FORMAT=%{&ff}]/ [TYPE=%Y]/ [ASCII=/%03.3b]/ [HEX=/%02.2B]/ [POS=%04l,%04v][%p%%]/ [LEN=%L]
set laststatus=2 " always show the status line
set acd

 

这里碰到的最大问题是现在masmv9的连接器只支持32位,所以特意下了个16位的连接器,命名为link16,对了,罗云斌说的做一个.bat文件来设置path,我都在控制面板里面设了,感觉更方便一些。

阅读全文....

新装Ubuntu想起的往事

很早以前在读大学的时候就开始使用Linux,刚开始还因为自己水平不行又不舍得买本书研究,自以为是,直接下载刻了盘就装,当时装的是Red Hat 9,开始就闹了个笑话,因为下载和刻的都是源代码盘。。。。。根本装不了,我才知道,应该下二进制文件。。。。

重新下载,再次刻盘,可是那时候根本不知道什么叫根目录/,什么叫交换区,什么叫/home,/usr等等,结果很明显,我直接把所有的盘都弄的格了,还是没有装好RH9,因为我不知道需要一个根目录和交换区,就这样,我一不做二不休,干脆Windows都不要了(刚开始是想双系统),用同寝室的电脑查Google,先知道了怎么安装,装好后那个兴奋啊,我那时候正要过4级,管他呢,我用全英文版,这个结果还引来室友的议论,"怎么连中文都不支持啊?",呵呵,其实是我想用英文版。

然后,第一个问题,自然是解决上网问题啊,不然不能老霸着同学的电脑吧,虽然这个问题看来比较容易解决,不就设置一下网卡吗?错了,学校当时用的是一个专用的上网拨号软件,连接到学校的服务器,然后上网,可不是现在这样用pppoeconf就可以解决的问题啊,我找到了学校的网络管理员,询问了一下这方面的问题,得到的结果是没有Linux版本的拨号软件,没有办法,只好问了一下学校服务器的情况,结果是他们使用的是华为的交换设备。好了,我去华为那看看,结果是根本他就不关心Linux(当时起码是这样),在无奈之中只能看看学校的拨号软件使用的是什么协议了,起码华为自己应该不会创造什么网络协议吧,果然是个通用的协议(已经忘了,好像不是目前最常用的几种),然后网络上还真找到了相关的此协议拨号软件的Linux版本,这才让我可以上网,也是在这时,我认识了FireFox,FireFox的易扩展性给我留下的非常深的印象,现在也是。

能上网了,一切就简单多了,都用自己电脑网上解决吧。首先解决屏幕分辨率问题,用了老半天,因为要修改配置文件,我用大家都推荐的VI吧,结果一通饱学,果然够复杂,不管用熟了多么好,号称Number1,但是易用性实在是够夸张的,然后装了个Nvidia的官方驱动,终于是能够看了。

先起码能听音乐吧?Linux因为版权问题,没有直接附带MP3播放器,自己搞一个吧。XMMS大家都推荐,不错的,那时候RH9使用的是号称简单的RPM包管理系统,使用别提多复杂,为了一个又一个依赖的包,我真的是快晕掉了,rpmfind类的网站是逛了个遍,最主要的是,好像不同版本的包都肯定不能兼容,必须完全符合版本,晕了,光搞个XMMS我用了一整天,当第一首MP3从电脑中放出来时,不要说有多自豪了,而反观Windows中,当时只需要下个Winamp就可以了,所以对Linux的复杂性有了深刻认识。

接下来要看看电影吧,噩梦的继续,大家都推荐mplayer,好的,我有开始了tree目录式的追寻,A包需要BCD包,然后B包又需要EFG包,E包又要HIJ包,等我搞好后,人都瘦了一半,经历了一天以后,终于也是搞好了,不要觉的夸张,对于我那样一个新手,真的是用了那么久时间。

然后呢?要用C编译器,总要找一个吧,不记得那时候找的什么了,不过起码认识了GTK+,WX,QT等库,知道了一些基本内容,搞了本QT和什么linux下的编程的书来看,基本看不懂,无果而终。那时候还看到了一些奇怪的名词,pango之类的。

然后再租了本关于RH9的书来看,理解了一些基本概念。中途最大的收获是,再次安装了RH9,并将硬盘的分区按书上所说的进行了优化,比如/,/usr,/home,/boot和交换区等等进行了合理的分配,我才知道原来Linux里面是没有所谓的CDEF盘的,都用文件挂载,连设备都是这样,比如cdrom就挂在/media/cdrom下,而且使用mount就可以直接使用虚拟光驱。使用完全安装而不使用默认安装实在是个进步,不要说这样会多占多少硬盘(那时候RH9是4张碟),当我不停的去跟踪树后,我得出的结论是,完全安装是不缩短生命的一个好办法,那以后安装需要追寻的树就小了很多,虽然还不能够排除。在需要自己编译一个程序的时候更是这样。

最后认识了伊甸园和ChinaUnix从此好了很多,也认识了yum这个人性化的包管理器,也是实实在在的从一个一个文件包的树目录中的追寻中摆脱了出来。后来还为很多细节问题不停的GOOGLE着,现在能回忆起来的还有RH9下USB的支持问题。

在后来的我认识到fedora core 4是当时Red Hat公司最新的产品,我改下了一个FC4,并刻盘,开始使用FC4,用FC4时因为认识了Yum,所以一切的软件安装都非常顺利,无论是XMMS,Mplayer。然后我一直一边GOOGLE一边使用着Linux,到我发现,我真的每天开始使用Linux而不是总在研究让他实现某个功能的时候,也就是真的把他当作一个日常使用的操作系统的时候,已经过了大半个月了,这实在是个不短的学习时间,对于一个操作系统来说,在后来的日子里,我一直使用着FC4,与室友完全不同,而且每每碰到同辈众人,(在买碟的时候和在实验室做自动控制实验的时候分别碰到一个)都特别兴奋,因为,好像在中国,我那个环境,使用Linux的人实在太少了,与那两位仁兄大侃特侃之后才发现,这两位同学原来都还是准备安装还没有付诸实践。。。。天哪。

有点深有体会的是,Linux不能普及的最大原因是因为Windows已经垄断了市场,所以它必须得不停的想办法兼容windows。就算在核心上不这样,在Linux 上的软件却不得不这样。我用samba和同学共享文件,用openoffice做成pdf文件才能拿去打印,(这是当时openoffice可以自豪的一件事,现在word2007也可以了)因为openoffice保存的文件不一定能和office兼容,老师的powerpoint幻灯片在openoffice上总是有些不尽人意的问题,我用scilab替代同学使用的matlab,原因不是matlab没有Linux版本,而是Linux版本我没有找到破解。。。你叫我买正版?我没有钱,很显然的问题是,windows的易用性很大一部分是由破解带来的,没有破解的东西,估计用windows的人会少很多,但是使用Linux的日子无论我多么想说我可以摆脱Windows,却终于到头了。因为老师指定的EDA软件,Linux没有。。。。。虽然我尝试过很多Linux的VHDL,Pspice软件,但是我最后不得不放弃,因为我们要用MAX+PLUSⅡ做一个课题设计,于是。。。。终于,我只能重新装上了Windows,我实在得把这一点算在Linux不能普及的一点上,因为在中国,学校都是使用Windows,而且似乎把这当成了唯一的操作系统(起码在我所在的电子系可能是这样,计算机系应该会好一点),所以他们用的软件都是Windows下的,也不会考虑,你们有人在使用Linux,假如计算机系的人都要求用VC的文件交作业,我想,那也会和我的情况一样。。。。你不能奢求,微软会做个Linux下的VS给你。

那个课程设计忙完以后,因为要期末考试了,所以后来也没有装。再后来,我为我的电脑装了双系统,一个windowsXP一个FC4,但是其实使用的最多的还是Windows了,再后来,FC6发布,我开始刻盘安装FC6,说实话,没有感觉有太大的突破,反而我的电脑在FC6下老死机,郁闷。后来因为习惯用VS编译C++程序,并学习c++,所以更没有关心什么Linux了。再到最近,看到了很多Ubuntu的消息,我突然心痒,下了个Ubuntu 8.10,开始装Ubuntu,后来发现原来8.10还是alpha版本,删了开始装7.10,目前使用的就是Ubuntu 7.10。

对于Ubuntu 7.10从安装到正常使用是我想特别说一下的。首先,虽然刚开始我还使用apt-get来安装和升级包,我后来发现原来Ubuntu提供了一个GUI的新立得软件包管理,这实在是更大的突破,个人认为比3d桌面什么的更来的实用。我想,让命令行和配置文件更改更进一步的消失是Linux普及的前提,不要说那样有多么大的自由,有多么大的优势,这年头,要的就是很多人称之为小白的操作,不然难以流行,要知道,大多数人没有心思去研究太多东西,易用是最重要的,所以那么多不见的好用,但是操作简单的东西才能流行,比如windows,傻瓜相机等等。而这本身就代表着一种先进的技术,那就是用户体验的改进技术。

然后,装nvidia的官方驱动,仅仅在受限驱动程序中点了个复选框然后应用就完成了,我简直感动的要哭了。。。。。最后,不能不搞个那么热的3d桌面,弄了各compiz,好的,特效真的夸张,起码我第一感觉真是这样,cube效果的确华丽,各种窗口转换效果也的确很出色,包括各种Widget,但是我最惊讶的其实是动画选项,那简直就是将整个操作系统变成了一个幻灯片在做,包括各种窗口的打开,关闭,最小化,甚至菜单的弹出,工具提示的弹出都能应用特效,当我常识点击全部随机特效的时候,我发现那样太眩晕了。。。。华丽到超出承受范围。。。。所以还是稍微改了下,具体的设置,大家一起去尝试吧,网上也很多了,秀的也很多了,我就不丢丑了,不过我要说的就是,Ubuntu的易用性实在是进步了很多,包括对ntfs的读,对fat的读写的直接支持,这在以前要弄很久的。是的,在现在,我可以说,Unbuntu已经可以作为日常操作系统使用了,但是,前提是,你不能是个游戏狂。。。。。

阅读全文....

C++ 中的DOS命令调用(3)——我不提倡大量使用DOS命令

    虽然我个人认为DOS命令有它的可取之处,并想了办法将其更好地融入到MFC程序中来,但是我个人并不是太提倡大量的使用DOS命令,因为毕竟很多东西用了DOS命令要改都难,而且DOS版本不一,以及很多Microsoft专属的DOS加强命令都让程序的移植性差了很多,而且,很重要的是,用了太多DOS命令,对于只学了C++而没有经历过DOS时代的人来说那简直就是天书,况且,不是人人都那么想去学习DOS 的,而Windows API大家都会觉得多了解几个没有坏处。另外,绝大部分DOS命令都有相关的Windows编程解决方案,比如微软为你提供的类库或则API中都有方法解决DOS命令可以完成的任务,而DOS命令一开始我就提出了,因为都是使用char *的方式调用,所以很大程度上加大了调试的难度,很多时候只能开个CMD窗口去一次一次尝试,问题是,各种情况有的时候没有办法都考虑到,就有了产生神秘BUG的可能,而微软的类库和API这方面就要好的多。还有一点就是如同我在以前例子中演示的那样,DOS命令很多的命令输出是为了在屏幕上输出信息的,比如DIR等,所以会有很多附加信息,导致你需要得到确实需要的信息需要通过一些转换,在前面的例子中我就是使用了正则表达式来完成这些任务,虽然比一般方法可能容易点,但是还是比较复杂,而类库和API一般都能直接得到需要的信息,这也是他们的优点。

    当然,我要说的也不是多么害怕使用DOS的命令,而且个人把调用命令行的程序都看做DOS命令的一种延生形式,可以作为还不明白进一步Windows API时的一种补充,而且,有的时候一个DOS命令可能只需要一步的功能要实现起来却可能需要很多行程序,另外,可以很好的利用一些别人编写的足够好的命令行程序来为自己程序增添一些本来很难以实现的功能。在这里,我还是提供几个个人认为值得一用的DOS命令:

FC,COMP: 比较文件

PING:不用说了吧

REGEDIT /E:导出注册表 REGEDIT /L:导入注册表

XCOPY复制文件和目录树,比SHFileOperation容易使用的多

DELTREE:删除目录,也比SHFileOperation容易使用的多

PROXYCFG,FINGER,TRACERT,IPCONFIG,ROUTE,TRANCE,NETSTAT,NET,FTP,TELNET:估计要通过一般编程实现他们的功能不是那么容易的吧,但是假如使用这Windows给你的命令行程序就简单的多了。其实由于个人还不是太会网络编程,所以才会觉得使用Windows提供的网络相关程序会使得一些网络功能的实现容易很多,假如精通了网络编程不知道还是不是这样。

At:计划

SHUTDOWN:关机

tskill:结束进程

另外推荐Pstools工具组,还有Winrar的命令行版本RAR,UNRAR,有了他们你可以很方便的压缩和解压程序,而不需要去学习怎么调用库。基本上;也就这些了,有什么以后再补充。

 

 


 

阅读全文....

C++ 中的DOS命令调用(2)——瞒天过海,隐藏DOS调用的命令行窗口

上一节我演示了一下在MFC程序中怎么应用DOSdir的命令,可是我们遇到了需要解决的问题,首先就是文件dir.txt的残留问题,其实这个问题很简单,我们也可以用dosdel命令在操作后将dir.txt文件删除,这样的结果就是程序会两次弹出窗口,这样更加让人无法接受了,现在我们的问题是,有没有办法隐藏弹出窗口?答案是有的,这点我在网上找了很久,都没有找到解答,最后自己摸索出了一些方法,不知道还有没有更好的方法,因为这些方法都有些缺点,比较恼火。

首先,我们可以用第二参数为SW_HIDE调用WinExec()这个windowsAPI,这时候又有个问题了,WinExec()是调用程序的,而DIR程序文件在哪里?呵呵,其实这点在有很多DOS使用经验的人都知道,DOS命令在以前就分两种,一种叫内部命令,一种叫外部命令。其中比较常用的比如dir,del都属于内部命令,特点是直接加载进内存。而外部命令是可以在目录中找到具体的文件的,当时就会常常遇到PATH设定不对导致的外部命令调用错误,而需要找到目录去调用的情况。既然加载在内存里面,我们到哪里去找命令文件?答案我不知道,不过有个变通的方法。因为在windows中,DOS的实际调用都是用cmd程序,那么我们就用它来调用,具体方法是以/C为参数调用。比如我们要调用dir命令,那么具体方法如下:

WinExec("cmd /C dir");

理由可以参考help cmd。当然我们还是有以前那样的问题,那就是dir后面跟具体目录做参数的时候需要加引号,那么我们调用目录的时候一般可以用下面的形式:

WinExec("cmd /c dir /"xxxxx/");

这种方法在直接调用的时候很好用,比如删除文件的时候,直接一个WinExec("cmd /c del dir.txt");就可以了,但是也是有个问题,这是个太老的API了,所以根本没有Unicode的版本,苦闷的Unicode版本程序因此无法较好的使用,在以CString为字符串调用的时候似乎只有两种方法,一种是以ANSI方式编译,一种就是通过UnicodeANSI的转换了,这样的转换很可能还会丢失中文信息。因此,个人推荐只在直接调用DOS命令的时候使用WinExec,而且也推荐直接调用的时候使用,因为WinExec只需要两个参数,很容易调用。但是要在Unicode程序中以CString为参数调用怎么办?当然,用MSDN中推荐的CreateProcess不会有任何问题,问题是,太复杂了。。。。。个人推荐另一个方案,ShellExecute。虽然比WinExec复杂一点,但是还可以接受。函数原型如下:

HINSTANCE ShellExecute(

HWND hwnd,

LPCTSTR lpOperation,

LPCTSTR lpFile,

LPCTSTR lpParameters,

LPCTSTR lpDirectory,

INT nShowCmd

);

这里,hwnd直接用窗口的句柄就可以了,lpOperation可以省略,默认为open,lpFile我们调用cmdlpParameters我们加入参数,注意的是cmd要多个/C参数,目录可以为空,nShowCmdSW_HIDE以达到我们的目的,同上面的情况,调用ShellExecute的时候为以下形式:

ShellExecute(m_hWnd, NULL, _T(cmd), _T("/C dir /"xxxxxx/"");

比如在上一节的例子中,我们调用ShellExecute的方法就是下面这样:

CString dir = _T("/C dir /"") + m_directory + _T("/" >dir.txt");

ShellExecute(m_hWnd, NULL, _T("cmd"), dir, NULL, SW_HIDE);

然后,改变后你会发现程序在第一次调用的时候不会有任何反应,一定要第二次点击按钮才能生效,原因可能会令人很困惑,有多线程编程经验的人可能会反应过来,因为ShellExecute的调用实际上是新开了一个线程,那么所有关于多线程编程让人郁闷苦恼烦躁的问题都完全适用。这里,问题在于,ShellExecute新开线程后直接返回了,不等dir调用完成,那么下面接着的打开文件根本就找不到文件,我给出一种解决方案,在打开文件的时候检测一下,然后用Sleep休息200毫秒,再检测,如此可以达到需要的要求:

while(!infile.is_open())

{

static int i = 0;

::Sleep(200);

infile.open("dir.txt");

++i;

if(i > 50)

{

MessageBox(_T("Error to create and open the file"));

exit(EXIT_FAILURE);

}

}

经过完善,上节中ReadFromDir函数完整结果如下:

std::string CTestDialogDlg::ReadFromDir()

{

UpdateData();

CString dir = _T("/C dir /"") + m_directory + _T("/" >dir.txt");

// _wsystem(dir);

std::string strFile,strTemp;

ShellExecute(m_hWnd, NULL, _T("cmd"), dir, NULL, SW_HIDE);

// WinExec(dir, SW_HIDE);

std::ifstream infile;

while(!infile.is_open())

{

static int i = 0;

::Sleep(200);

infile.open("dir.txt");

++i;

if(i > 50)

{

MessageBox(_T("Error to create and open the file"));

exit(EXIT_FAILURE);

}

}

while(std::getline(infile, strTemp))

{

strFile += strTemp + "/n";

}

infile.close();

WinExec("del dir.txt", SW_HIDE);

// _wsystem("del dir.txt");

return strFile;

}

经过如上改变,再使用程序,没有看到源代码的人,谁还知道你是用了DOSDIR命令实现的呢?

这一节的主要内容是给广大因为使用了DOS命令而导致程序运行效果不佳老弹出窗口的同志们信心,大胆的调用DOS命令吧,没有人知道的,只要能简单的完成任务,我们不择手段,呵呵。

阅读全文....

C++ 中的DOS命令调用(1)——还可以记起什么?从DIR开始

不要搞错了,我是讲怎么在C++中利用DOS命令,不是准备讲DOS编程。以下都以Windows XP中的MS-DOS为例,程序一般也以MFC程序为实例,在VS 2005下编译通过。

很久以前用电脑的时候还属于蛮荒时代,那时候都是黑屏白字的DOS,怎么说都是一种回忆了,现在谁还用那东西啊?呵呵,似乎以前用电脑的经验都是等于废了,记得我前两年考的C二级是最后一届需要考DOS的,虽然那时DOS也只在里面占了几分而已。

难道现在DOS真的没有一点用了吗?其实也不全是,对于个人操作来说,高手会告诉你命令行操作虽然复杂的多,但是永远比GUI操作来的快。在C++中调用DOS命令虽然感觉上可移植性能不是太好,但是有的时候的便利实在是无法言喻,避开了一大堆的API学习,让以前学习的DOS知识发挥余热,何乐而不为呢?

下面本人列举一些在C++DOS命令的调用,让以前也熟悉DOS的同辈中人一起回到DOS时代,看看DOS时代的命令其实也是有很大学问的啊:)--至于以前没有学过DOS的人,就没有太大必要再来学习了,因为个人感觉重新学DOS和重新学Windows API区别不是很大,一般来说windows API还有更大的通用性和安全性,用DOS命令可以说很大程度上都没有办法调试源程序,因为大量的命令都是const char*形式调用的,编译器没有办法来帮你查错误,这些可以说是DOS命令在C++中调用的缺点,但是,假如有时间,DOS命令很多时候的便利也是值得学习的,另外,因为dos以前可是一个操作系统,所以从理论上来说,光用dos命令可以完成的任务都是数不清的。。。。。。

废话说多了,下面让我们重新回到DOS时代,看看我们能怎么用DOS命令简化我们的编程。

基础:在C++中调用DOS命令的基本方法,就是system(const char*);形式;比如,DEVC++中最常用的system("PAUSE"),其实就是DOS中的一个命令,用来暂停程序,并输出"按任意键继续",那可就是一个DOS命令的调用啊,呵呵,以前还不知道吧。

下面一边回顾DOS的命令,一边让我们来看看他们都能干什么吧。

首先,最常用的DOS命令自然是DIR了,呵呵,虽然现在有的时候习惯LS了,不过DIR也很好用啊。

这里要讲的是,光system("dir")在程序中仅仅是输出在屏幕上而已,对于编程的实际帮助不是很大,该怎么调用呢?这里有个小的技巧,用">"操作,使用dir >xxx.txt的时候,dir的屏幕输出就被输出到xxx.txt文件中了,然后可以在程序中分析调用。当你需要看某个目录的时候其实一条命令也够用,用dir x:/xxx/xxx >xxx.txt的形式就可以了,这些其实都属于DOS知识。以下都以MFC程序为例,看看怎么使用DOS命令。呵呵,再次申明,用DOS命令仅仅是一条路而已,不要抱怨其他问题。我不是说没有别的办法啊,但是起码要另外学习,在没有学习之前用DOS命令作为替代也是不错的。谁说的来着,"There are more then one way"。剩下的就是对于输出文件的字符分析了,这个比一般的方法难点,不过,我们有正则表达式嘛,一切能够难道哪里去?对了,忘了说,假如是Unicode程序的话,是要用_wsystem的。

比如现在有个需要,在对话框的一个编辑栏中输入某个目录,我们用一个列表框输出该目录下的所有目录或者文件,由一个单选按钮控制。似乎好像有点复杂。。。。,让我们完成他。

先建立一个MFC对话框程序,然后建立一个编辑框,一个列表框,三个Radio按钮和一个普通的按键,最后程序界面大概如下:

dos1.jpg

首先我们完成DIR的调用。为编辑框添加一个CString类型的控件变量取名为m_directory,最后只需要下面三行就可以了。

UpdateData();

CString dir = _T("dir /"") + m_directory + _T("/" >dir.txt");

_wsystem(dir);

需要解释的就是dir的构成了,因为在dos下调用命令,dir后面的目录参数假如有空格是不行的,比如最常见的program files目录就不行,所以为目录参数加上引号,这样就算有空格也没有任何问题,在C++中引号用转义字符后加引号表示,另外,后面用管道把内容传给dir.txt文件。

这里完成一个MFC的函数封装,最后将文件内容保存为string。以下面函数完成任务:

std::string CTestDialogDlg::ReadFromDir()

{

UpdateData();

CString dir = L"dir /"" + m_directory + L"/" >dir.txt";

_wsystem(dir);

std::string strFile,strTemp;

std::ifstream infile("dir.txt");

while(std::getline(infile, strTemp))

{

strFile += strTemp + "/n";

}

infile.close();

return strFile;

}

现在最麻烦的就是处理dir.txt文件了。恩,的确比较麻烦,假如没有正则表达式的话。当然有了正则表达式就没有那么麻烦了。这里我以我最习惯的boost库正则表达式为例,假如你还不会的话可能看不懂,可以到www.boost.org去看看在线文档。我建立以下函数来分析目录:

std::string CTestDialogDlg::MatchDirectory(const std::string &strFile)

{

boost::regex re("<DIR>//s+([_//w][^//r//n]+)");

boost::smatch result;

std::string::const_iterator start = strFile.begin();

std::string::const_iterator end = strFile.end();

boost::match_flag_type flags = boost::match_default;

std::string strDir;

while(boost::regex_search(start, end, result, re, flags))

{

strDir += "<DIR>" + result[1] + "/n";

start = result[0].second;

flags |= boost::match_prev_avail;

flags |= boost::match_not_bob;

}

return strDir;

}

其他的不多说了,不然不知道多浪费时间,至于正则表达式re的构成主要看到dir命令出来的文件格式中,目录都由<DIR>后面跟具体目录名,这里就匹配这一点,然后取<DIR>后面空白字符的第一个字符到最后一个字符即匹配后的result[1]为一个目录名,这里为了方便区分,也和dir一样,为目录加上<DIR>前缀。

文件名的匹配也基本类似。

std::string CTestDialogDlg::MatchFile(const std::string &strFile)

{

boost::regex re("//d{4}-//d{2}-//d{2}//s+//d{0,2}://d{0,2}//s+[//d,]+//s([^//r//n]+)");

boost::smatch result;

std::string::const_iterator start = strFile.begin();

std::string::const_iterator end = strFile.end();

boost::match_flag_type flags = boost::match_default;

std::string strDir;

while(boost::regex_search(start, end, result, re, flags))

{

strDir += result[1] + "/n";

start = result[0].second;

flags |= boost::match_prev_avail;

flags |= boost::match_not_bob;

}

return strDir;

}

匹配文件名的正则表达式之所以这么复杂,主要是为了避免在dir.txt文件的前面有类似的文字,所以才完全的匹配了一行,当然,最后留下需要的文件名就可以了。

然后在搞个函数将字符串内容写入列表框就可以了。首先为列表框建立一个相关的控件变量,取名m_list

void CTestDialogDlg::WriteToList(const std::string &strList)

{

m_list.ResetContent();

std::stringstream strStream(strList);

std::string strTemp;

while(std::getline(strStream, strTemp))

{

CString str(strTemp.c_str());

m_list.AddString(str);

}

UpdateData(false);

}

这里利用了stringstream的特性来完成每行的读取。

最后为按键添加一个按键处理程序,就可以完成任务了。这里为radio控件添加一个相关变量m_radio,以此用switch决定完成什么功能。

void CTestDialogDlg::OnBnClickedButton1()

{

std::string strFile = ReadFromDir();

if(strFile.empty())

{

return;

}

std::string strList;

switch(m_radio)

{

case 0: //目录

strList = MatchDirectory(strFile);

break;

case 1: //文件

strList = MatchFile(strFile);

break;

case 2: //全部

strList = MatchDirectory(strFile);

strList += MatchFile(strFile);

break;

}

WriteToList(strList);

UpdateData(false);

}

好了,这样一个程序就完成了全部想要的功能,看看运行效果: dos2.jpg

其实程序的最主要部分就是DOS命令dir的调用了,所有的程序都是围绕此命令完成。当然,这时候完美主义者可能会发现两个问题。首先就是程序运行后留下了dir.txt文件,调用dir命令的痕迹太明显,另外就是DIR调用的时候会在MFC程序运行的时候弹出一个黑色命令行窗口,虽然此窗口由于DIR的调用速度很快所以一下就关闭了,还是有点影响程序的效果,起码别人知道你是调用了命令行了,其实也不是没有解决办法,请看下节,瞒天过海--隐藏DOS调用的命令行窗口

阅读全文....