libfv:基于C++20的异步HTTP库


Warning: WP_Syntax::substituteToken(): Argument #1 ($match) must be passed by reference, value given in /www/wwwroot/fawdlstty.com/wp-content/plugins/wp-syntax/wp-syntax.php on line 383

仓库地址:https://github.com/fawdlstty/libfv

介绍一款船新HTTP库。C++的HTTP库很多,但基于C++20的异步网络HTTP库几乎没有。我没找到好用的,因此写了一个。在讲解这个库之前,我先说说为什么我们需要这样的库。

C++ HTTP库有两种主要的实现方式,第一种是同步HTTP网络访问,比如这样的代码:

1
2
3
// 伪代码
Response _r = HttpGet ("https://t.cn");
std::cout << _t.text;

这样的代码写起来很简单,但它存在一个问题:HTTP网络访问比较耗时,可能需要几百毫秒,这么长时间,这个线程将阻塞在这里,比较消耗线程资源。假如遇到需要同时发起几十、几百个请求,将较大消耗系统资源。很显然,它不是一个较好的设计。

继续阅读libfv:基于C++20的异步HTTP库

状态机是什么?有什么用?

推荐一个状态机库,支持C++与C#,通过两种语言分别实现。链接:https://github.com/fawdlstty/SMLite

为了大家都能看懂,下面的代码以C#做示例,C++可以在项目里找到具体示例代码及用法。

回到最初的标题问题,我们来假设一下,假如碰到了一个需求,需求是实现一个半双工的网络处理程序,所谓半双工也就是上传时不能下载,下载时不能上传,另外也不能两块数据同时上传或下载。

看起来很简单是吧,一个线程专门做上传或下载操作。但如何让外部知道网络线程的状态呢?一个合适的方案是,定义一个枚举状态,有四种枚举值,Rest、Ready、Reading、Writing,分别代表搁置状态、待命状态、正在读取、正在写入。然后就是,外部的事件,比如打开、关闭、写入等命令,如何传达到线程呢?这儿可以定义触发器枚举值,Run、Close、Read、FinishRead、Write、FinishWrite六种,其中外部触发四种,内部触发两种(完成读取与完成写入,线程自己最先知道)。下面我们来分析分析状态与触发器的关系:

继续阅读状态机是什么?有什么用?

使你的ASP.Net Core项目支持git

你有时候是否想过,自己开发一套类似Gitea的项目?或者是自己项目加入Git服务端功能,来做一些自动化文件同步之类的操作?现在你有了一个选择,GitServerCore。

地址:https://github.com/fawdlstty/GitServerCore

GitServerCore是一个.NET Core开发的服务端中间件,可以非常便捷的在你的项目中加入git服务端功能,然后就能在远程电脑上随心所欲的git clone、git push。基于这个功能,你能轻松实现:

  • 一个基于.Net Core的Gitea或GitLab
  • 自动化文件同步
  • 自动化CI/CD插件
继续阅读使你的ASP.Net Core项目支持git

linux服务器软件自动化部署

自动化部署主要用在linux服务器集群上。比如有后台服务器需要升级软件,基本做法就是ftp传上去,然后ssh上面重启服务。假如后台服务器一旦多了,这种方式就很麻烦了。这时候可以考虑使用自动化部署的方式来实现后台程序自动更新。由于实现方式太多,是否用docker、WebHook还是GitHook还是轮询都能实现,此处我使用一种自认为最简单的方式实现。大致流程为git仓库->WebHook->程序接受相应->更新并重启

继续阅读linux服务器软件自动化部署

C#中的async/await关键字

这对关键字可能是C#迄今为止争议最大的关键字了。这两个关键字可谓是让人又爱又恨了。爱的是这对关键字极大简化了C#异步调用代码的开发,恨的是对初学者非常不友好,对于没用过这对关键字的人来说,使得丈二和尚摸不着头脑。

下面我将以最直观的原理来讲解这两个关键字的作用。在讲解这个之前,先给大家说说协程的原理。协程指的是一个线程里面分配多个调度任务,然后由用户选择是否切换任务。示例如下:
继续阅读C#中的async/await关键字

Python爬虫完全指导

这篇文章将会对Python爬虫做出完整的开发指导,对于新手小白来说,很容易通过这篇文章找到合适的开发方案。

