明日看到园子里一篇关于 Url
重写的小说《获取ISAPI_Rewrite重写后的U瑞虎L》
U奇骏L-Rewrite
这项技能早已不是一项新技巧了,那些话题也早已被不少人谈论过频仍。搜索一下UOdysseyL-Rewrite能够找到很多U汉兰达L-Rewrite方面包车型客车小说和零部件,自个儿原先也再3触及过这几个东东,也来说说吗。
ScottGu 有1篇13分经典的 UHighlanderL-Rewrite Blog
Tip/Trick: Url Rewriting with
ASP.NET

http://weblogs.asp.net/scottgu/archive/2007/02/26/tip-trick-url-rewriting-with-asp-net.aspx

此处正是1般呈现全数的源码 首先是 cjson.h

摘引自ScottGu Blog 的原文

Why does URL mapping and rewriting matter?
The most common scenarios where developers want greater flexibility with
URLs are:
1) Handling cases where you want to restructure the pages within your
web application, and you want to ensure that people who have bookmarked
old URLs dont break when you move pages around. Url-rewriting enables
you to transparently forward requests to the new page location without
breaking browsers.
2) Improving the search relevancy of pages on your site with search
engines like Google, Yahoo and Live. Specifically, URL Rewriting can
often make it easier to embed common keywords into the URLs of the pages
on your sites, which can often increase the chance of someone clicking
your link. Moving from using querystring arguments to instead use fully
qualified URLs can also in some cases increase your priority in search
engine results. Using techniques that force referring links to use the
same case and URL entrypoint (for example: weblogs.asp.net/scottgu
instead of weblogs.asp.net/scottgu/default.aspx) can also avoid diluting
your pagerank across multiple URLs, and increase your search results.
In a world where search engines increasingly drive traffic to sites,
extracting any little improvement in your page ranking can yield very
good ROI to your business. Increasingly this is driving developers to
use URL-Rewriting and other SEO (search engine optimization) techniques
to optimize sites (note that SEO is a fast moving space, and the
recommendations for increasing your search relevancy evolve monthly).
For a list of some good search engine optimization suggestions, Id
recommend reading the SSW Rules to Better Google Rankings, as well as
MarketPositions article on how URLs can affect top search engine

引言

何以要实行UHighlanderL-Rewrite
斯科特Gu的blog中付出了多少个主要的原因:
一.确定保证WebApplication在展开结构调整,移动页面位置时,用户收藏的U哈弗L不会由此而变成死链。
2. SEO优化。

  错误是在所难免的, 欢迎吐槽, 下次分析完整的 cjson 使用进程,补充上cjson的 填充构造进度. 欢迎用在协调的项目中. 只怕学习一下. 

ranking.

 
第1点原因中所描述的地方,在Web站点改版中时时遇上。Web站点改版常常会调整部分页面包车型地铁岗位,QueryString中参数的组织等等。十分大概使原先用户在深藏夹中珍藏的链接成为死链。在那种境况下U福特ExplorerL-Rewrite像是软件框架结构技术中的一当中间层的概念,URL-Rewrite对外公开的UHighlanderL是被重写过的,这么些UWranglerL被用户收藏,不会变,当Web站点调整,内部Page的任务变动了,使得内部实际的U昂科威L地址也变更了,那时修改内部的重写规则,让原本对外祖父开的U景逸SUVL重写到新的里边U奥迪Q7L上。从而确定保障了对外UOdysseyL不变,其实对内已经形成了页面位置的调动。纵然U汉兰达L-Rewrite能够成功常备不懈死链的发出,可是大部分站点在改版或调整时,不会利用UWranglerL-Rewrite来防护死链的产生,一般会从来改动404The page cannot be found
页面,把40肆出错页面改成2个进一步友好的提醒页面,并且会在几秒钟之后跳转到网站首页。

  第3点原因是SEO了,如若您的站点是个里面OA EBMWX三P
C凯雷德M那种站点,只须要协调内部职员来做客。其实完全能够不用做SEO,因为这种站点根本不需求寻找引擎来收音和录音,也不需求旁人通过查找引擎找到这么些站点,所以那种站点完全未有须要开始展览SEO优化。假若您的站点是个商业贸易站点,音信站点,娱乐站点,越六个人访问越好的站点,SEO优化是极度关键,此时因此U瑞虎L-Rewrite举办SEO优化也就特别须求了。随着搜索引擎逐步变成人们追寻新闻,索取能源的首选工具,搜索引擎对二个站点的熏陶也就进一步大,上边是
zhangsichu.com 九-壹~9-拾 那段时光内的第三方来路数据总括。

统计 1

来路总结是透过记录httpheader中的Referer,来得知用户在浏览这几个页面以前所在的尤其页面。从而得出用户是通过丰盛页面到达那么些页面包车型客车。
在270个独立IP中,有200个IP是来源于搜索引擎的。也正是说,用户先通过寻找引擎的追寻结果,然后来到zhangsichu.com的用户有200个。占到了75.二%。大多数的人是透过搜寻来的。丰硕表达了SEO对站点的根本,在那种状态下,就务须做UHavalL-Rewrite进行SEO优化了。

统计, 
若果你的站点既不需求思索UOdysseyL包容幸免死链难点,也不须要展开SEO优化,就完全未有供给实行UXC60L-Rewrite。URubiconL-Rewrite是多个对品质有剧毒的处理进度。

常用的URL-Rewrite方案
U途锐L-Rewrite既能够生出在Web服务器(IIS/Apache)一级,也足以爆发在Web应用程序一流(Asp.Net/Jsp/PHP/…)。

 
一.Web应用程序级别的UWranglerL-Rewrite
  在Web应用程序级别的UCR-VL-Rewrite。有五个比较显赫的现成组件。
  1) 微软提供的 U宝马X5L-Rewrite
http://msdn2.microsoft.com/zh-cn/library/ms972974.aspx
  2) Open Source的 UrlRewriter.NET http://urlrewriter.net/
  3) UrlRewriting http://www.urlrewriting.net/en/Download.aspx

