精华内容
下载资源
问答
  • lazarus 多语言编写demo.

    2012-02-26 07:44:40
    lazarus 多国语言界面demo, 这是编写源码。内有2种方法实现。
  • 这是我在使用Qt语言编写上位机时,为将单片机sd卡保存的运行数据转换成动态波形过程中,借鉴的简单易懂Demo
  • 使用Python语言编写简单的HTML5语法解析器 摘要:通过使用Python语言编写一个简单的HTML5语法解析器作为例子,探讨了在设计手写的递归下降语法解析器程序时需要注意的一些事项。   关键字:PythonHTML5语法解析器 ...

    摘要:通过使用Python语言编写一个简单的HTML5语法解析器作为例子,探讨了在设计手写的递归下降语法解析器程序时需要注意的一些事项。

     

    关键字:Python HTML5 语法解析器 正则表达式 递归下降 编译器技术

     


    1     问题

     

        如何编写一个语法解析器(Parser)呢?在C/C++语言领域,我们有lex & yacc(文法解析器和语法解析器的生成器)及其GNU移植版本flex & bison,yacc是根据大牛Knuth的LALR文法设计的,自底向上进行解析;在Java语言领域,我们有ANTLR,这是是一个基于LL(n)文法的解析器生成器(递归下降,向前看n个Token消解冲突)。通过这些工具,我们只要写出要解析语言的文法、语法定义,就可以让它们帮我们生成对应的解析器,这通常称为编译器的前端(后端指的是代码生成和指令优化),此外,还有称为‘解析器组合子’的半自动工具可用于前端语法分析。

      抛开这些工具和第三方库,现在的问题是:给你一个HTML5文件,如何仅使用编程语言本身的库,编写一个语法解析器程序呢?

      首先,一个语法解析器需要文法扫描器(Lexer)提供Token序列的输入。而文法扫描器的每个Token通常使用正则表达式来定义,对这里的任务,我们可不想自己实现一套正则表达式引擎(重复造轮子),反之,将依赖本身就提供了正则表达式的编程语言来完成Lexer的编写。

      那么,哪些编程语言内置正则表达式引擎呢?C没有,C++ 11之前也没有(可以使用Boost),C++ 11有,Java、C#、Python、Ruby、PHP、Perl则都提供了支持。这里我选择Python,原因无它,相比其他脚本语言,我个人更熟悉Python。而编译型语言处理字符串则不如脚本语言灵活。虽然无类型的Python不像C++/C#/Java那样,有一个好的IDE及调试环境,但记住一点:开发原型优先选择灵活的脚本语言,待技术实现可靠性得到验证后,可以再移植到编译型语言以进一步提高性能。这里值得一说的是,上述语言均支持OOP。我想强调的是,好的OO设计风范(主要涉及类层次结构的定义和核心流程的参数传递)对于编写可读性佳、可维护性高的代码无疑是十分重要的。


    2     程序设计思路

     

    2.1 简化版HTML5语法定义

     

        首先,给出一段要解析的HTML文件内容如下:

     

    <!DOCTYPE html>
    <html><!-- this is comment--><head><title>Test</title></head>
    <bodystyle=”background:#000;”><div>Text Content</div></body></html>

     

        根据上面的简单用例,我们的程序设计目标限定如下:它能够处理文档类型声明(DocType)、元素(Element)、元素属性(Attr)、Html注释(Comment)和普通文本(Text),暂不支持内嵌JavaScript 的<script>元素和内嵌CSS的<style>元素。也暂不考虑Unicode的解析,假设输入文件是纯英文ASCII编码的。

        在此约束条件下,首先来定义此简化版的HTML5语法定义:

    '''
    Document = DocType Node*
    DocType = "<!DOCTYPE" TypeName">"
    Node = Comment | Element | Text
    Comment = "<!--" ...any text without'-->'... "-->"
    Element = "<" TagName Attrs"/"? ">"
        |"<" TagName Attrs ">" Node* "</" TagName">"
    Text = ...any characters until '<'
    TagName = [a-zA-Z][a-zA-Z0-9]*
    Attrs = <empty>
        | AttrAttrs
    Attr = AttrName ( "=" AttrValue)? #No WShere
    AttrName = [a-zA-Z_][a-zA-Z0-9_\-]*
    AttrValue = '"' [^"]* '"'
    '''


        注意,这里没有写出严格的定义。在编写demo程序的过程中,重要的是保持思路清晰,但不需要把细节问题一步详细到位,只要保证细枝末节的问题可以随时扩展修正即可。

     

    2.2简化版DOM数据结构定义

     

        我曾经做过Java XML/DOM解析,也维护过浏览器内核DOM模块的代码,但对于我们的demo开发而言,没必要写一个完善的DOM类层次结构定义。尽管如此,保持简明扼要还是很重要的。    

          DOM数据结构的Python代码如下:(Python没有枚举类型,直接使用字符串代替)

     

    class Node:
        def __init__(self, pos, type):
            self.type = type
            self.pos = pos #startposition if ref html string
            self.parent = None
    class DocType(Node):
        def __init__(self, pos,docType):
            Node.__init__(self, pos,"DocType")
            self.value = docType
    class Comment(Node):
        def __init__(self, pos,comment):
            Node.__init__(self, pos,"Comment")
            self.value = comment
    class Element(Node):
        def __init__(self, pos,tagName):
            Node.__init__(self, pos,"Element")
            self.tagName = tagName
            self.attrs = []
            self.hasEndSlashMark =False #True, if <xxx .... />
            self.childrenNodes = []
        def addAttr(self, attr):
            attr.parent = self
            self.attrs.append(attr)
        def addChild(self, node):
            node.parent = self
            self. childrenNodes.append(node)
    class Text(Node):
        def __init__(self, pos, text):
            Node.__init__(self, pos,"Text")
            self.text = text
    class Attr(Node):
        def __init__(self, pos, name,value=None):
            Node.__init__(self, pos,"Attr")
            self.name = name
            self.value = value
    class Document(Node):
        def __init__(self, pos=0):
            Node.__init__(self, pos,"Document")
            self.docType = None
            self.rootElement = None


    这里Node是所有DOM树节点的基类,DocType、Comment、Element、Attr、Text、Document都是Node的子类。

      

    2.3 TDD:main程序入口

     

         前面说到,我们使用的是Python语言,让我们追随直觉,快速写下main程序的启动代码吧:

     

    if __name__=="__main__":
        print "Parsing %s" %(sys.argv[1])
        html_string =open(sys.argv[1]).read()
        P = Parser(Lexer(html_string))
        P.parse()
        D = P.getDocument()

     

         从上面的代码可以看到,我们需要实现2个类:Lexer和Parser,一个核心方法parse,解析的结果以Document对象返回。

     

    2.4 Lexer设计与实现

     

       编译原理里提到的文法解析通常基于正则文法(有限自动机理论),然而,实际世界中使用的正则表达式引擎则支持更高级的特性,如字符类、命名捕获、分组捕获、后向引用等。我们这里不关心如何实现一个基于正则文法的有限自动机,而只是使用正则表达式引擎实现Lexer。

       由于Lexer的输入为字符流,输出为Token序列,那么将此接口命名为nextToken。

       首先,它应该带一个模式(pattern)字符串参数,代表我们期望从字符流中扫描的模式,同时,Lexer对象维护一个状态pos,代表当前扫描的起始位置。

       其次,我们给nextToken加上额外的2个参数(注意:这里的API设计仅仅从权考虑,在正式的产品开发中,可能需要根据实际的需求做出改动):

    1. skipLeadingWS  代表在扫描调用者提供的下一个模式之前,是否先忽略前导空白字符串
    2. groupExtract      有的时候,扫描模式有匹配之后,我们只想提取其中的部分返回,这里根据正则表达式引擎的一般后向引用定义,0代表整个模式,而1代表第1个左圆括号对应的部分。

       OK,Lexer的设计部分大抵差不多了,可以开始写代码了:

     

    class Lexer:
        WS =re.compile("\s{1,}", re.MULTILINE|re.DOTALL)
        def nextToken(self, pattern, skipLeadingWS=True, groupExtract=0):
            if skipLeadingWS:
                m= Lexer.WS.match(self.html_string, self.pos)
                  ifm:
                   ws_length = len(m.group(0))
                      self.pos = self.pos + ws_length #Python没有+=操作
             p = re.compile(pattern,re.MULTILINE|re.DOTALL)
             m = p.match(self.html_string, self.pos)
             if m:
                  m_length= len(m.group(0))
                  self.pos= self.pos + m_length
                  return(m.pos, m.group(groupExtract)) #返回值的设计:(pattern的起始位置,token字符串)
             else:
                  return(-1, None)


    2.4.1验证你不了解的API!

         请再看一下上面的函数Lexer.nextToken的API设计与实现。这里的核心要点就是用到了Python 正则表达式库的API PatternObject.match方法。

         这里的要点是:对于你不了解的API(所谓的不了解,就是以前你没怎么用过),一定要仔细阅读该API的帮助手册,最好是编写简单的单元测试case来验证它是否能够满足你的需求。

         事实上,我一开始使用的是PatternObject.search,而不是match方法,但是我发现了问题:

     

         p=re.compile(r”^[a-z]{1,}”)
         p.search(“123abc”, 3) #不匹配,虽然模式使用了^,并期望与search方法的第一个起始位置参数共同作用

     

         Python帮助手册里对此API行为居然做出了明确规定,但我不明白API这样设计有何合理性——相当地违背直觉嘛。

         山穷水尽疑无路,柳暗花明又一村。当感觉有点绝望的时候(实际上也没那么夸张,可以用一个方法绕过这个缺陷并仍然使用search API来完成工作,就是会有性能缺陷),再看看match方法:… 嗯?这不就是我想要的API嘛:

     

         p=re.compile(r”[a-z]{1,}”)
         m = p.match(“abc456”) #匹配
         m = p.match(“123abc456”) #不匹配
         m = p.match(“123abc456”, 3) #匹配

     

         糟糕的是,match API也有一个问题:m.endpos理所当然的应当返回匹配模式在源字符串中的结束位置,但它实际上返回的却是整个源字符串的结束位置(也就是它的长度),还好,这个问题可以用len(m.group(0))巧妙地绕过且不影响性能。

      结论:API使用内藏陷阱,请谨慎使用,使用之前先做好单元测试功能验证。

     

    2.5 Parser设计与实现

     

        让我们先写出parse入口函数:

     

        def parse(self):
            ifnot self._parseDocType(self.document):
                  pass#Ignore
            try:
                return self._parseElement(self.document) #MUST start with <html>?
           except ParseFinishException, e:
                return True

     

        从这个顶层的parse()来看,一个HTML文档由一个开始的DocType节点和一个根<html>元素组成。parse()内部调用了2个方法:_ parseDocType和_ parseElement。注意,后2个函数名前面加了下划线,代表私有函数,不提供外部使用(脚本语言通常没有C++的名字可见性概念,通常使用命名规范来达到同样的目的)。

        ParseFinishException的用法请参考2.7节说明。

     

    2.6 验证Lexer.nextToken:实现_parseDocType()

     

        DocType节点的语法声明参考2.1,下面是_parseDocType()的实现:

     

        def_parseDocType(self, ctx):
            print"_parseDocType: enter"
           assert ctx.type=="Document"
            pos,docType = self.lexer.nextToken(r"<!DOCTYPE\s{1,}([a-z]+)>", True, 1)
            if docType:
               ctx.docType = DocType(pos, docType)
               return True
            else:
               print "No DOCTYPE node found."
               return False

     

        _parseDocType的代码完美演示了Lexer.nextToken API的用法,其形参ctx代表当前的上下文节点,比如说,解析DocType时,其ctx就是根Document对象。

        这里_parseDocType使用的扫描模式可以提取出像“<!DOCTYPE html>”中的“html”。不过,也许这里可以放松条件以匹配HTML4的语法。

     

    2.7 实现_parseComment()时的代码健壮性考虑

     

        前面实现_parseDocType()时只使用了1次nextToken扫描,这里实现_parseComment()将考虑使得代码更健壮一点。怎么讲呢?HTML注释节点以“<!--”开始,以“-->”结束,中间是任意的字符(不包含连续的-->)。

        如果我们的扫描模式写成:

    p=re.compile(r"<!--(.*)-->")

        则由于正则表达式的默认贪心模式匹配,它将匹配字符串“<!—abc-->123-->”中的“abc-->123”,为此,可改用非贪心模式匹配:

           p=re.compile(r"<!--(.*?)-->")

        这样就行了吗?还不行。当html字符串中只有开始的<!--,没有结束的-->时,将视为一直到文档结束都是注释。为实现这个规约,需要补充进行一次扫描:

        如果p=re.compile(r"<!--(.*?)-->")扫描失败,就用p=re.compile(r"<!--(.*?)$")重新扫描一次。

     

    2.8难点:递归的_parseElement()

     

        元素节点的解析存在许多难点,比如说,需要在这里解析元素属性、需要递归地解析可能的子节点。让我们尝试着写写看吧:

     

    def _parseElement(self, ctx):
            print"_parseElement: enter"
            pos,tagName = self.lexer.nextToken(r"<([a-zA-Z][a-zA-Z0-9]*)", True, 1)
            ifnot tagName:
               print "_parseElement: tagName not found."
               return False
           element = Element(pos, tagName)

     

        这里的容错处理逻辑是:至少当匹配了’<’及有效的tagName后,才认为找到了一个元素节点,这时可以创建一个element对象,但这时我们还不知道接下来的解析是否会成功,所以暂时不addChild到ctx父节点上。

        接下来是属性解析:

          

            if not self._parseAttrs(element):
                  return False

     

        如果属性解析失败,则_ parseElement也随之失败,否则将element添加到ctx上:

     

            if ctx.type=="Document":
               ctx.rootElement = element
            else:
                ctx.addChild( element )

     

    _parseAttrs函数的一个副作用是设置元素是否直接以‘/>’结束,如果是这样,则该元素没有进一步的子节点;否则需要进一步递归处理子节点的解析。

     

            if element.hasEndSlashMark:
               return True
             #now try to recursive descendant parse childnodes:       
            while True:
                pos, endTagName =self.lexer.nextToken(r"</([a-zA-Z][a-zA-Z0-9]*)>", True, 1)
                if endTagName:
                    if endTagName==tagName:
                       break

     

    由于子节点的数目定义在语法规则中是*(0个或多个),则我们需要向前看,即查找形如</xxx>这样的结束标签。如果匹配到endTagName,并且等于当前元素的tagName,则递归解析可以结束;

    否则的话,需要向上抛出一个定制的异常,请考虑下面的case:

     

    <div>
           <span id=”x” name=”a”>
                  <img src=”a.jpg”>
                         </div>

     

    _parseElement在解析img元素时遇到了</div>结束标签,然后这个结束标签与它自己并不匹配,于是,它需要通知上上层的处于解析<div>位置的_parseElement:

     

        else:
                       find_match = False
                       n = ctx.parent
                       while n:
                            if n.type=="Element" and n.tagName==endTagName:
                                find_match = True
                                break
                            n = n.parent
                       if find_match:
                            raise FindElementEndTagException(endTagName)
                       else:
                            pass #Ignore this unknown </xxx>!


     

    注意,抛出FindElementEndTagException异常的是_parseElement,接受此异常的同样是_parseElement,只不过两者处于Call Stack的不同位置而已。

     

               pos_before= self.lexer.pos
               try:
                   self._parseNode(element)
               except FindElementEndTagException, e:
                   if e.endTagName==tagName:
                       return True
                   else:
                        raise e
               pos_after = self.lexer.pos
               if pos_before==pos_after:
                      if self.lexer.reachEnd():
                       raise ParseFinishException()
                   else:
                       print"_parseElement: something error happened!"
                       raise ParseError()
           return True

     

        由于FindElementEndTagException异常由Call Stack的最底层向上抛出,tagName与endTagName第一个匹配的_parseElement将捕获到它。

        此外,_parseElement 递归调用_parseNode的时候,我们要一对变量(pos_before和pos_after)记住Lexer的前后状态,如果Lexer状态没有发生变化(pos_before==pos_after),说明_parseNode失败。

        这对应什么情况呢?因为Node对象包含3种:Comment、Element、Text,不匹配其中任意一种的话,_parseNode就会失败,比如说,一个单独的‘<’。

        目前demo程序以抛出解析异常的方法结束,至于怎么容错处理(忽略,或仍然当作Text节点处理),留待读者自行考虑。

        如此,最难的_parseElement()函数就结束了。

     

    3     测试

     

        HTML解析之后是一个DOM树,其根节点为Document对象,怎么验证解析得对不对呢?

        把这个DOM树重新序列化为html字符串,与原始输入进行比较即可。考虑到HTML5的容错处理,序列化后的结果不能保证与源输入结构上一致。

        DOM树的序列化代码从略,它其实就是一个针对子节点的递归调用,这里代码从略(完整脚本代码请参考附件)。

        OK,现在HTML5语法解析器基本上已经编写完成了。

     

    4     结语

     

        对于递归下降的语法解析器而言,重要的就是要做好“向前看”的工作,自上而下的递归解析相比自底向上的解析,实际性能并没有什么大的损失,但其代码结构的可理解程度就要高不少了。

        感谢你的耐心阅读,欢迎来信交流心得体会。

    展开全文
  • C 语言开发Demo

    千次阅读 2017-03-13 20:19:21
    项目:用C语言写贪吃蛇,五子棋,俄罗斯方块以及链表通讯录??  C++经典小程序,C++小程序。  通过阅读书籍来帮助掌握某种语言的基本知识。随后又通过阅读书籍来全面探索这门语言的灵活性。... C, C编写记事...

      项目:用C语言写贪吃蛇,五子棋,俄罗斯方块以及链表通讯录??
      C++经典小程序,C++小程序。
      通过阅读书籍来帮助掌握某种语言的基本知识。随后又通过阅读书籍来全面探索这门语言的灵活性。

    > IDE C
    IDE C-Free 5 免安装注册版- http://download.csdn.net/download/u4110122855/5265901

    > C, C编写记事本和界面
    用C编写的记事本- http://blog.csdn.net/yueguanghaidao/article/details/6949249
    C语言API编写窗体界面和按钮- http://blog.csdn.net/eastmount/article/details/26902307

    通过这九本开源好书学习 C 语言- https://linux.cn/article-8251-1.html
    《 C 语言 The C Book》《风格的要素—— C 语言版C Elements of Style 》《编写你自己的 Lisp 语言Build Your Own Lisp》《GNU C 语言参考手册The GNU C Reference Manual》《GNU C 语言编程教程The GNU C Programming Tutorial》《C 语言基础Essential C》《Beej 的 C 语言教程Beej’s Guide to C Programming》《现代 C 语言Modern C》《GCC 简介An Introduction to GCC》

    C语言怪异代码大赛历届作品大全(1984~1996,1998,2000,2001,2004~2006,2011,2012)- http://download.csdn.net/download/zhao4zhong1/5538847
    C语言教程(附带C程序100例)- http://download.csdn.net/download/zhao4zhong1/8830817
    c语言中基本数据类型printf()对应格式- http://www.cnblogs.com/xiaobaizhu/articles/2783868.html
    C语言各种数据类型取值范围- http://blog.csdn.net/abaloon/article/details/8173552
    C语言文件读写基本操作DEMO- http://blog.csdn.net/hxpjava1/article/details/54375008
    C语言的小DEMO - http://www.cnblogs.com/likebeta/archive/2012/06/16/2551780.html
    最值得阅读学习的 10 个 C 语言开源项目代码- http://blog.csdn.net/manoel/article/details/41309261
    C 语言进阶有哪些优秀的代码可以阅读?- http://top.jobbole.com/14814/
    深入理解 C 指针阅读笔记- http://blog.csdn.net/DLUTBruceZhang/article/category/2717877

    > C++ , C++操作数据库
    C++ API方式连接mysql数据库实现增删改查- http://blog.csdn.net/android_lover2014/article/details/52760717
    C++通过mysql的c api和通过mysql的Connector C++ 1.1.3操作mysql的两种方式.
    设计模式(简单工厂和工厂方法C++版)Demo程序- http://download.csdn.net/detail/justkong/4639280
    C/C++,ios的博客 - http://blog.csdn.net/shenjie12345678?viewmode=contents
    C++ - http://blog.csdn.net/u012515223/article/category/1709505
    Socket通信原理和实践(VC++)- http://blog.csdn.net/dlutbrucezhang/article/details/8577810
    ipconfig的C(VC++)语言实现- http://blog.csdn.net/dlutbrucezhang/article/details/8577712

    > 简单记事本
    (Java ios Android wp C)简易的记事本- http://download.csdn.net/download/earl_yuan/5293013
    手把手教你用VC++做简单记事本- http://download.csdn.net/detail/lkmcom/4722907
    QT实现简单记事本- http://download.csdn.net/download/qq_32961637/9507898
    MFC记事本开发- http://download.csdn.net/detail/djb100316878/8311511

    > notepad++
    notepad++源代码- http://download.csdn.net/download/zxr1001/276161 , http://sourceforge.net/projects/notepad-plus/files/
    Notepad++源码:https://github.com/notepad-plus-plus/notepad-plus-plus/releases/tag/v6.7.9.2
    scintilla的官网- http://www.scintilla.org/

    > 简单的项目
    一些简单的项目的源代码- http://www.oschina.net/project/lang/21/c
    c++项目(详细简单适合初学者)- http://download.csdn.net/detail/u011706736/7998273
    适合C++/C新手的项目列表- http://www.jianshu.com/p/ba9e07857aca
    C++开源代码项目汇总- http://blog.csdn.net/wenrenhua08/article/details/40040903
    C语言可以开发哪些项目?- http://www.jianshu.com/p/91d671836b1d#
    用C语言实现Ping命令- http://blog.csdn.net/qq_33724710/article/details/51576444 , https://github.com/Fireplusplus/Project/tree/master/Ping

    > QT与MFC的区别???

    展开全文
  • Go语言是谷歌2009年推出的一种全新的编程语言,可以在不损失应用程序性能的情况下降低代码的复杂性。Google对Go寄予厚望。其设计是让软件充分发挥多核心处理器同步多工的优点,并可解决面向对象程序设计的麻烦。它...

    Go语言是谷歌2009年推出的一种全新的编程语言,可以在不损失应用程序性能的情况下降低代码的复杂性。

    Google对Go寄予厚望。其设计是让软件充分发挥多核心处理器同步多工的优点,并可解决面向对象程序设计的麻烦。它具有现代的程序语言特色,如垃圾回收,帮助程序设计师处理琐碎但重要的内存管理问题。Go的速度也非常快,几乎和C或C++程序一样快,且能够快速制作程序。

    Go语言有以下特色:

    • 简洁 快速 安全
    • 并行 有趣 开源,
    • 内存管理,数组安全,编译迅速

    本文将介绍如何在linux系统(以ubuntu16.04 x64为例)下搭建go环境以及如何编写第一个demo程序。

    搭建go环境

    第一步 下载go1.10.2.linux-amd64.tar.gz 软件包

    
    
    1. curl -O https://storage.googleapis.com/golang/go1.10.2.linux-amd64.tar.gz

    第二步 解压软件包到/usr/local目录下(如果目录下go目录已存在则先删除go目录)

    
    
    1. tar -C /usr/local -xzf go1.10.2.linux-amd64.tar.gz

    第三步 将go所在的二进制目录加入到PACH路径中,并使其生效

    
    
    1. echo "export PATH=$PATH:/usr/local/go/bin" >> ~/.bashrc
    2. source ~/.bashrc

    第四步 查看go版本

    
    
    1. root@cc-virtual-machine:~# go version
    2. go version go1.10.2 linux/amd64

    恭喜您,go环境已经安装成功!!!

    编写第一个demo程序

    编写代码

    新建demo.go文件并输入下面go代码

    
    
    1. package main
    2. import . "fmt"
    3. func main() {
    4. var value int = 100
    5. Println(value)
    6. Println("hello cc")
    7. }

    运行结果

    
    
    1. root@cc-virtual-machine:/opt/go_study/ex# go run hello.go
    2. 100
    3. hello cc
    • 第1行 包申明,表示该程序属于哪个包
    • 第3行 其他包引入,表示使用fmt包中的函数(输入输出等); 符号 . 表示在引用fmt的函数时,可省略fmt。例如:使用Println() 代替 fmt.Println()
    • 第5行 主函数,每个项目必须有主函数;如果没有init函数,则它是第一个执行的。main函数的后面紧跟大括号 “{” (不能把“{”换行书写)
    • 第6行 变量定义, 名称为value, 类型为整型int,值为100
    • 第8-9行 输出函数, Println 大写字母开头(引入其他包函数以大写字母开头),输出整数和字符串(默认输出后换行);也可以使用Printf()函数进行出出。例如 Printf(“%s, %d\n”, “hello cc”, value)

    温馨提示

    • 一行代表结束,不用写分号“;”。如果在一行中写多条语句,则语句之间需要加分号(不推荐这样使用)
    • 变量命名由字母、数字和下划线组成,首字母不能是数字;也不能是关键字和预定字符(25个关键字和36个预定义符,后续章节描述)
    • 变量声明、定义和初始化中间用空格隔开,可保持程序美观、可读性强

    附录

    25 个关键字或保留字

    36 个预定义标识符

    版权声明:B链网原创,严禁修改。转载请注明作者和原文链接
    http://www.360bchain.com/article/140.html

    展开全文
  • VC编写Demo Scene的一些可能技巧

    千次阅读 2008-03-15 02:28:00
    本来也不是专门为了写这篇文章,只不过觉得已这样的形式发表比较合适。... 注意:为了最佳的阅读效果,请访问:http://www.csksoft.net/blog/post/demo_scene_tip1.html转载注意:请注明原文出处: http://www.cs

    本来也不是专门为了写这篇文章,只不过觉得已这样的形式发表比较合适。同时好久都没有写过教程了,以往都以简单的发表作品或者通报一些事情为主。总的来说这篇文章还是有点参考价值的,希望能有所帮助。

     注意:

    为了最佳的阅读效果,请访问:http://www.csksoft.net/blog/post/demo_scene_tip1.html

    转载注意:

    请注明原文出处: http://www.csksoft.net/blog/post/demo_scene_tip1.html

     

    Demo Scene我就不想再介绍了,对他还不了解或者听说过但不明白原理和背景的可以参考我以前写的一些文章:

    Demo Scene:Principles,Techniques,and Tools (http://www.csksoft.net/blog/post/154.html)

    模块音乐(Mod)的制作和使用,Demo程序的主体之一 (http://www.csksoft.net/blog/post/intro2track_music.html)

     

    目前国内Demo Scene基本处在0起步的阶段,已经有了一些小团体打算去参加欧洲的比赛,但是还没有一定的规模。同时,对于制作这类程序网上也没有系统的资料。使得制作Demo Scene被看成一种高深的事情。

    下面我就说说目前在Windows平台下,使用最常用的开发工具(Visual C++)如何来制作一个符合64kb demo的程序框架和常用技巧。当然这只是一些次要手法,最核心的还是3d引擎、mod音乐的设计。因为那些资料很好找。所以就不再涉及。

     

    我将介绍下面几个方面的技术:

     

    1.如何产生体积最小的程序

    2.如何不使用C运行库开发程序

    3.如何实现高速GDI绘图

    4.对于NT5.0提供的LayeredWindow的使用--不规则窗体、窗口的AlphaBlend渲染、鼠标事件穿透

    5.如何将所有数据(代码、图片等)整合在一个C文件中

    6.其他的一些编译技巧

    7.一个完整的示例程序代码

    问题和需求

    Scene Demo中有一个项目为4kb-intro 或者 64kb-intro。 他要求Demo的程序体积必须小于或者正好等于4/64kb。而往往正是这类Demo程序在国内流传最广。因为大家都认为那么小的体积能播放长时间的高品质3d动画和音乐是不可思议得。甚至有人将45分钟的demo动画看成是avi视频,45分钟的音乐算作44KHz采样的wav。计算出将他们压缩到64kb完全是不可能的(见farbrausch的作品: the product 中的说明字幕)。当然这只是忽悠外行的吓人话。其实写过游戏引擎的人都知道那只是通过实时渲染的到的,而音乐本身就是体积在12kb左右的mod音乐序列(见我以前写过的文章)。

     

    目前很多机器都已安装最新版本的DirectX,而OpenGL是windows的默认库之一。这样Demo Scene设计者一般就不需要自己去编写基本的3d引擎。动画部分几个基本特效的代码不会超过30kb(这里假设开发者具有较高的设计素质),而一些复杂网格模型的纹理贴图即时采用bmp保存,也在100kb-300kb左右。加上mod音乐和其播放引擎。一个64kb DemoScence程序的原始体积一般应在600kb。而不是通过用等效为avi文件计算方法算出的几个G的天文数字。

     

    不过,问题就产生了,实际程序体积只有64kb。在这600kb->64kb还是有相当距离的。如何尽可能的去减少这部分文件大小,以及其中伴随的一些技巧就是本文所要讨论的。

     

    Some Tricks

    对于减小程序代码体积,自然对coding的技巧有一定要求。不过这不是问题所在。大家都知道使用VC编译产生的程序,即使就写了句printf("HelloWorld");,也会产生出100kb以上的代码。但是实际上这行语句的有效代码只是:

    WriteFile(StdOut,  "Hello World",12,NULL,NULL);

    WriteFile只是句API call, 实际上对应汇编大致为(这里只是说明性描述):

    push NULL

    push NULL

    push 12

    push offset "Hello World"

    push StdOut

    call WriteFile

    就这么几行汇编指令,大致也就几十字节的数据,但VC却占用了大量的体积。而那些多余的文件数据主要是下面这些内容:

    1. C运行库

    这应该是最主要的因素,程序中会编译近大部分的c函数库的代码信息。同时,在程序的开始执行点到逻辑上认为起始位置:main函数之间也填充了大块的C运行库代码完成初始化工作(初始化堆数据、获取命令行数据)。对于完成通常编程任务来说,这些代码是十分必要的。但是对于一个需要尽可能小体积,且具有足够经验的Demo Scene开发者来说,这些代码绝对是鸡肋。

    因此,减少程序体积的第一要务就是将C运行库完全从程序代码中剥离。不过要实现这个需要满足几个前提:

    a.程序不能使用C运行库,这是当然的。不过有人会问一些很常用的函数,诸如printf没有了该怎么办。回答只有是:自己使用等效的API实现。不过后文也会介绍一些办法

    b.尽量不要使用C++语言,原因是对于class的一些操作,诸如析构操作。new/delete运算符。这些本该是语言特性的语句,实际上在编译时会去调用c运行库来完成。

    c.不要使用tchar.h

    d.关闭VC后续版本提供的堆栈安全检查、异常处理等特性

    e.完全采用Win32 API

    对于很多人来说,要满足这些条件已经无法正常编写程序了。可能这也是Demo Scene的一个门槛。这里有一个变通办法,就是采用微软提供的精简版C运行库(LIBCTINY.LIB)或者使用ATL/WTL中的精简版C运行库。也能大幅减小体积。(实际上,在kernel.dll和ntdll.dll中也提供了C运行库的API接口)

    在符合上述条件后,就能大胆的将C运行库去除。具体办法就是在链接设置中取消默认库,或者用下面语句:

    #pragma comment(linker,  "/nodefaultlib")

    此时,不会有任何的C运行库被编译进程序,但是基本的windows API还是需要链接的。因此起码需要kernel32.lib。

    #pragma comment(lib,  "kernel32.lib")

    接下来可以按照需要添加相关的lib或者用LoadLibrary自行加载其他库。具体相信也不需要我废话了。

    不过需要注意的是几个特列。位于Winnt.h中有如下定义:

    # define RtlFillMemory(Destination,Length,Fill) memset((Destination),(Fill),(Length))
    # define RtlZeroMemory(Destination,Length) memset((Destination),0,(Length))

    相信MS这样做无非是考虑到运行效率和debug的需要。但是这样也给我们的工作造成了麻烦。如果在程序中直接或间接的使用了这2个函数(还有其它情况)的话,仍旧会被linker告知symbol _memset不存在。最直接的办法可以去修改这个winnt.h。但相信这是个十分愚蠢的做法。因此,推荐的办法是先undefine这些定义,再重新import。

    #undef memset
    #undef RtlFillMemory
    extern  "C" NTSYSAPI BOOL NTAPI
    RtlFillMemory ( VOID *Source1,DWORD Source2,BYTE Fill );
    # define memset(Destination,Fill,Length) RtlFillMemory((Destination),(Length),(Fill))

    其他类似的情况也可以这样处理,同时大家也可以开动智慧将部分C运行库用kernel32.dll或者ntdll.dll中现成的等价函数替换。这样对于暂时不习惯完全采用WINAPI编写的开发者来说能带来些便利。

    到目前为止,在代码逻辑上已经将C运行库从程序剥离了,但实际上编译还是不会通过。原因在于目前程序的真正起始位置还不是int main()或者int WinMain(...)这些。在执行这些逻辑上的开始位置前,还有不少的C运行库Wrapper。

    要将这个Wrapper去除,直接办法就是在link选项中修改入口地址到目前的main(WinMain)函数。或者用等效语句:

    #pragma comment(linker,  "/ENTRY:MyMain"

    此时,如果程序中有MyMain这个函数,那么他将真正作为程序的入口点(OEP)。不过要注意的是,作为OEP的函数不能带参数。因为传统的main函数或者WinMain中那些命令行信息的参数,都是由之前的C运行库Wrapper获取提供的,而直接从OEP启动时候,是没有参数提供给程序的。这样将造成堆栈的不平衡(但实际影响不是很大)。

    在做到这一步后,就可以尝试编译程序。以VC中创建的SDK的helloworld示例程序为例。经过目前的操作,产生的程序应该在2kb左右或更小(作者并未测试,只是估计值)。不过这里要注意的是,目前的程序有一个问题:进程无法结束。这是因为在原始的C运行库Wrapper中,执行完了WinMain等程序,会调用ExitProcess()终止进程。因此,在我们的新OEP程序的适当地方,加入此语句即可。

    对于原先WinMain等函数中参数的获取

    要获取诸如命令行参数或者HINSTANCE值,其实可以调用相应的API实现。对于命令行,可以调用函数:

    GetCommandLine( void); 

    对于HINSTANCE,可以调用

    GetModuleHandle(NULL);

    其他参数这里就不列举了,可以查阅相关文档或者反汇编原始的C Wrapper分析。

    2.段合并问题

    段(Section)是一个PE文件格式中的术语,具体可以参照MSDN中的一篇很完善的PE格式介绍文章"An In-Depth Look into the Win32 Portable Executable File Format"(http://msdn2.microsoft.com/en-us/magazine/cc301805.aspx),对于采用VC生成的程序文件,一般会带有下面3个段:

    .text

    .data

    .rdata

    一般而言,代码主体将被置于.text,而一些常量数据和资源文件会分配在其余2个段中。对于一个段来说,它的大小不是随意的。因此当程序代码或者资源较少时,就会在段内存在大量的冗余数据(一般都是0)。这样就白白占用了程序体积。解决办法就是把它们合并到一个或多个段中,压缩体积。

    采用如下语句合并2个段:

    #pragma comment(linker,  "/MERGE:.data=.text")

    也可以重新定义一个段,然后将所有段合并到新定义的段中

    #pragma comment(linker,  "/SECTION:tiny,")
    #pragma comment(linker,  "/MERGE:.data=tiny")
    #pragma comment(linker,  "/MERGE:.text=tiny")
    #pragma comment(linker,  "/MERGE:.rdata=tiny")

    这样也能在一定程度上减少体积。

    不过注意的是,这个办法适用范围并不大,尤其是对于含有资源的程序,基本很难成功运行。而且他对程序体积的减少贡献不是很大。

    3.将程序加壳

    如果仅仅利用上述手段,实际上只是得到了一个程序本应该具有的体积。但是就像前面提到的一个典型的demo程序也要占用600kb的空间。那么如果做近一步缩减呢?

    方法其实就是数据压缩。对于程序的代码本身、纹理贴图(假设为非压缩的bmp)、MOD音乐(其中音色为wav格式)。这些都含有很高的冗余信息,具有很大的压缩潜力。因此可以使用一些压缩加壳工具将程序加壳。就Demo程序而言,一般采用UPX和由farbrausch专门为demo程序设计的kkrunchy。同时也十分推荐国内由Dwing制作的加壳工具:WinUpack

     

    这几个工具均能从网络上免费获取,下面我做一点简要的点评

    对于UPX和WinUnpack

    采用的是LZ系列的压缩算法,而WinUnpack采用的一些改进措施,所以压缩效果比UPX好。同样采用LZ系列压缩算法的是人人皆知的WinZip和WinRAR工具。对于这2个压缩壳而言,因为定位于通用的程序加壳,所以具有较高的稳定性。而UPX历史悠久,因此基本上可以应对所有情况。

    对于kkrunchy

    他采用的是目前号称压缩比最高的PAQ7-9算法。

    参考:

    对于各类压缩算法的比较:http://www.maximumcompression.com/index.html

    PAQ算法在wikipedia的介绍:http://en.wikipedia.org/wiki/PAQ (大陆需要设置代理访问)

    PAQ算法的相关说明和论文:http://www.cs.fit.edu/~mmahoney/compression/

    PAQ虽然具有很高的压缩比,但是代价就是缓慢的解压缩和压缩速度。有人曾测试说解压缩1MB数据需要半分钟。但是因为很多demo程序体积都不大,而且自身加载后还需要近1分钟的预先计算过程,所以这还可以接受。

    不过kkrunchy还有一个缺陷,就是他会采用上面提到的段合并技术。因此很多带有资源的程序加壳后便无法执行。

    这里谈谈我本人的看法,我比较倾向使用前2者。第一是这些加壳工具在压缩比率上差别不是很大。不过UPX目前可以看成是开发的技术,WinUnpack为国人开发。而kkrunchy,他的开发者farbrausch自身就是Demo Scene的参赛者。

    不过这只是本人的看法。

    在经过了加壳处理后,原先600kb的程序很容易的缩小到了64kb左右。这样,又一个“神奇”的demo程序诞生了。(当然真正能让人称的上神奇的应该是本身的画面和音乐)。

     

    案例分析和其他一些技巧

    最近进入MSRA实习不久,任务还不是非常忙。MSRA在每天下午3点会供应水果,但是我时常忘记。因此就写了一个小程序,在特定时间会在屏幕上显示半透明的水果图片。同时为了不能打扰正常的工作操作,所以这个半透明的图片不会干扰正常的鼠标操作。下面就提供出这个程序代码,同时附上一些讲解。(这个程序是下班后在我笔记本上编写的,不涉及任何著作权问题)。

    程序的运行效果如图所示:

     

     

     

    为了不影响当前我进行的工作,在显示画面的时候,可以看到原有的操作不会打断。或者说这个窗口会将事件穿透。实际上就是利用了WinNT5.0以后提供的LayeredWindows特性。但很多人只是以为LayeredWindow只是简单的实现了窗口半透明。实际上他能通过对alpha的识别作高级的hittest和不规则窗口的创建。对于用LayeredWindow产生的不规则窗口效率要比传统的SetWindowRgn方法快出很多(我猜可能有20倍)。

    而目前一些桌面Widget也正是利用这个特性制作的。实际也没什么技术含量,感兴趣的可以参考msdn的一篇文章:

     http://msdn2.microsoft.com/en-us/library/ms997507.aspx

     

    本程序正是利用前面所说的将C运行库剔除的办法编写的,同时还有一个特别之处,就是仅采用了单个c文件实现了程序的所有功能。这样的好处之一就是可以使用前文提到的段合并,而不需要额外的资源文件。其实就是将图片文件保存为了数组信息作为代码附加在程序中。

     

    这里顺便说下GDI显示的效率问题,很多人都认为GDI效率低下,尤其是在对像素级别进行操作的时候。其实不然,最好的例子就是Flash播放器。目前Youtube类型的网站十分普及。那些flash实时渲染得视频、flash实现的3d引擎正是直接用GDI绘图实现的。关于如何高效进行GDI绘图的论述网上已经很多。在这里提这个一方面是因为这个程序中用到了同样办法:采用CreateDibSection实现创建出图像的内存数据缓冲,接下来就可以直接对内存操作实现像素的修改。而不必使用SetPixel这种缓慢的函数。同时,目前使用GDI直接渲染的Demo也是有的。

     

    下面就给出本程序的主要代码,完整的代码和程序在文章末尾提供:

     

    /*
    By CSK(Shikai Chen)
    http://www.csksoft.net
    */              
     
    #pragma comment(linker, "/nodefaultlib")
    #pragma comment(lib, "kernel32.lib")
    #pragma comment(lib, "user32.lib")
    #pragma comment(lib, "gdi32.lib")
    #pragma comment(linker, "/ENTRY:OEP")
     
     
    #pragma comment(linker, "/SECTION:tiny,")
    #pragma comment(linker, "/MERGE:.data=tiny")
    #pragma comment(linker, "/MERGE:.text=tiny")
    #pragma comment(linker, "/MERGE:.rdata=tiny")
     
    #define POINTER_64 __ptr64
     
    #define WINVER 0x0501      
    #define _WIN32_WINNT 0x0501
    #define _WIN32_WINDOWS 0x0410
     
     
    #define WIN32_LEAN_AND_MEAN      // Exclude rarely-used stuff from Windows headers
     
    #include <windows.h>
     
    #undef memset
    #undef RtlFillMemory
    extern "C" NTSYSAPI BOOL NTAPI
    RtlFillMemory ( VOID *Source1,DWORD Source2,BYTE Fill );
     
    #define memset RtlFillMemory
     
    #define IMG_SIZE_WIDTH 256
    #define IMG_SIZE_HEIGHT 256
     
    //SETTING AREA=======================================================
     
    #define FADE_DURATION 1
    #define FADE_FPS       50
     
    #define LOOP_WAITING_TIME   500
     
    #define LOOP_TIME      5
     
    #define MAX_ALPHA      150
     
    #define FOOD_COMING_HOUR 15
    //SETTING AREA=======================================================
     
    #define TOTAL_FRAMES   (FADE_DURATION*FADE_FPS)
     
    #define FADE_SLEEPTIME ((int)FADE_DURATION*1000/TOTAL_FRAMES)
     
    SIZE sz = {IMG_SIZE_WIDTH,IMG_SIZE_HEIGHT};
    POINT pt={0,0};
     
    extern BYTE raw_pic_data[];
     
    HWND hWnd;
     
    BOOL               InitInstance();
     
    BOOL               CreateMemBuffer();
    VOID               ReleaseMemBuffer();
     
    VOID               FadeInWindows();
    VOID               FadeOutWindows();
     
     
     
    HBITMAP g_mem_dib = NULL;
    HBITMAP g_mem_old = NULL;
    HDC      g_mem_dc = NULL;
    DWORD    *dib_section = NULL;
    BLENDFUNCTION g_std_blend;
     
    BOOL               CreateMemBuffer()
    {
        BITMAPINFOHEADER memBitmap;
        ZeroMemory(&memBitmap,sizeof(BITMAPINFOHEADER));
        memBitmap.biSize                  = sizeof (BITMAPINFOHEADER) ;
        memBitmap.biWidth                 = IMG_SIZE_WIDTH;
        memBitmap.biHeight                = IMG_SIZE_HEIGHT;
        memBitmap.biPlanes                = 1 ;
        memBitmap.biBitCount              = 32 ;
        memBitmap.biCompression           = BI_RGB ;
        memBitmap.biSizeImage             = 0 ;
        memBitmap.biXPelsPerMeter         = 0 ;
        memBitmap.biYPelsPerMeter         = 0 ;
        memBitmap.biClrUsed               = 0 ;
        memBitmap.biClrImportant          = 0 ;
     
        g_mem_dib = CreateDIBSection(NULL, (BITMAPINFO *) &memBitmap, 0, (void **)&dib_section, NULL, 0);
        g_mem_dc = CreateCompatibleDC(NULL);
        g_mem_old =(HBITMAP)SelectObject(g_mem_dc,g_mem_dib);
     
        for (int cp_pos=0;cp_pos<IMG_SIZE_WIDTH*IMG_SIZE_HEIGHT;cp_pos++){
            dib_section[cp_pos] = (raw_pic_data[cp_pos*4]) | ((DWORD)(raw_pic_data[cp_pos*4+1])<<8 ) | ((DWORD)(raw_pic_data[cp_pos*4+2]) <<16) |((DWORD)(raw_pic_data[cp_pos*4+3])<<24);
        }
        return TRUE;
    }
     
    VOID               ReleaseMemBuffer()
    {
        SelectObject(g_mem_dc,g_mem_old);
        DeleteObject(g_mem_dib);
        DeleteObject(g_mem_dc);
     
    }
     
    int OEP()
    {
        int state_id =0;
     
        SYSTEMTIME current_time;
     
     
        CreateMutex(NULL,TRUE,L"OnlyME");
        if   (GetLastError()==ERROR_ALREADY_EXISTS)
        {
            return FALSE;
        }
     
        for(;;)
        {
     
            GetLocalTime(&current_time);
          
            if (current_time.wHour == FOOD_COMING_HOUR )
            {
                if (state_id == 0){
                    state_id = 1;
                    CreateMemBuffer();
                    InitInstance ();
                    for (int loop_time=0; loop_time<LOOP_TIME ; loop_time++)
                    {
                        FadeInWindows();
                        Sleep(LOOP_WAITING_TIME);
                        FadeOutWindows();
                        Sleep(LOOP_WAITING_TIME);
                    }
                    DestroyWindow(hWnd);
                    ReleaseMemBuffer();
                }
     
            }else
            {
                state_id = 0;
            }
            Sleep(60000);
        }
     
        return TRUE;
    }
     
     
     
     
    BOOL InitInstance()
    {
     
        DWORD dw_old_style;
     
        RECT rt_desktop_size;
        GetWindowRect(GetDesktopWindow(),&rt_desktop_size);
     
     
        hWnd = CreateWindowEx(WS_EX_LAYERED | WS_EX_TRANSPARENT | WS_EX_TOPMOST |WS_EX_TOOLWINDOW
            ,L"#32770", L"", WS_POPUPWINDOW,
            (rt_desktop_size.right-IMG_SIZE_WIDTH)/2, (rt_desktop_size.bottom-IMG_SIZE_HEIGHT)/2,
            IMG_SIZE_WIDTH, IMG_SIZE_HEIGHT, NULL, NULL, NULL, NULL); 
     
        if (!hWnd)
        {
            return FALSE;
        }
     
        dw_old_style = GetWindowLong(hWnd,GWL_STYLE);
        dw_old_style &= ~(WS_SYSMENU | WS_THICKFRAME|WS_CAPTION|WS_BORDER);
        SetWindowLong(hWnd,GWL_STYLE,dw_old_style);
     
        dw_old_style = GetWindowLong(hWnd,GWL_EXSTYLE);
        dw_old_style &= ~(WS_EX_APPWINDOW);
        dw_old_style |= WS_EX_NOACTIVATE;
        SetWindowLong(hWnd,GWL_EXSTYLE,dw_old_style);
     
     
        g_std_blend.BlendOp = AC_SRC_OVER;
        g_std_blend.BlendFlags = 0;
     
        g_std_blend.AlphaFormat = AC_SRC_ALPHA;
     
        ShowWindow(hWnd, SW_SHOW);
     
        return TRUE;
    }
     
     
    VOID               FadeInWindows()
    {
     
     
        for (int i=0;i<TOTAL_FRAMES;i++){
            g_std_blend.SourceConstantAlpha = i*MAX_ALPHA/TOTAL_FRAMES;
            UpdateLayeredWindow(hWnd,NULL,NULL,&sz,g_mem_dc,&pt,0x0,&g_std_blend,ULW_ALPHA);
            Sleep(FADE_SLEEPTIME);
        }
     
    }
     
    VOID               FadeOutWindows()
    {
     
        for (int i=0;i<TOTAL_FRAMES;i++){
            g_std_blend.SourceConstantAlpha =MAX_ALPHA- i*MAX_ALPHA/TOTAL_FRAMES;
            UpdateLayeredWindow(hWnd,NULL,NULL,&sz,g_mem_dc,&pt,0x0,&g_std_blend,ULW_ALPHA);
            Sleep(FADE_SLEEPTIME);
        }
     
    }
    /
     
    BYTE raw_pic_data[262144] = { … 省略 … }
     
     

    如果对本程序感兴趣的话,也可以通过修改其中的一些Define定义作简单修改。当然如果你能基于他写个更加强大的提醒程序,也欢迎给我看看:-)

     

    本程序采用VS2005/VS2008在xp系统中成功运行。

     

    代码和程序:ftp://FTP_Visitor:visitor@ftp.csksoft.net/Public/Products/Misc/Fruit_Alert.rar

     

    By CSK

     
    展开全文
  • 根据OLAMI平台开发的日历Demo

    千次阅读 2017-06-02 09:07:03
    前言 编写语法 前言 在自然语言处理中,语义理解一直是个难题。...最近发现OLAMI语义平台提供了自然语言语义理解 API ,而且支持自定义语法,所以决定写一个日历的Demo来看一下效果。 编写语法
  • 自然语言处理现在是比较火的一个领域,也有了很多成熟的应用,像科大讯飞或是百度语音等等这些商业产品的一个优势之一就是比较稳定,适合用来做开发。下面就展示一个根据讯飞SDK文档做出的具有语音识别和语音合成...
  • RabbitMq使用demo

    千次阅读 2018-06-10 15:43:42
    1.为什么使用mq2.常见的mq有哪几种3.RabbitMq使用配置4.RabbitMq使用demo4.总结5.参考
  • 2、本次后台代码使用Java语言编写。由于微信支付要求传输方式必须采用https,而且实验室阿里云上已为Nginx服务器配置证书,因此只需配置一下/etc/nginx/sites-enabled/itest文件,在server参数下添加如下代码,让...
  • Qt在地图方面的研发。百度的地图分为多个开发,都是在线的(离线的需要自己提取,本篇解说在线地图)。百度地图JavaScript API支持HTTP和HTTPS,免费对外开放,可直接使用。接口使用无次数限制。
  • gpython是采用Go语言编写的Python 3.4解释器
  • C++编写的获取已打开的IE窗口源代码的DEMO 能得到所有打开的IE窗口的所有代码,与页面开发语言无关。 初始开发环境为VC6,后转换到VS2005,均可成功运行。
  • Qt开发技术:QtSVG介绍、使用Demo

    千次阅读 2020-04-15 22:55:45
    若该文为原创文章,未经允许不得转载 原博主博客地址:...本文章博客地址: 各位读者,知识无穷而人力有穷,要么改需求,要么找专业人士,要么自己研究 目录 前话 Demo ...Demo1:QSvgGene...
  • 纯Go语言编写的高并发、分布式、重量级爬虫软件,支持单机、服务端、客户端三种运行模式,拥有Web、GUI、命令行三种操作界面;规则简单灵活、批量任务并发、输出方式丰富(mysql/mongodb/csv/excel等)、有大量Demo...
  • 使用Java开源工作流jBPM开发一个简单的Demo

    万次阅读 热门讨论 2009-09-15 22:09:00
    jBPM是jboss旗下遵守LGPL许可的...由于相关资料还比较少,开发自己的一个demo还不是太容易,本文详细讲解如何做一个简单的demo程序。 我们从http://www.jboss.org/jbossjbpm/jbpm_downloads/下载jBPM,实际使用时发现4
  • SqlCE数据库编程Demo

    2011-02-28 13:19:58
    sqlce compact edition 3.5\ SqlCE数据库编程Demo 使用C#语言编写
  • 开发一个聊天室程序时,我们可以使用Socket、Remoting、WCF这些具有双向通信的协议或框架。而现在,我正要实现一个C#语言作为服务器端、Android作为客户端的聊天室。由于服务器端和客户端不是同一语言(C#和java),...
  • python语言编写的DLL注入工具

    千次阅读 2019-05-02 22:23:46
    dll_path = 'd://softdp//pyworkspace//wechat_demo//WechatDB.dll'.encode('ascii','ignore') dll_len = len(dll_path) print(dll_len) kernel32 = windll.kernel32 #第一步获取整个系统的进程快照 pids = ...
  • 使用TypeScript编写mocha单元...开发使用typescript作为主要开发语言,为了更好的理解,这里不采用任何集成框架。 单元测试工具使用mocha,使用chai作为断言工具 测试报告使用mochawesome作为测试报告生成工具 在这
  • CANoe Demo工程

    2018-09-21 17:01:07
    附件是基于CANoe软件CAPL语言编写demo脚本,模拟车载诊断仪发送诊断请求。CAPL软件里面包含报文、定时器的定义和使用简例。
  • 介绍如何使用C#语言调用VB6开发的COM组件以及OCX控件。并完整介绍了所有的技术细节。该技术主要用于使用C#语言开发用友U8系列产品的插件。
  • 一个简单的 C# 语言编写的 WIN32 程序

    千次阅读 2009-08-19 21:06:00
    刚k始学习 C / C++ / windows 编程时接触的经典 demo 程序, 今日拿来用 C# 玩耍了一下. 其实用 C# 编写 win32 窗口和控件的小型 wrapper 类库也无不可. Delphi 的 VCL 可以拿来做参照. 代码备忘: using System;...
  • 本节,我们学习如何在新版 VS 2017 中编写程序输出“C语言中文网”,程序代码如下: #include <stdio.h> int main() { puts("C语言中文网"); return 0; } 创建项目(Project) 在 VS 2017 下开发...
  • 基于Java语言实现了一个Mosquitto客户端demo,该demo是一个maven工程,demo运行的前提是mosquitto已经安装成功,安装步骤请参考http://blog.csdn.net/wangpf2011/article/details/78542018。 Mosquitto为MQTT的一个...
  • 小米手环iOS开发实战(二):开发Demo让你的手环振动起来上一节讲了CoreBluetooth的使用,理论知识很枯燥,那么现在先利用上一节讲的内容,做一个简易手环应用,实现连接/断开手环,查看手环UUID、查看电量信息,并让...
  • Kafka 使用Java实现数据的生产和消费demo

    万次阅读 多人点赞 2018-01-28 14:38:46
    在上一篇中讲述如何搭建kafka集群,本篇则讲述如何简单的使用 kafka 。不过在使用kafka的时候,还是应该简单的了解下kafka。 Kafka的介绍 Kafka是一种高吞吐量的分布式发布订阅消息系统,它可以处理消费者规模的...
  • 第一次使用Eclipse:编写简单的Java小程序

    千次阅读 多人点赞 2020-04-02 21:05:03
    通过前部分的学习,了解了Java的安装和配置,那么从现在开始,要开始自己着手编写Java程序,学习一门编程语言,学会编写的第一个程序一般都是写一个输出“hello World!”语句的小程序来表示自己开始学习这门语言。...
  • 使用Golang编写基于LDAP的Web应用程序
  • Android Studio NDK-Jni开发Demo

    千次阅读 2016-04-14 13:30:10
    JNI 是本地编程接口,它使得在 Java 虚拟机 (VM) 内部运行的 Java 代码能够与用其它编程语言(如 C、C++ 和汇编语言)编写的应用程序和库进行交互操作。由于Android的应用层的类都是以Java写的,这些Java类编译为Dex型...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 60,739
精华内容 24,295
关键字:

使用开发语言编写demo