用VS.Net编写扩展存储过程(三,完)

原创 2004年08月27日 19:14:00

昨天忙着写这个,没看奥运会,刚知道老郭和小吴又让外国人郁闷了一把。不知道奥委会是不是又要对跳水改变规则哪?

在上一节,我们把含有两车站的所有火车ID和路线信息分别读入了int *TrainID和CHAR** ppcStations,完成了最后一次读入数据的过程,下面就要处理这些数据以获得结果……就要结束了,前途是光明的,道路是平坦的……风是背后吹来的……有MM在身边陪着的……晕,忘了告诉各位兄弟,小弟现在不吃西红柿了,改吃鸡蛋,请多多关照积极配合,谢谢~~~

第三步,寻找最短的路线

显然,首先要对路线信息的字符串做分词处理,知道了从一站到下一站需要多长时间,才可能计算最短路线。本文中将用.Net类库中提供的正则式类来分割这些字符串……当然用普通方法也完全可以处理。

要使用正则式类,就要用到托管代码,我还是只讲应用,不讲原理。对托管代码工作机制感兴趣的朋友,请参阅MSDN中的托管编程部分。

先选择xsTrainQuery项目的属性,将“配置属性”下“常规”中的“使用托管扩展”改为“是”,随后在proc.cpp中加入如下预编译头:

#using
#using
using namespace System;
using namespace System::Text::RegularExpressions;
对于char*字符串,必须转化为托展字符串类型,即System::String*指针,才能用正则式类处理。我是用这样的方式把char*转化为System::String*:
System::String *newStr = System::Text::Encoding::Unicode->GetString(System::Text::Encoding::Unicode->GetBytes(oldStr));
现在就到了这一步的重头戏——正则式分词。不知道大家有没有掌握.Net的正则式,如果象我一样是菜鸟,还是让MSDN随时待命吧~
{
    // 把起点和终点站由PBYTE转化为System::String*
    System::String *strStart = System::Text::Encoding::Unicode->GetString(System::Text::Encoding::Unicode->GetBytes((PCHAR)pbStart));
    System::String *strEnd = System::Text::Encoding::Unicode->GetString(System::Text::Encoding::Unicode->GetBytes((PCHAR)pbEnd));

    // 定义正则式模式
    // 用两个//,是因为|在正则式中有特定含义,必须在|前加上/转义,而//代表了/
    Regex *r = new Regex(S"( ?://|(.+?),(.+?)小时)+//|");

    // 定义循环中将用到的变量
    int iOldTime = INT_MAX;
    int nFastest = 0;

    // 万事俱备,开始处理ppcStations指针数组
    for (int i=0; i    {
        // 用r来匹配ppcStations[i]
        Match *m = r->Match(ppcStations[i]);
        // 所有站点的匹配组
        System::Text::RegularExpressions::Group *gStation = m->Groups->get_Item(1);
        // 所有时间的匹配组,与站点匹配组按顺序一一对应
        System::Text::RegularExpressions::Group *gTime = m->Groups->get_Item(2);

        // 定义ppcStations[i]所代表的路线所需时间
        int iNewTime = 0;
        // 定义是否已经找到起点站的BOOL变量
        BOOL bStartFound = FALSE;

        // j从0到gStation中捕获的个数,即总共捕获到多少个站点
        for (int j=0; jCaptures->Count; ++j)
        {
            if ( !bStartFound )
            {
                // 如果尚未找到起点,则判断当前捕获站点是否为起点
                if ( gStation->Captures->get_Item(i)->Value->Trim()->Equals(strStart->Trim()) )
                     bStartFound = TRUE;
                // 即使找到了起点,也直接continue,因为起点站对应的时间对运算无意义
                continue;
            }
            else
            {
                 // 已经找到起点站,开始累加路线时间
                 iNewTime += Int32::Parse(gTime->Captures->get_Item(i)->Value->Trim());
                 // 看当前捕获站点是否为终点,若是则退出for循环
                 if ( gStation->Captures->get_Item(i)->Value->Trim()->Equals(strEnd->Trim()) )
                    break;
            }
        }
        // for循环已退出,比较iNewTime和iOldTime,并记录较小的路线索引
        if ( iNewTime < iOldTime )
        {
             iOldTime = iNewTime;
             nFastest = i;
        }
    }
    // 现在,nFastest代表了最短路线的索引,iOldTime则代表所需的最短时间
}

终于找到了最短路线和最短时间,工作完成了!运行运行…………期待…………期待…………焦虑…………疑惑…………不对啊,怎么没有结果?

哦!忘了最后一步,还需要把结果传出……

第四步,也是最后一步,传出结果

结果定义为这样的形式——'车ID,需时',生成结果就很简单了:
{
    // 定义一个255字节的数组,较容易处理
    CHAR pcResult[255];
    sprintf(pcResult, "火车ID为%d,需时%d小时", piTrainID[nFastest], iOldTime);
}

怎样把pcResult送出去?这就用到了srv.h中定义的srv_paramsetoutput方法,其格式如下:

int srv_paramsetoutput (
SRV_PROC * srvproc,
int n,
BYTE * pbData,
ULONG cbLen,
BOOL fNull );

其中,n代表是第几个参数,pbData代表参数数据,cbLen代表参数长度(字节),
若fNull设置为TRUE,则此出参将被强制设为NULL.

大家肯定注意到,pbData是一个BYTE指针,于是我们还要把pcResult转换为BYTE*,这个很简单,直接显式转换即可。

设置出参的代码如下:
{
     // 得到结果的实际长度(字节数)
     int nResultLen = strlen(pcResult);
     // 传出参数
     srv_paramsetoutput(srvproc, 3, (PBYTE)pcResult, nResultLen, FALSE);
 
     // 所有的工作完成后,发出senddone信息
     srv_senddone(pSrvProc, (SRV_DONE_COUNT | SRV_DONE_MORE), 0, 1);
    }

现在工作似乎已经完成了,快拿去运行吧,嘿嘿,多运行几次……再运行几次……陶醉一下……又运行几次……然后……重启计算机,继续往下看。

上面的代码中,我们没有完全释放所分配的资源,所以每次运行都会造成内存泄漏。我们必须在代码的最后加上释放内存空间的语句,比如ppcStations, piTrainID等。

大功告成,第一个问题得到了解决。

本来还想写第二个问题的,可第二个问题和第一个问题的区别只在于第三步,即如何计算最短路线的算法上,这个,我相信我现在用的方法一定不是最好的,就不拿出来献丑了。

原来写篇文章是这么累的……

.net 用EF开发项目,你不得不再引用的几个第三方扩展库

EntityFramework 非常好用,结构优美.. 但是美中有不足.  1.对动态查询条件支持的不是很好  2.批量操作支持的不是很好.  下面就是几个第三方库,对EntityFramework ...
  • mysouling
  • mysouling
  • 2016年05月10日 16:53
  • 1673

51单片机程序存储器扩展

在单片机的扩展中,要分别考虑程序存储器及数据存储器的扩展。 存储器是单片机系统中使用最多的外扩芯片,对80C51系列单片机而言,由于程序存储器与数据存储器的空间在物理空间上的各自独立性,使得两者...
  • i792439187
  • i792439187
  • 2013年04月19日 17:39
  • 1950

oracle存储过程实例

认识存储过程和函数 存储过程和函数也是一种PL/SQL块,是存入数据库的PL/SQL块。但存储过程和函数不同于已经介绍过的PL/SQL程序,我们通常把PL/SQL程序称为无名块,而存储过程和函数是...
  • G15738290530
  • G15738290530
  • 2016年07月05日 12:19
  • 5029

VS2010轻松学习C#-从零到深入-天轰穿.NET4趣味编程视频教程_第30讲:综合应用&amp;存储过程

  • 2017年07月26日 02:08
  • 63.09MB
  • 下载

VS.NET 2003 + MySQL Server 5.1 编写第一个MySQL控制台程序 (三)

继续向下学。。。 接下来,我们要学习编写一个错误处理函数。咱们的代码中都是通过检查调用的 MySQL 库函数的返回值来判断函数调用是否成功。返回指针的函数调用,判断返回值是否是一个空指针(NULL...
  • gmbros
  • gmbros
  • 2017年04月20日 15:25
  • 326

使用存储过程查询时间段数据 编程小实例,C++.net源代码编写

  • 2010年03月18日 20:53
  • 2.33MB
  • 下载

VS.NET 2003 + MySQL Server 5.1 编写第一个MySQL控制台程序 (一)

首先,我们必须下载 MySQL Server 5.1 。是那种完整安装包,安装后在 MySQL Server 的安装目录里要有 include 和 lib\debug 文件夹,其中必须包含开发 MyS...
  • gmbros
  • gmbros
  • 2017年02月23日 16:47
  • 410

VS.NET 2003 + MySQL Server 5.1 编写第一个MySQL控制台程序 (二)

连接/断开MySQL数据库服务器 好,我们接着上一篇继续编写第一个 MySQL 数据库应用程序。接下来,我们要写代码连接到 MySQL 的数据库服务器。需要说明一下,我们通过一本书,叫《My...
  • gmbros
  • gmbros
  • 2017年03月06日 16:50
  • 432

再谈存储过程的编写经验和优化措施、与人为本:优秀ASP.NET程序员的修炼之路

再谈存储过程的编写经验和优化措施存储过程的编写经验和优化措施:  ◆1、开发人员如果用到其他库的Table或View,务必在当前库中建立View来实现跨库操作,最好不要直接使用“databse.dbo...
  • fengyupeng
  • fengyupeng
  • 2011年01月18日 12:36
  • 486

SQL Server中的CLR编程——用.NET为SQL Server编写存储过程和函数

很早就知道可以用.NET为SQL Server2005及以上版本编写存储过程、触发器和存储过程的,不过之前开发的系统要么因为历史原因用的是SQL2000要么根本用不着在SQL Server中启用CLR...
  • zhoufoxcn
  • zhoufoxcn
  • 2012年05月10日 01:30
  • 13649
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:用VS.Net编写扩展存储过程(三,完)
举报原因:
原因补充:

(最多只允许输入30个字)