那种组件内部基本的行事规律: 都以在和谐的Web
Application的web.config中添加httpModule。用那些httpModule来处理重写。(其实也可一连System.Web.HttpApplication,在Application_BeginRequest中插入三个体协会调的秘诀处理重写)

里面宗旨的拍卖代码,上边包车型大巴代码摘引自UrlRewriter.NET组件。
 
1)从IHttpModule继承获得叁个本身的HttpModule,那几个HttpModule要求在web.config中配备,表明拥有的伸手都要透过那几个HttpModule。

public sealed class RewriterHttpModule : IHttpModule
  {
    /// <summary>
    /// Initialises the module.
    /// </summary>
    /// <param name="context">The application context.</param>
    void IHttpModule.Init(HttpApplication context)
    {
      context.BeginRequest += new EventHandler(BeginRequest);
    }
…
private void BeginRequest(object sender, EventArgs e)
    {
      // Add our PoweredBy header
      HttpContext.Current.Response.AddHeader(Constants.HeaderXPoweredBy, Configuration.XPoweredBy);

      _rewriter.Rewrite();
    }
}

 

贰)读取重写规则,判断是或不是需求重写,明确哪些重写,进行重写。

 

public void Rewrite()
    {
      string originalUrl = ContextFacade.GetRawUrl().Replace("+", " ");
      RawUrl = originalUrl;

      // Create the context
      RewriteContext context = new RewriteContext(this, originalUrl,
        ContextFacade.GetHttpMethod(), ContextFacade.MapPath,
        ContextFacade.GetServerVariables(), ContextFacade.GetHeaders(), ContextFacade.GetCookies());

      // Process each rule.
      ProcessRules(context);

      // Append any headers defined.
      AppendHeaders(context);

      // Append any cookies defined.
      AppendCookies(context);

      // Rewrite the path if the location has changed.
      ContextFacade.SetStatusCode((int)context.StatusCode);
      if ((context.Location != originalUrl) && ((int)context.StatusCode < 400))
      {
        if ((int)context.StatusCode < 300)
        {
          // Successful status if less than 300
          _configuration.Logger.Info(MessageProvider.FormatString(Message.RewritingXtoY, 
            ContextFacade.GetRawUrl(), context.Location));

          // Verify that the url exists on this server.
          HandleDefaultDocument(context);// VerifyResultExists(context);

          ContextFacade.RewritePath(context.Location);
        }
        else
        {
          // Redirection
          _configuration.Logger.Info(MessageProvider.FormatString(Message.RedirectingXtoY,
            ContextFacade.GetRawUrl(), context.Location));

          ContextFacade.SetRedirectLocation(context.Location);
        }
      }
      else if ((int)context.StatusCode >= 400)
      {
        HandleError(context);
      }
      else if (HandleDefaultDocument(context))
      {
        ContextFacade.RewritePath(context.Location);
      }

      // Sets the context items.
      SetContextItems(context);
    }

 

这种重写是ASP.NET
Pipeline级其他重写,能够重写1切Asp.net接管的伏乞。

 

统计 2

 

在那里对/Pd/Book.aspx的请求被重写到了 /Pd.aspx?Cg=books.
Web应用程序级别的U普拉多L-Rewrite只好重写Web应用程序接管的央求。它并未办法处理.js
.jpg的重写。原因是那几个请求到达IIS后,IIS根本就平素不把那个请求分发到Asp.Net,所以那一个请求就不会时有发生重写的处理和操作。在IIS中得以安顿,对什么后缀的乞请是被IIS分发到Asp.Net的。

 

统计 3

 

就算您一定要在Asp.Net级别对.js的伏乞举办重写,能够在此间钦命.js的呼吁由Asp.Net接管,不过此时你须求团结处理.js的Response。Web服务器级别的URAV四L-Rewrite能够比较好的缓解那上边的题材吧。

2. Web服务器级其他U卡宴L-Rewrite

 

Apache服务器
Apache服务器原生援救了URubiconL-Rewrite。在config中开辟LoadModule
rewrite_module modules/mod_rewrite.so 然后布置重写的正则表明式。例如:

引用自Apache2.第22中学文参考手册
中文手册
Apache-UrlRewrite

---------------------------------------------
描述: 
这个规则的目的是强制使用特定的主机名以代替其他名字。比如,你想强制使用www.example.com代替example.com,就可以在以下方案的基础上进行修改: 
解决方案: 
对运行在非80端口的站点

RewriteCond %{HTTP_HOST} !^fully\.qualified\.domain\.name [NC]
RewriteCond %{HTTP_HOST} !^$
RewriteCond %{SERVER_PORT} !^80$
RewriteRule ^/(.*) http://fully.qualified.domain.name:%{SERVER_PORT}/$1 [L,R]

对运行在80端口的站点

RewriteCond %{HTTP_HOST} !^fully\.qualified\.domain\.name [NC]
RewriteCond %{HTTP_HOST} !^$
RewriteRule ^/(.*) http://fully.qualified.domain.name/$1 [L,R]
---------------------------------------------------------------------------

 

IIS6/IIS7 Web服务器
IIS七新的“管道格局”其实是把ASP.NET中的有些概念与IIS实行了越发深度的并轨。在IIS7Program Manager: Mike Volodarsky的Blog中有1篇小说分析了那方面包车型地铁内容:
Breaking Changes for ASP.NET 2.0 applications running in Integrated
mode on IIS
7.0

 

IIS柒的“经典情势”与IIS 陆基本上是如出壹辙的。

在IIS6 +
Asp.Net应用程序级的U翼虎L-Rewrite,只可以在呼吁被分配到Asp.Net引擎后才能生出重写操作。在IIS七那点被改动了。IIS七能够对未有后缀名的恳求进行重写,Asp.Net和IIS柒举行了纵深的集成。IIS7能够在
IIS
请求管道的其他地点实施三个HttpModule,下边是3个IIS柒下Asp.Net的重写配置:

摘引自ScottGu的Blog

 

<?xml version="1.0" encoding="UTF-8"?>

<configuration>

<configSections>
<section name="rewriter" 
requirePermission="false" 
type="Intelligencia.UrlRewriter.Configuration.RewriterConfigurationSectionHandler, Intelligencia.UrlRewriter" />
</configSections>

<system.web>

<httpModules>
<add name="UrlRewriter" type="Intelligencia.UrlRewriter.RewriterHttpModule, Intelligencia.UrlRewriter" />
</httpModules>

  </system.web>

<system.webServer>

<modules runAllManagedModulesForAllRequests="true">
<add name="UrlRewriter" type="Intelligencia.UrlRewriter.RewriterHttpModule" />
</modules>

<validation validateIntegratedModeConfiguration="false" />

</system.webServer>

<rewriter>
<rewrite url="~/products/(.+)" to="~/products.aspx?category=$1" />
</rewriter>

</configuration>

 

其中:<rewrite url=”~/products/(.+)” to=”~/products.aspx?category=$一”
/>那条规则中的~/products/(.+)那条正则表明式。匹配了/products/下的装有链接。
IIS陆服务器级别下的重写要求利用ISAPI Filters Rewrite来促成。

ISAPI Filters有八个尤其有名工程:
  1)Helicon Techs ISAPI Rewrite: http://www.isapirewrite.com/
提供贰个9九欧元(可免费试用30天)的ISAPI
UXC60L重写产品总体版,以及二个免费的轻量级版本。
  2)Ionics ISAPI Rewrite: http://cheeso.members.winisp.net/IIRF.aspx
全免费开源组件。
  在 ISAPI
Filter编程重写UHummerH贰L

中有认证。

服务器级的重写与使用程序级的重写最大的区别在于他们爆发的机会比不上。下图是在服务器级把/Pd/Book.aspx重写到/Pd.aspx?Cg=books

 

 

统计 4

 

呼吁还不曾到Asp.Net引擎,就被重写了。

三.Asp.Net级别上海重机厂写的一对小细节难题(部分内容源自司各脱Gu 的Blog)
  若是页面中留存form且form是runat=server的<form
runat=”server”>,那么那么些页面在重写后form的action是原始U奥迪Q5L,不是重写后彻底的U帕杰罗L。例如/Pd/Book.aspx重写到/Pd.aspx?Cg=books这几个场地。实际用户浏览器访问的地点是/Pd/Book.aspx,在劳动器级被重写后呼吁变成了/Pd.aspx?Cg=books,在这种景色下form的action会被render成/Pd.aspx?Cg=books,其实此时越发想要action被render成/Pd/Book.aspx,让页面PostBack到同一职位。在壹些情形下action被render成/Pd.aspx?Cg=books是不会对健康办事有震慑的,只要/Pd.aspx?Cg=books不被重写规则匹配上,/Pd.aspx?Cg=books会被正确发回去Asp.Net引擎。可是浏览器上的地址栏会变化,暴流露真正的地点。要是这么些U福睿斯L被某分别的规则匹配,那就亟须供给form的action被正确的Render成/Pd/Book.aspx,那种联合的重写后的ULX570L。

解决办法:
  一)自身包裹form控件。把url写在有些hidden
田野里同postback一起回来,render时修改action为hidden 田野(field)里的url.
  2)使用JavaScript在form submit前修改action例如
window.document.forms[0].action = window.location;
  3)使用ASP.NET 2.0 Control Adapter(源自ScottGu 的Blog)
 
那种重写是当在动用Asp.Net应用程序超级的重写时,使用Context.Request.RawUrl填写form的action,当使用IIS应用服务器拔尖的重写时把干净的ULacrosseL记录在Request.ServerVariables[“HTTP_X_REWRITE_URL”]中,使用Request.ServerVariables[“HTTP_X_REWRITE_URL”]填写form的action,填写form
action 的进程都以经过Control Adapter对form Control增添,override
form控件的 WriteAttribute方法,在Render时再也钦定form的action。

着力源代码
摘引自ScottGu的Blog

统计 5

 

重写后路径包容难题
 
在/Pd/Book.aspx重写到/Pd.aspx?Cg=books的风貌中,页面中一经有相对地方的能源,如某些img的src=”../logo.gif”或src=”logo.gif”。那时浏览器请求那几个能源条件的任务是/pd/也便是说src=”../logo.gif”请求的门道是/logo.gif,src=”logo.gif”请求的路线是/pd/logo.gif。但是实际这么些财富的原则地点是
/ 因为本来的U凯雷德L是/Pd.aspx?Cg=books。这时就会时有产生产资料源找不到的情景。
  一)使用劳务器端的img使用 ~ 路径能够消除那一个难点(源自斯科特Gu的Blog)。
  2)使用<base href=” http://xxx/
“></base>标签,这几个标签供给写在head里。告诉页面,页面中享有相对路径的原则路径是
http://xxx/ ,从而消除重写后路径失效的题目。
  base标签的评释: http://www.w3school.com.cn/tags/tag_base.asp

 
到那里,URubiconL-Rewrite的标题探究完了。在事实上项目中肯定还会碰着各个分化的题材,不过消除的思绪,臆想会是地方那么些技能的组合和扩充,希望由此上边对U福特ExplorerL-Rewrite难点的议论,会对蒙受的新题材能起到有个别赞助的作用。

 

 

作者:葡萄城控件技术团队.Zhang Sichu

职称:Web消除方案专家

统筹 => 开发 => 测试 => 优化
=> 设计 => 开发 => 测试
……………………………. 流程很多,出三个好东西最难的是时刻和执着.

1.json 标准     
http://www.json.org/

/*
* 根据索引得到这个数组中对象
* array    : 数组对象
* idx        : 查找的索引 必须 [0,cjson_getlen(array)) 范围内
*            : 返回查找到的当前对象
*/
cjson_t 
cjson_getarray(cjson_t array, int idx)
{
    cjson_t c;
    DEBUG_CODE({
        if (!array || idx < 0) {
            SL_FATAL("array:%p, idx=%d params is error!", array, idx);
            return NULL;
        }
    });

    for (c = array->child; c&&idx > 0; c = c->next)
        --idx;

    return c;
}