这篇文章的所有爬虫技术全部基于HTTP,也就是说,爬虫主要是针对浏览器看到的东西,将内容给抓取出来。此处不讲其他TCP/UDP爬虫的原因是,只有HTTP协议在用户角度能直接看到,在不清楚具体TCP/UDP协议内容的情况下,很难找到通讯的方法。当然,如果你有接口文档,开发出TCP/UDP也是很容易的事。

现在开始学习爬虫。首先说说网页。当你打开一个网站,看到的这个页面,所有内容的集合,统称一个网页。每个网页都由三部分组成:HTML、CSS、JS。

  1. HTML是超文本标记代码,用于负责网页整体内容的排版及布局,以及内容呈现方式。只要看到很多的<a><label>等等这种标签,说明这些是HTML代码;
  2. CSS是格式描述代码,用于描述格式,比如字体大小、颜色、背景图等等,网页源码中标签以内的东西,或者.css文件中,里面的内容都是CSS代码。这类代码,可以说,除了文字本身,其他能看到的基本都与CSS有关,看起来大概像这样a { background-color: #FF0000; }
  3. JS是脚本语言代码,几乎所有与后台的交互,以及前台比较特殊的效果,以及所有的逻辑操作,存在于标签内,或者.js文件中,代码看起来像这样function a (b) { return b + 1; }
继续阅读Python爬虫完全指导

【易大师网络工具箱】项目解析

在我几年前上大学那会,我就想开发一个小软件,里面集成更多的功能,方便更多的人使用,但因为各种原因,软件始终没做成。到了现在,我觉得我可以重新建立好这个软件。本着学习交流的目的,我将软件开源,开源协议GPL3.0,并将里面的工具类授权为MIT,方便需要找代码的朋友们直接使用。

继续阅读【易大师网络工具箱】项目解析

JavaScript纯符号输出文本

最近发现一种新的玩法,一篇纯符号的就能显示出各种各样的文本,有点像C语言的乱码大赛的那种玩法。

感觉很感兴趣,具体研究了一下,是因为js的语言特性,可以说是它的“劣势”:

很奇怪的特性就在于,很多特殊语法拼凑起来就成了一种违反直觉的结果类型。纯符号就建立在这样的机制上面。下面我们来详细解析:
继续阅读JavaScript纯符号输出文本

几道不太简单的C艹面试题


Warning: WP_Syntax::substituteToken(): Argument #1 ($match) must be passed by reference, value given in /www/wwwroot/fawdlstty.com/wp-content/plugins/wp-syntax/wp-syntax.php on line 383

Warning: WP_Syntax::substituteToken(): Argument #1 ($match) must be passed by reference, value given in /www/wwwroot/fawdlstty.com/wp-content/plugins/wp-syntax/wp-syntax.php on line 383

Warning: WP_Syntax::substituteToken(): Argument #1 ($match) must be passed by reference, value given in /www/wwwroot/fawdlstty.com/wp-content/plugins/wp-syntax/wp-syntax.php on line 383

下面几道问题是我整理的来源于网络的比较坑的问题,看起来题目很简单,但理清整个逻辑是非常不容易的,除非对底层知识有非常深刻的理解。你觉得你掌握了C++的基础么?这几道简单的问题,来试试吧?

1、下面哪些表达式必须加锁
A、a = 4; B、a = b; C、a++; D、a = b + 4;

2、下面代码double计算时间总比float小,请解释原因

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
float fs[] = { 1.1f, 2.2f, 3.3f, 4.4f, 5.5f };
double ds[] = { 1.1, 2.2, 3.3, 4.4, 5.5 };
DWORD tk = ::GetTickCount ();
float f = 0.0f;
for (int i = 0; i < 100000000; ++i) {
    f += fs[i % 5];
}
tk = ::GetTickCount () - tk;
std::cout << "float:" << f << "    " << tk << '\n';
tk = ::GetTickCount ();
double d = 0.0;
for (int i = 0; i < 100000000; ++i) {
    d += ds[i % 5];
}
tk = ::GetTickCount () - tk;
std::cout << "double:" << d << "    " << tk << '\n';

3、下面代码,257的二维数组计算时间总比256的二维数组计算时间短,请解释原因

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
char a[256][256], b[257][257]; // 全局变量
DWORD tk = ::GetTickCount ();
for (int i = 0; i < 256; ++i)
    for (int j = 0; j < 256; ++j)
        for (int k = 0; k < 10000; ++k)
            a[i][j] += '\x01';
tk = ::GetTickCount () - tk;
std::cout << "256:" << tk << '\n';
tk = ::GetTickCount ();
for (int i = 0; i < 257; ++i)
    for (int j = 0; j < 257; ++j)
        for (int k = 0; k < 10000; ++k)
            b[i][j] += '\x01';
tk = ::GetTickCount () - tk;
std::cout << "257:" << tk << '\n';

4、std::string中的COW优化与SSO优化分别对应怎样的输出?

1
2
3
4
5
6
7
std::string a = "hello";
std::string b = a;
std::cout << (a.c_str () == b.c_str () ? "true" : "false") << '\n';
char ch = b[0];
std::cout << (a.c_str () == b.c_str () ? "true" : "false") << '\n';
a = "";
std::cout << (a.c_str () == b.c_str () ? "true" : "false") << '\n';

继续阅读几道不太简单的C艹面试题

数据编解码及序列化技术

1、数据编解码:
数据编解码的最主要作用是将非标准格式的数据转为某种协议的标准格式,使这种协议可以传输、处理这种数据。举个最简单的例子,网页中很多一个网页里面嵌套了另一个网页或者汉字及特殊符号等,会转码为%23%67这类东西,这实际上是一种转码方式,js函数名叫encodeURI。由于url地址的特殊符号具有特殊含义,比如/?&及空格等,如果实际需要传输的参数带了这些符号,将导致格式错误。所以通常处理方式是将这些符号转为%XX格式,使特殊符号可以传递。这些方式都不能达到加密的效果,有经验的开发者都可以看一眼转码后的数据就能知道原始数据内容。
主要软件编码方式有三种:Base64、encodeURI(js函数)、encodeURIComponent(js函数)
Base64的主要特点是,编码后的数据只有0-9、a-z、A-Z、+/以及末尾可能的空格,长度为4的整数倍。编码后的数据大约比原数据长了1/3;
encodeURI是使用的最多的web请求的url转换,它将转码掉特殊字符,然后使请求用于HTTP协议的通讯;
encodeURIComponent稍微特殊一点,它除了编码特殊字符外,还将编码保留字符,编码的更完全;
另外有一个escape这个函数不太规范,因为它编码的不是UTF8编码,不便于不同设备间的通讯,所以它不再是主流编码方式;
除此之外还有数据压缩,也可以从某种层面上将其称之为编码及解码。
主要硬件编码方式就多了,Web技术刚出来就有一堆比如海明码等,然后选入标准的2G一种、3G一种、4G一种、5G两种、Wifi一种。硬件编码主要用于电磁信号传输过程中由于距离太长或者被干扰,导致传输后的信号乱码后,纠正错误的编码。著名的5G信道编码之争的三个编码为:LDPC、Polar和Turbo。硬件编码主要是硬件工程师写入硬件设备的编码方式,由于和软件是不同层次,所以软件中的纠错编码是相当的少了,可能最多的是二维码,就算有一小部分模糊也能正确的扫出结果。

2、序列化:
主要是软件通信以及存取时候使用,通常将一个内存结构模型序列化为一种顺序的结构便于存取及通信。使用的最多的两种结构为Json和XML。这两种结构主要用于简单的内存结构,对于复杂的结构就有些无力了。还有很多3D游戏的模型文件,比如MOD之类的,都属于3D模型的序列化数据。
序列化有多重要呢?举个简单的例子,以C++结构体为例,如果不使用序列化,那么将需要考虑内存pack长度、各种数据结构的长度比如32位或64位,主机字节序或网络字节序等等……如果是不同的语言,那么难度将更高。
Json数据主要长这样:{"a":"test1","b":"test2"}
XML数据主要长这样:<node><a>test1</a><b>test2</b></node>
由于这两类数据都太灵活,很可能有不标准的结构,所以出现一种技术:Schema,上面两种序列化结构都能用,主要用于数据结构的格式校验。这个东西在ISO(国际标准化组织)里面用的最多,涉及Json和XML的全是Schema。比如XML Schema详见XML Schema (一种XSL) 简单介绍,基于这两类数据结构的衍生版本也非常多,比如Json有Jsonb,Bson等,XML有XAML、HTML等。Json及衍生序列化格式渐渐偏向于储存内存数据结构,XML及衍生序列化格式渐渐偏向于存储UI布局。不过这也不是绝对的,在需要时完全可以反过来。

自制编程语言(一):EBNF表达式及Boost.Spirit的使用


Warning: WP_Syntax::substituteToken(): Argument #1 ($match) must be passed by reference, value given in /www/wwwroot/fawdlstty.com/wp-content/plugins/wp-syntax/wp-syntax.php on line 383

大家有没想过,编程语言是如何被编译或解释执行的?使用sscanf还是正则表达式?今天我们一起来揭开编程语言的神秘面纱。
程序语言按照层次,可以分为普通语言以及语法描述语言。普通语言包括出语法描述语言外的其他所有语言。简单的说,C++,C#,Java、XML、JSON等等都属于这类;语法描述语言是用来描述一门语言的语法,通常一门语法描述语言就能描述所有的语言的语法。这类语言最著名的叫EBNF(扩展巴克斯范式)。
这类语言的作用就是定义一门语言的语法。语法定义完毕后,语言会按照设计思路,生成AST(抽象语法树)。抽象语法树能非常方便的生成中间语言。通常,这个过程就叫编译器前端。相应的,编译器后端是指将中间语言优化,再解析为本地字节码的过程,叫编译器后端。一般来说,生成AST后,一个语言基本上就设计完成了,后端可以自己撸,也可以对接llvm等,很方便就能实现一门编程语言。
语言前端基于EBNF的工具有很多,看一些工具的名称就能看出来,比如yacc(又一个编译器的编译器)。C++的“准”标准库也提供了全套库,叫“Boost.Spirit”。后面的代码我将以这个工具来举例。
说及这个库,首先不得不谈这个EBNF表达式。下面我给大家展示一个用于描述四则运算的EBNF代码:

1
2
3
4
5
6
expr1 := oper1 ('+' | '-') (expr1 | oper1)
expr2 := oper2 ('*' | '/') (expr2 | oper2)
oper1 := expr2 | integer | block;
oper2 := integer | block;
block := '(' oper ')';
oper := expr1 | expr2 | integer | block;

继续阅读自制编程语言(一):EBNF表达式及Boost.Spirit的使用

计算机语言总结

为了方便执行各种各样不同的任务,描述不同的数据,先后诞生了成千上万种计算机语言。每种计算机语言都有自己的特点,分类方式有多种,按照不同的分类方式,语言的类型也不同。下面按照最常用的方式对语言分类进行大致介绍。其实这种分类方式并不完全准确,数据和代码并没有明显分界线,所以我就按照使用习惯对它们进行分类。

1、数据描述语言
数据描述语言分为基础数据描述语言以及文本标记语言,它们的界限也并不十分明确。

1.1、基础数据描述语言主要有:regex、css、xml、json、xaml
regex是正则表达式语法,几乎所有的通用编程语言都能使用正则表达式,但让人意外的是不同语言的regex语法不完全一样,有些有细微区别,需要仔细鉴别。另外正则表达式对于数字处理上较弱,另外语法教复杂,对于新手来说非常不友好。
css是样式标记语言的老大,主要存在于网页或视频中,网页文字或图像的样式、电影字幕通常都使用css进行描述,它的儿子们有qss(css的qt移植版)、less(语法比css先进,编译为css后通用)、sass(与less差不多)等等。
xml、json是使用最广泛的基础数据描述语言,可以描述几乎所有的数据类型,它俩语法结构上比较相似,但几乎不成对出现。xml年代较古老,所以基于它的语法变种更多;从语法上讲json更先进,能以更少的空间描述相同的数据量。它俩都有schema,用于保证格式准确。
xaml是xml的一个变种,主要用于描述.NET系列程序界面(WinForm除外的窗口程序)。描述界面的语言特乱,一个框架就是一种界面描述语言,比如.NET、安卓等,所以跨框架迁移是一件非常麻烦的事。
继续阅读计算机语言总结

C++:模拟键盘


Warning: WP_Syntax::substituteToken(): Argument #1 ($match) must be passed by reference, value given in /www/wwwroot/fawdlstty.com/wp-content/plugins/wp-syntax/wp-syntax.php on line 383

Warning: WP_Syntax::substituteToken(): Argument #1 ($match) must be passed by reference, value given in /www/wwwroot/fawdlstty.com/wp-content/plugins/wp-syntax/wp-syntax.php on line 383

模拟键盘是一个简单的话题,随便普通程序猿都能说出好多种方式。不同的方式应用于不同场合,总的来说分为三大类:
1、用户层模拟键盘
这个层来模拟是最方便的,但也是最容易无效的。总的来说有三种方式,第一种是直接往目标窗口发送按键消息;第二种是使用剪贴板复制待粘贴消息然后在目标窗口模拟Ctrl+V;第三种是用户层触发按键事件
这儿贴一个模拟输入一个字节的函数:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
bool input (char ch)
{
    DWORD KeyScan = ::OemKeyScan (ch);
    bool bShift = !!(KeyScan >> 16);
    UINT uCode = KeyScan & 0xFFFF;
    UINT uScanCode = ::MapVirtualKey (uCode, MAPVK_VK_TO_VSC);
    UINT uShiftScanCode = ::MapVirtualKey (VK_SHIFT, MAPVK_VK_TO_VSC);
    if (bShift)
    {
        ::keybd_event (VK_SHIFT, (BYTE) uShiftScanCode, KEYEVENTF_EXTENDEDKEY, 0);
        std::this_thread::sleep_for (std::chrono::milliseconds (20));
    }
    ::keybd_event (uCode, (BYTE) uScanCode, KEYEVENTF_EXTENDEDKEY, 0);
    std::this_thread::sleep_for (std::chrono::milliseconds (20));
    ::keybd_event (uCode, (BYTE) uScanCode, KEYEVENTF_KEYUP, 0);
    std::this_thread::sleep_for (std::chrono::milliseconds (20));
    if (bShift)
    {
        ::keybd_event (VK_SHIFT, (BYTE) uShiftScanCode, KEYEVENTF_KEYUP, 0);
        std::this_thread::sleep_for (std::chrono::milliseconds (20));
    }
    return true;
}

这个函数很可能在一些有简单安全措施的软件里面失效。软件屏蔽键盘模拟按键一般是使用钩子,那么可以dll注入然后反钩子,挂钩SetWindowsHookEx即可。注意反钩子必须和目标在同一个进程里,否则M$的Copy-On-Write会让反钩子失效。示例代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
BOOL func_unhook (LPVOID func_ptr, WORD param_num, DWORD ret_val)
{
    // mov eax, 12345678h
    // ret 0004h
    BYTE bBuf [] = { '\xB8', '\x00', '\x00', '\x00', '\x00', '\xC2', '\x00', '\x00' };
    *(DWORD*) (&bBuf [1]) = ret_val;
    *(WORD*) (&bBuf [6]) = param_num * 4;
    DWORD dw = 0, dw2 = 0;
    BOOL bRet = ::VirtualProtect (func_ptr, 8, PAGE_EXECUTE_READWRITE, &dw);
    if (!bRet)
        return FALSE;
    ::memcpy (func_ptr, bBuf, 8);
    if (func_ptr != ::VirtualProtect)
        ::VirtualProtect (func_ptr, 8, dw, &dw2);
    return TRUE;
}
 
func_unhook (::SetWindowsHookA, 2, 1);
func_unhook (::SetWindowsHookW, 2, 1);
func_unhook (::UnhookWindowsHook, 2, 1);
func_unhook (::SetWindowsHookExA, 4, 1);
func_unhook (::SetWindowsHookExW, 4, 1);
func_unhook (::UnhookWindowsHookEx, 1, 1);

反钩子后模拟键盘事件差不多可以过绝大部分弱保护的安全措施了,不过这也不完全总是灵的。如果以上方法都不行,可以试试其他方案。
继续阅读C++:模拟键盘

加密介绍


加密通讯总共有四大类,签名算法、对称加密算法、非对称加密算法与量子加密技术,下面分别对几大类算法进行介绍

1、签名算法

代表算法:md5、sha1、crc32
有的地方叫签名算法,有的地方叫哈希算法,有的地方叫指纹算法、数字摘要等等,都代表一个意思。在解释这个之前我先说说“哈希”的意思。哈希也叫散列,意思是,将一串任意长度数据经过某种特定的算法转为固定长度的输出。比如10000个字符将其转为20个字符,这样的算法就叫哈希算法。20个字符虽然容易重复,但随着字符的增多,重复概率将会指数级下降,但通过20个字符很容易就能确定10000个字符的范围,比如对N组10000个字符进行对比,这样做的好处是通过比对20个字符的签名就能筛选出绝大部分数据,剩下的数据经过简单的比对就行了,非常省时省力。
签名算法有什么优势呢?
(1)、校验数据准确性。数据的准确性在通信领域有着举足轻重的作用。纠错编码比如海明码等等可以使错误编码在一定范围内时对其进行纠正,但一旦超过阈值,就算使用编码纠错,最终得出的结果也是错误的。这时候就需要哈希算法对数据进行签名,以确保数据准确性。
(2)、对数据进行归纳整理。比如哈希表就基于哈希算法,将一堆数据均匀分布在哈希结构中,使得寻找某个数据非常简单方便。
(3)、做验证之用。比如用户登录界面,在客户端经过哈希算法计算之后,服务器端只需要验证结果是不是符合就可以了,不用传输整个密码。就算黑客在中途截获数据包也无法推算出密码(暂时不考虑彩虹表等玩意儿,哈希算法可以定义为信息不可逆)。
优势挺多,但劣势也比较严重:
(1)、数据不可逆。也就是说,生成的签名几乎不可能再推导回原数据。
(2)、相对来说不那么安全。彩虹表有一定概率可以在短时间破解MD5加密的密码,王小云研究出来的哈希碰撞也能在一定程度上削弱MD5的安全性。
继续阅读加密介绍

C#:HTTP客户端与服务器的实现

HTTP协议算是使用最广泛的Web协议了,主要面向基于B/S的实现。对于很多语言比如PHP或者javascript等Web语言,HTTP协议使用上甚至比TCP协议更简单。但这协议对普通的系统语言程序员就不太友好了。C#这种语言嘛,支持上还行,但并没达到特别优秀的效果,比如发起一个POST请求至少需要十行代码以上。这个库是我基于对C#语言封装的协议的了解,在此基础上新增的一个更加方便开发的库。源码在 https://github.com/fawdlstty/hanHttpLib 查看或下载。因为可能更新频繁,所以源码我就不在文章里面贴了。
目前这个库不算特别优秀,目前只支持基础的调用,结构也非常简单,但它能力可不差,用来给小伙伴学习也是极好的。我将不定期更新它,力争将其做到完美。
先说说HTTP的客户端。HTTP客户端主要是浏览器用来请求Web服务器上的资源用的,最广泛的就是浏览器了。每个浏览器都有一套HTTP客户端实现方式,请求完成资源后在界面上显示。但随着时代的发展,HTTP协议有着基本化的趋势。因为使用HTTPS进行通讯可以使用非常小的代价就能非常安全的加密通讯数据。另外HTTP客户端也不止是做浏览器了,除此之外最大的用处是做爬虫。比如很多零散的数据分布在零散的Web服务器上,这时就可以使用爬虫技术将零散的数据归纳起来。更加厉害的就是,再结合人工智能技术,爬取数据之后使用人工智能进行分析,差不多就能成就改变世界的武器了。
继续阅读C#:HTTP客户端与服务器的实现

XML Schema (一种XSL) 简单介绍


Warning: WP_Syntax::substituteToken(): Argument #1 ($match) must be passed by reference, value given in /www/wwwroot/fawdlstty.com/wp-content/plugins/wp-syntax/wp-syntax.php on line 383

简单介绍下,XSL(eXtensible Stylesheet Language,扩展样式语言)是描述XML文档结构规则的语言,XML Schema是其中使用最广泛的一种。也就是说,你可以使用XML Schema限制某一类XML完全按照你的规范来编写代码。
比如,你想让某个XML文件以MainNode作为根节点名称,但可能有main_node作为根节点名称的,符合XML文件规范,但你就是识别不了它。这就比较尴尬了。那么,使用XSL验证机制,就可以保证XML文件100%符合你的规范。
也许做过HTML4的前端知道,DOCTYPE需要指向一个dtd文件,比如

1
< !DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">

DTD也是XSL的一种,但因为对扩展的支持性不好,在HTML5中被弃用,之后DTD的使用将会越来越少。对于基于HTML4的网页代码都使用DTD作为规则文件对网页文档进行规范。HTML是从XML基础上演化而来的,当然也属于XML标准规范的一部分。使用标准XML也可以使用dtd进行描述。dtd此处不细说,此处说说XML Schema。
继续阅读XML Schema (一种XSL) 简单介绍

C#:扩展方法集合类


Warning: WP_Syntax::substituteToken(): Argument #1 ($match) must be passed by reference, value given in /www/wwwroot/fawdlstty.com/wp-content/plugins/wp-syntax/wp-syntax.php on line 383

Warning: WP_Syntax::substituteToken(): Argument #1 ($match) must be passed by reference, value given in /www/wwwroot/fawdlstty.com/wp-content/plugins/wp-syntax/wp-syntax.php on line 383

Warning: WP_Syntax::substituteToken(): Argument #1 ($match) must be passed by reference, value given in /www/wwwroot/fawdlstty.com/wp-content/plugins/wp-syntax/wp-syntax.php on line 383

扩展方法集合类在C#中是一种特别的语法糖,可以在开发中极大简化代码的编写。主要功能是在系统类中创建自定义方法。比如字符串转数字,每次都得Convert.ToInt32 (str),从语法上来说就太不简洁了。下面我一步一步讲解扩展方法集合类的编写。首先创建一个静态类,类名必须为ExtensionMethods。示例代码如下

1
2
3
public static class ExtensionMethods {
    //...
}

所有扩展方法都需要放在此类中进行实现。首先创建一个静态函数,第一个参数使用this描述符,表示基于哪个类提供扩展方法

1
2
3
public static Int32 toInt32 (this object o) {
    return Convert.ToInt32 (o);
}

此函数代表在object这个类基础上新增一个扩展方法,无参数,返回值为Int32类型。示例调用代码如下:

1
int t = "123".toInt32();

下面我提供一个我自用的扩展方法集合类供参阅
继续阅读C#:扩展方法集合类

C#:使用OpenXML读写Excel文档

对于Office文件的读写操作始终是一个比较麻烦的事情,最近做了一个Office操作的小程序,写一个文章用于备忘。
Office文件读写方法挺多,最好的方法自然是OpenXML无疑,但这库用起来还是比较麻烦。我在此做一个Excel读写小例子,其他Office类型同理。
对于OpenXML这库,首先需要知道OpenXML是什么。新建一个Excel文件,另存为a.xlsx,然后将其重命名为a.rar,解压,然后看到一大堆xml文件。不用怀疑,这就是xml标准。对于Office文件的读的访问来说,如果对OpenXML不熟,并且使用其他语言不方便调用C#的库,那么,手工解压然后找找数据的位置,然后手工解析,是最快捷的方式。当然,在可以使用OpenXML的场合,我个人还是推荐使用OpenXML。
OpenXML2.5下载地址(主体工具摘自微软官网,扩展摘自网络):https://pan.baidu.com/s/1slDa0Jr,提取码98dd
这个工具只有英语版的,但由于操作过于简单,所以对于英语小白也完全无压力。
继续阅读C#:使用OpenXML读写Excel文档

GDI/GDI+用法总结

GDI是Graphics Device Interface的缩写,含义是图形设备接口,它的主要任务是负责系统与绘图程序之间的信息交换,处理所有Windows程序的图形输出。GDI+是在GDI基础上提供的一层更高级的图像绘制抽象接口,语义更明确调用更方便。它们都支持向图片对象或者窗口上输出图形。在窗口上绘图时它们都使用窗口提供的HDC句柄实现绘制;在图片对象绘制图像时,GDI+支持直接传入图片对象实现对图片的绘制,GDI需要先创建一个与图片兼容的HDC,再将HDC与被绘制图片进行绑定,然后才能在图片上进行绘制。
它们在用法上相似,区别主要有以下几个方面:

  • GDI不支持透明图片处理(AlphaBlend只能混合颜色,透明得由第三方库支持)
  • GDI不支持反锯齿(对于图片绘制线条、图像或拉伸等处理时,可能出现白色锯齿形状图像,影响美观)
  • GDI对于图片颜色处理具有很大优势。GDI+慢的一比
  • GDI是以C的接口形式提供接口,GDI+是以C艹和托管类的方式提供接口
  • 使用GDI+的程序在初始化后、程序关闭前需调用GDI+初始化、释放的代码
  • 从层次结构上来说,GDI+更好用

继续阅读GDI/GDI+用法总结