来分析一下 struct cjson 中组织字段意思, 当中 next,prev 驾驭为双向链表, 为了寻觅同一层的 对象.例如

统计 6

肆.cjson 源码源码呈现

  恭喜到此处了,上边第2个享受的函数还有一种好思路 是 整数片段 和 小数部分分离算,前边再加起来. 就到此地吧.

[1,2,3,4,[6,5]] 其中 1,2,3,4,
[6,5] 正是同一层, 陆,五 是同1层

从 value 解析早先 境遇的 string number true false null 直接解析

吐槽一下

 

是否很不难 一下都驾驭了. 个中 str_icmp 上壹篇博文中就像讲过源码 如下

伍.cjson 测试代码 显示

// 将value 转换塞入 item json值中一部分
static const char* __parse_value(cjson_t item, const char* value)
{
    char c; 
    if ((value) && (c = *value)) {
        switch (c) {
            // n = null, f = false, t = true
        case 'n' : return item->type = _CJSON_NULL, value + 4;
        case 'f' : return item->type = _CJSON_FALSE, value + 5;
        case 't' : return item->type = _CJSON_TRUE, item->vd = 1.0, value + 4;
        case '\"': return __parse_string(item, value);
        case '0' : case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9':
        case '+' : case '-': return __parse_number(item, value);
        case '[' : return __parse_array(item, value);
        case '{' : return __parse_object(item, value);
        }
    }
    // 循环到这里是意外 数据
    SL_WARNING("params value = %s!", value);
    return NULL;
}

 那里是 value函数入口, 再以 array处理为例 流程 如下

#include <schead.h>
#include <sclog.h>
#include <cjson.h>

// 测试 cjson 函数
int main(int argc, char* argv[])
{
    //注册等待函数
    INIT_PAUSE();

    //启动日志记录功能
    sl_start();

    // 第二个 测试 json 串的解析
    puts("测试 cjson 是否可用");
    char text1[] = "{\n\"name\": \"Jack (\\\"Bee\\\") Nimble\", \n\"format\": {\"type\":       \"rect\", \n\"width\":      1920, \n\"height\":     1080, \n\"interlace\":  false,\"frame rate\": 24\n}\n}";

    cjson_t js = cjson_parse(text1);

    cjson_t name = cjson_getobject(js, "name");
    printf("name => %s\n", name->vs);

    cjson_t format = cjson_getobject(js, "format");
    printf("len(format) => %d\n", cjson_getlen(format));

    cjson_t interlace = cjson_getobject(format, "interlace");
    printf("interlace => %d\n", cjson_getint(interlace));

    cjson_delete(&js);

    //进行第三组测试

    puts(" 测试 数组的读取");
    char text2[] = "[\"Sunday\", \"Monday\", \"Tuesday\", \"Wednesday\", \"Thursday\", \"Friday\", \"Saturday\"]";
    js = cjson_parse(text2);
    int len = cjson_getlen(js);
    int i;
    for (i = 0; i < len; ++i) {
        cjson_t item = cjson_getarray(js,i);
        printf("%d => %s.\n", i, item->vs);
    }
    cjson_delete(&js);


    puts("第三组测试");
    char text3[] = "[\n    [0, -1, 0],\n    [1, 0, 0],\n    [0, 0, 1]\n    ]\n";
    js = cjson_parse(text3);
    len = cjson_getlen(js);
    for (i = 0; i < len; ++i) {
        cjson_t item = cjson_getarray(js, i);
        printf("%d => %d.\n", i, cjson_getlen(item));
    }

    cjson_delete(&js);

    return 0;
}
/*
 * 这是个不区分大小写的比较函数
 * ls       : 左边比较字符串
 * rs       : 右边比较字符串
 *          : 返回 ls>rs => >0 ; ls = rs => 0 ; ls<rs => <0
 */
int 
str_icmp(const char* ls, const char* rs)
{
    int l, r;
    if(!ls || !rs)
        return (int)ls - (int)rs;

    do {
        if((l=*ls++)>='a' && l<='z')
            l -= 'a' - 'A';
        if((r=*rs++)>='a' && r<='z')
            r -= 'a' - 'A';
    } while(l && l==r);

    return l-r;
}

 

实质上 结构 就控制 全体. 等同人的脾气.

一. json 的语法解析 分析

json 的语法解析 同

上面正是cJSON中常出现的代码格式,确实 700多行, 那些700多行全部分离或然要
1400行. 对于那么些说700行的爱侣只可以是呵呵.

 

上面做法是 幸免野指针, 用时间换安全. 时间空间安全 3要素,基本就是编制程序三大成分. 上面
_CJSOn_ISREF 是为了 set 前面设计留的, 添加了不供给释放的事物

真诚手累. 到此处 cjson 解析部分在工程中生产的代码我们都搭建起来. 还不易,欢迎尝试

 

动用举例如下

到这里 近日 理解的安顿中央就竣事了.

粗粗流程就像上了,通过直接递归处理了 json语法的语句.

此地提供接口如下

#include <cjson.h>
#include <schead.h>
#include <sclog.h>
#include <tstring.h>
#include <math.h>

// 删除cjson
static void __cjson_delete(cjson_t c)
{
    cjson_t next;
    while (c) {
        next = c->next;
        //递归删除儿子
        if (!(c->type & _CJSON_ISREF)) {
            if (c->child) //如果不是尾递归,那就先递归
                __cjson_delete(c->child);
            if (c->vs)
                free(c->vs);
        }
        else if (!(c->type & _CJSON_ISCONST) && c->key)
            free(c->key);
        free(c);
        c = next;
    }
}

/*
*  删除json串内容,最近老是受清华的老学生打击, 会起来的......
* c        : 待释放json_t串内容
*/
void 
cjson_delete(cjson_t* pc)
{
    if (!pc || !*pc)
        return;
    __cjson_delete(*pc);
    *pc = NULL;
}

//构造一个空 cjson 对象
static inline cjson_t __cjson_new(void)
{
    cjson_t c = calloc(1, sizeof(struct cjson));
    if (!c) {
        SL_FATAL("calloc sizeof struct cjson error!");
        exit(_RT_EM);
    }
    return c;
}

// 简化的代码段,用宏来简化代码书写 , 16进制处理
#define __parse_hex4_code(c, h) \
    if (c >= '0' && c <= '9') \
        h += c - '0'; \
    else if (c >= 'A' && c <= 'F') \
        h += 10 + c - 'A'; \
    else if (c >= 'a' && c <= 'z') \
        h += 10 + c - 'F'; \
    else \
        return 0

// 等到unicode char代码
static unsigned __parse_hex4(const char* str)
{
    unsigned h = 0;
    char c = *str;
    //第一轮
    __parse_hex4_code(c, h);
    h <<= 4;
    c = *++str;
    //第二轮
    __parse_hex4_code(c, h);
    h <<= 4;
    c = *++str;
    //第三轮
    __parse_hex4_code(c, h);
    h <<= 4;
    c = *++str;
    //第四轮
    __parse_hex4_code(c, h);

    return h;
}

// 分析字符串的子函数,
static const char* __parse_string(cjson_t item, const char* str)
{
    static unsigned char __marks[] = { 0x00, 0x00, 0xC0, 0xE0, 0xF0, 0xF8, 0xFC };
    const char *ptr;
    char *nptr, *out;
    int len;
    char c;
    unsigned uc, nuc;

    if (*str != '\"') { // 检查是否是字符串内容
        SL_WARNING("need \\\" str => %s error!", str);
        return NULL;
    }

    for (ptr = str + 1, len = 0; (c = *ptr++) != '\"' && c; ++len)
        if (c == '\\') //跳过转义字符
            ++ptr;
    if (!(out = malloc(len + 1))) {
        SL_FATAL("malloc %d size error!", len + 1);
        return NULL;
    }
    // 这里复制拷贝内容
    for (ptr = str + 1, nptr = out; (c = *ptr) != '\"' && c; ++ptr) {
        if (c != '\\') {
            *nptr++ = c;
            continue;
        }
        // 处理转义字符
        switch ((c = *++ptr)) {
        case 'b': *nptr++ = '\b'; break;
        case 'f': *nptr++ = '\f'; break;
        case 'n': *nptr++ = '\n'; break;
        case 'r': *nptr++ = '\r'; break;
        case 't': *nptr++ = '\t'; break;
        case 'u': // 将utf16 => utf8, 专门的utf处理代码
            uc = __parse_hex4(ptr + 1);
            ptr += 4;//跳过后面四个字符, unicode
            if ((uc >= 0xDC00 && uc <= 0xDFFF) || uc == 0)    break;    /* check for invalid.    */

            if (uc >= 0xD800 && uc <= 0xDBFF)    /* UTF16 surrogate pairs.    */
            {
                if (ptr[1] != '\\' || ptr[2] != 'u')    
                    break;    /* missing second-half of surrogate.    */
                nuc = __parse_hex4(ptr + 3);
                ptr += 6;
                if (nuc < 0xDC00 || nuc>0xDFFF)        
                    break;    /* invalid second-half of surrogate.    */
                uc = 0x10000 + (((uc & 0x3FF) << 10) | (nuc & 0x3FF));
            }

            len = 4;
            if (uc < 0x80) 
                len = 1;
            else if (uc < 0x800) 
                len = 2;
            else if (uc < 0x10000) 
                len = 3; 
            nptr += len;

            switch (len) {
            case 4: *--nptr = ((uc | 0x80) & 0xBF); uc >>= 6;
            case 3: *--nptr = ((uc | 0x80) & 0xBF); uc >>= 6;
            case 2: *--nptr = ((uc | 0x80) & 0xBF); uc >>= 6;
            case 1: *--nptr = (uc | __marks[len]);
            }
            nptr += len;
            break;
        default: *nptr++ = c;
        }
    }

    *nptr = '\0';
    if (c == '\"')
        ++ptr;
    item->vs = out;
    item->type = _CJSON_STRING;
    return ptr;
}

// 分析数值的子函数,写的可以
static const char* __parse_number(cjson_t item, const char* str)
{
    double n = 0.0, ns = 1.0, nd = 0.0; //n把偶才能值, ns表示开始正负, 负为-1, nd 表示小数后面位数
    int e = 0, es = 1; //e表示后面指数, es表示 指数的正负,负为-1
    char c;

    if ((c = *str) == '-' || c == '+') {
        ns = c == '-' ? -1.0 : 1.0; //正负号检测, 1表示负数
        ++str;
    }
    //处理整数部分
    for (c = *str; c >= '0' && c <= '9'; c = *++str)
        n = n * 10 + c - '0';
    if (c == '.')
        for (; (c = *++str) >= '0' && c <= '9'; --nd)
            n = n * 10 + c - '0';

    // 处理科学计数法
    if (c == 'e' || c == 'E') {
        if ((c = *++str) == '+') //处理指数部分
            ++str;
        else if (c == '-')
            es = -1, ++str;
        for (; (c = *str) >= '0' && c <= '9'; ++str)
            e = e * 10 + c - '0';
    }

    //返回最终结果 number = +/- number.fraction * 10^+/- exponent
    n = ns * n * pow(10.0, nd + es * e);
    item->vd = n;
    item->type = _CJSON_NUMBER;
    return str;
}

// 跳过不需要处理的字符
static const char* __skip(const char* in)
{
    if (in && *in && *in <= 32) {
        unsigned char c;
        while ((c = *++in) && c <= 32)
            ;
    }
    return in;
}

// 递归下降分析 需要声明这些函数
static const char* __parse_array(cjson_t item, const char* str);
static const char* __parse_object(cjson_t item, const char* str);
static const char* __parse_value(cjson_t item, const char* value);

// 分析数组的子函数, 采用递归下降分析
static const char* __parse_array(cjson_t item, const char* str)
{
    cjson_t child;
    if (*str != '[') {
        SL_WARNING("array str error start: %s.", str);
        return NULL;
    }

    item->type = _CJSON_ARRAY;
    str = __skip(str + 1);
    if (*str == ']') // 低估提前结束
        return str + 1;

    item->child = child = __cjson_new();
    str = __skip(__parse_value(child, str));
    if (!str) {//解析失败 直接返回
        SL_WARNING("array str error e n d one: %s.", str);
        return NULL;
    }
    while (*str == ',') {
        cjson_t nitem = __cjson_new();
        child->next = nitem;
        nitem->prev = child;
        child = nitem;
        str = __skip(__parse_value(child, __skip(str + 1)));
        if (!str) {// 写代码是一件很爽的事
            SL_WARNING("array str error e n d two: %s.", str);
            return NULL;
        }
    }

    if (*str != ']') {
        SL_WARNING("array str error e n d: %s.", str);
        return NULL;
    }
    return str + 1; // 跳过']'
}

// 分析对象的子函数
static const char* __parse_object(cjson_t item, const char* str)
{
    cjson_t child;
    if (*str != '{') {
        SL_WARNING("object str error start: %s.", str);
        return NULL;
    }

    item->type = _CJSON_OBJECT;
    str = __skip(str + 1);
    if (*str == '}')
        return str + 1;

    //处理结点, 开始读取一个 key
    item->child = child = __cjson_new();
    str = __skip(__parse_string(child, str));
    if (!str || *str != ':') {
        SL_WARNING("__skip __parse_string is error : %s!", str);
        return NULL;
    }
    child->key = child->vs;
    child->vs = NULL;

    str = __skip(__parse_value(child, __skip(str + 1)));
    if (!str) {
        SL_WARNING("__skip __parse_string is error 2!");
        return NULL;
    }

    // 递归解析
    while (*str == ',') {
        cjson_t nitem = __cjson_new();
        child->next = nitem;
        nitem->prev = child;
        child = nitem;
        str = __skip(__parse_string(child, __skip(str + 1)));
        if (!str || *str != ':'){
            SL_WARNING("__parse_string need name or no equal ':' %s.", str);
            return NULL;
        }
        child->key = child->vs;
        child->vs = NULL;

        str = __skip(__parse_value(child, __skip(str+1)));
        if (!str) {
            SL_WARNING("__parse_string need item two ':' %s.", str);
            return NULL;
        }
    }

    if (*str != '}') {
        SL_WARNING("object str error e n d: %s.", str);
        return NULL;
    }
    return str + 1;
}

// 将value 转换塞入 item json值中一部分
static const char* __parse_value(cjson_t item, const char* value)
{
    char c; 
    if ((value) && (c = *value)) {
        switch (c) {
            // n = null, f = false, t = true
        case 'n' : return item->type = _CJSON_NULL, value + 4;
        case 'f' : return item->type = _CJSON_FALSE, value + 5;
        case 't' : return item->type = _CJSON_TRUE, item->vd = 1.0, value + 4;
        case '\"': return __parse_string(item, value);
        case '0' : case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9':
        case '+' : case '-': return __parse_number(item, value);
        case '[' : return __parse_array(item, value);
        case '{' : return __parse_object(item, value);
        }
    }
    // 循环到这里是意外 数据
    SL_WARNING("params value = %s!", value);
    return NULL;
}

/*
* 对json字符串解析返回解析后的结果
* jstr        : 待解析的字符串
*            : 返回解析好的字符串内容
*/
cjson_t 
cjson_parse(const char* jstr)
{
    cjson_t c = __cjson_new();
    const char* end;

    if (!(end = __parse_value(c, __skip(jstr)))) {
        SL_WARNING("__parse_value params end = %s!", end);
        cjson_delete(&c);
        return NULL;
    }

    //这里是否检测 返回测试数据
    return c;
}

/*
* 根据 item当前结点的 next 一直寻找到 NULL, 返回个数
*推荐是数组使用
* array    : 待处理的cjson_t数组对象
*            : 返回这个数组中长度
*/
int 
cjson_getlen(cjson_t array)
{
    int len = 0;
    if (array)
        for (array = array->child; array; array = array->next)
            ++len;

    return len;
}

/*
* 根据索引得到这个数组中对象
* array    : 数组对象
* idx        : 查找的索引 必须 [0,cjson_getlen(array)) 范围内
*            : 返回查找到的当前对象
*/
cjson_t 
cjson_getarray(cjson_t array, int idx)
{
    cjson_t c;
    DEBUG_CODE({
        if (!array || idx < 0) {
            SL_FATAL("array:%p, idx=%d params is error!", array, idx);
            return NULL;
        }
    });

    for (c = array->child; c&&idx > 0; c = c->next)
        --idx;

    return c;
}

/*
* 根据key得到这个对象 相应位置的值
* object    : 待处理对象中值
* key        : 寻找的key
*            : 返回 查找 cjson_t 对象
*/
cjson_t 
cjson_getobject(cjson_t object, const char* key)
{
    cjson_t c;
    DEBUG_CODE({
        if (!object || !key || !*key) {
            SL_FATAL("object:%p, key=%s params is error!", object, key);
            return NULL;
        }
    });

    for (c = object->child; c && str_icmp(key, c->key); c = c->next)
        ;

    return c;
}
/*
* 根据key得到这个对象 相应位置的值
* object    : 待处理对象中值
* key        : 寻找的key
*            : 返回 查找 cjson_t 对象
*/
cjson_t 
cjson_getobject(cjson_t object, const char* key)
{
    cjson_t c;
    DEBUG_CODE({
        if (!object || !key || !*key) {
            SL_FATAL("object:%p, key=%s params is error!", object, key);
            return NULL;
        }
    });

    for (c = object->child; c && str_icmp(key, c->key); c = c->next)
        ;

    return c;
}

有标题再互相交流.

我们就不处理.

 

看一遍是还是不是就明白了,看代码如故相比较好懂的. 自个儿写就难壹些了. 首要难在

#ifndef _H_CJSON
#define _H_CJSON

// json 中几种数据类型定义
#define _CJSON_FALSE    (0)
#define _CJSON_TRUE        (1)
#define _CJSON_NULL        (2)
#define _CJSON_NUMBER    (3)
#define _CJSON_STRING    (4)
#define _CJSON_ARRAY    (5)
#define _CJSON_OBJECT    (6)

#define _CJSON_ISREF    (256)        //set 时候用如果是引用就不释放了
#define _CJSON_ISCONST    (512)        //set时候用, 如果是const char* 就不释放了

struct cjson {
    struct cjson *next, *prev;
    struct cjson *child; // type == _CJSON_ARRAY or type == _CJSON_OBJECT 那么 child 就不为空

    int type;
    char *key;    // json内容那块的 key名称     
    char *vs;    // type == _CJSON_STRING, 是一个字符串     
    double vd;  // type == _CJSON_NUMBER, 是一个num值, ((int)c->vd) 转成int 或 bool
};

//定义cjson_t json类型
typedef struct cjson* cjson_t;

/*
 * 这个宏,协助我们得到 int 值 或 bool 值 
 * 
 * item : 待处理的目标cjson_t结点
 */
#define cjson_getint(item) \
    ((int)((item)->vd))

/*
 *  删除json串内容  
 * c        : 待释放json_t串内容
 */
extern void cjson_delete(cjson_t* pc);

/*
 * 对json字符串解析返回解析后的结果
 * jstr        : 待解析的字符串
 */
extern cjson_t cjson_parse(const char* jstr);

/*
 * 根据 item当前结点的 next 一直寻找到 NULL, 返回个数
 *推荐是数组使用
 * array    : 待处理的cjson_t数组对象
 *            : 返回这个数组中长度
 */
extern int cjson_getlen(cjson_t array);

/*
 * 根据索引得到这个数组中对象
 * array    : 数组对象
 * idx        : 查找的索引 必须 [0,cjson_getlen(array)) 范围内
 *            : 返回查找到的当前对象
 */
extern cjson_t cjson_getarray(cjson_t array, int idx);

/*
 * 根据key得到这个对象 相应位置的值
 * object    : 待处理对象中值
 * key        : 寻找的key
 *            : 返回 查找 cjson_t 对象
 */
extern cjson_t cjson_getobject(cjson_t object, const char* key);

#endif // !_H_CJSON

 此前这么些和cJSON相比较了眨眼间间, 觉得 在此以前写的要命 优点是 空间小, 速度快, 因为运用的是 用时解析.而cJSON接纳

引进有心的人多写一遍,  帮忙 +19.09 -1九.0
19.5陆7e1贰3 -1玖.09E-九 那些转换. 那是种类工程代码,久经考验.

的是递归下落分析, 结构也正如大.最终 决定再重构3个用的cjson. 近年来统筹思路以通用为主.

正文

那是个 测试code 宏, 有时候我们想 DEBUG下 测试代码在 Release 格局 删除
. 就用地方宏 .在gcc 下 需求 加上 -I_DEBUG

  网上海人民广播电视台湾大学恋人 推荐看cJSON源码, 因为就700多行,可以看看学学. 真的吗 看上面 摘录的代码

第贰看 结构文件

先看最根本的 内部存款和储蓄器释放代码

后买你是 cjson.c 的落到实处

参照资料

统计 7

老是写博文,发现写的好长,不关怀的人很难驾驭, 这一次运用1种新思路. 先将通用的简单的C开发技术
. 前边再讲大旨.

 

1 void   cJSON_ReplaceItemInObject(cJSON *object, const char *string, cJSON *newitem) { int i = 0;cJSON *c = object->child;while (c && cJSON_strcasecmp(c->string, string))i++, c = c->next;if (c) { newitem->string = cJSON_strdup(string);cJSON_ReplaceItemInArray(object, i, newitem); } }

测试的  test_cjson.c

// json 中几种数据类型定义
#define _CJSON_FALSE    (0)
#define _CJSON_TRUE        (1)
#define _CJSON_NULL        (2)
#define _CJSON_NUMBER    (3)
#define _CJSON_STRING    (4)
#define _CJSON_ARRAY    (5)
#define _CJSON_OBJECT    (6)

#define _CJSON_ISREF    (256)        //set 时候用如果是引用就不释放了
#define _CJSON_ISCONST    (512)        //set时候用, 如果是const char* 就不释放了

struct cjson {
    struct cjson *next, *prev;
    struct cjson *child; // type == _CJSON_ARRAY or type == _CJSON_OBJECT 那么 child 就不为空

    int type;
    char *key;    // json内容那块的 key名称     
    char *vs;    // type == _CJSON_STRING, 是一个字符串     
    double vd;  // type == _CJSON_NUMBER, 是一个num值, ((int)c->vd) 转成int 或 bool
};

//定义cjson_t json类型
typedef struct cjson* cjson_t;

但有一点,看了重重nb的json引擎,也就 cJSON最不难精晓了.最容命理术数习了. 质量还不错. 这些是只好说的优点.

眼见了不少开源的c json引擎
.对个中三个比较正规的 cJSON 引擎 长远学习了一下.

4->next 后面 的 child 就是
[6,5]每一遍解析到新的 array 或 object 都用child 导向它.

是或不是很好精通

 

key 用于关联对象.

后记

 

三.cjson 部分源码分析

面试也常考. 没啥意思
, 那还有二个 不错的技能 如下

二.cjson 的 结构解析

  

// 分析数组的子函数, 采用递归下降分析
static const char* __parse_array(cjson_t item, const char* str)
{
    cjson_t child;
    if (*str != '[') {
        SL_WARNING("array str error start: %s.", str);
        return NULL;
    }

    item->type = _CJSON_ARRAY;
    str = __skip(str + 1);
    if (*str == ']') // 低估提前结束
        return str + 1;

    item->child = child = __cjson_new();
    str = __skip(__parse_value(child, str));
    if (!str) {//解析失败 直接返回
        SL_WARNING("array str error e n d one: %s.", str);
        return NULL;
    }
    while (*str == ',') {
        cjson_t nitem = __cjson_new();
        child->next = nitem;
        nitem->prev = child;
        child = nitem;
        str = __skip(__parse_value(child, __skip(str + 1)));
        if (!str) {// 写代码是一件很爽的事
            SL_WARNING("array str error e n d two: %s.", str);
            return NULL;
        }
    }

    if (*str != ']') {
        SL_WARNING("array str error e n d: %s.", str);
        return NULL;
    }
    return str + 1; // 跳过']'
}

  
以前或然是二零一八年的二零一八年,写了2个 c json 解析引擎用于3个计算实验数据项目开支中. 尚可. 二零一八年在网上

  首先大家分享一个 string convert number 的主次, 首先看上边代码.

比如value 解析如下

2.cJSON 源码  
https://sourceforge.net/projects/cjson/

在看二个赢得涉及对象的值

运作的结果如下

 

到 array
, object 初步解析的时候 解析完结直接 到value 解析, 那样递归下跌解析达成 函数调用的流程图如下:

代码一定是跨平台的, 选取window 是为着 提升调节和测试效用选拔VS的DEBUG
. 在Linux 上就是重复 的调节和测试 , 手累. 吐槽一下 在 Linux 上行事久了,

// 分析数值的子函数,写的可以
double parse_number(const char* str)
{
    double n = 0.0, ns = 1.0, nd = 0.0; //n把偶才能值, ns表示开始正负, 负为-1, nd 表示小数后面位数
    int e = 0, es = 1; //e表示后面指数, es表示 指数的正负,负为-1
    char c;

    if ((c = *str) == '-' || c == '+') {
        ns = c == '-' ? -1.0 : 1.0; //正负号检测, 1表示负数
        ++str;
    }
    //处理整数部分
    for (c = *str; c >= '0' && c <= '9'; c = *++str)
        n = n * 10 + c - '0';
    if (c == '.')
        for (; (c = *++str) >= '0' && c <= '9'; --nd)
            n = n * 10 + c - '0';

    // 处理科学计数法
    if (c == 'e' || c == 'E') {
        if ((c = *++str) == '+') //处理指数部分
            ++str;
        else if (c == '-')
            es = -1, ++str;
        for (; (c = *str) >= '0' && c <= '9'; ++str)
            e = e * 10 + c - '0';
    }

    //返回最终结果 number = +/- number.fraction * 10^+/- exponent
    n = ns * n * pow(10.0, nd + es * e);
    return n;
}

type 代表项目 暗中认可有个中 [0,6], 如上
_CJSON_*

第一看 while中内容,挨个分析 数组中内容,最终又导向于 value中.

背后将交给测试代码

一. 积攒的C开发 技巧 看完那个 你就曾经赚了,
前边就足以不看了

末尾 再扯一点, 那篇博文也足以精晓为cJSON的一语道破剖析. 最终小编使用递归下跌分析 语法,构造 cjson_t 结构. 设计比cJSON更好用,更高效.

/*
 * 这个宏,协助我们得到 int 值 或 bool 值 
 * 
 * item : 待处理的目标cjson_t结点
 */
#define cjson_getint(item) \
    ((int)((item)->vd))

/*
 *  删除json串内容  
 * c        : 待释放json_t串内容
 */
extern void cjson_delete(cjson_t* pc);

/*
 * 对json字符串解析返回解析后的结果
 * jstr        : 待解析的字符串
 */
extern cjson_t cjson_parse(const char* jstr);

/*
 * 根据 item当前结点的 next 一直寻找到 NULL, 返回个数
 *推荐是数组使用
 * array    : 待处理的cjson_t数组对象
 *            : 返回这个数组中长度
 */
extern int cjson_getlen(cjson_t array);

/*
 * 根据索引得到这个数组中对象
 * array    : 数组对象
 * idx        : 查找的索引 必须 [0,cjson_getlen(array)) 范围内
 *            : 返回查找到的当前对象
 */
extern cjson_t cjson_getarray(cjson_t array, int idx);

/*
 * 根据key得到这个对象 相应位置的值
 * object    : 待处理对象中值
 * key        : 寻找的key
 *            : 返回 查找 cjson_t 对象
 */
extern cjson_t cjson_getobject(cjson_t object, const char* key);
// 删除cjson
static void __cjson_delete(cjson_t c)
{
    cjson_t next;
    while (c) {
        next = c->next;
        //递归删除儿子
        if (!(c->type & _CJSON_ISREF)) {
            if (c->child) //如果不是尾递归,那就先递归
                __cjson_delete(c->child);
            if (c->vs)
                free(c->vs);
        }
        else if (!(c->type & _CJSON_ISCONST) && c->key)
            free(c->key);
        free(c);
        c = next;
    }
}

/*
*  删除json串内容,最近老是受清华的老学生打击, 会起来的......
* c        : 待释放json_t串内容
*/
void 
cjson_delete(cjson_t* pc)
{
    if (!pc || !*pc)
        return;
    __cjson_delete(*pc);
    *pc = NULL;
}

前言

/*
 * 10.1 这里是一个 在 DEBUG 模式下的测试宏 
 *    
 * 用法 :
 * DEBUG_CODE({
 *        puts("debug start...");    
 * });
 */
#ifndef DEBUG_CODE
# ifdef _DEBUG
#    define DEBUG_CODE(code) code
# else
#    define DEBUG_CODE(code) 
# endif // ! _DEBUG
#endif // !DEBUG_CODE

统计 8

是或不是很酷. 到那边 可以认为 值了学到了.  后边不佳懂,可看可不看了!