博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
无服务器端的UDP群聊功能剖析(新增QQ表情功能)
阅读量:5122 次
发布时间:2019-06-13

本文共 5204 字,大约阅读时间需要 17 分钟。

上一篇中,我们讲解的是这个小软件的重构:使用可二进制化的Model类代替拼接字符串的方式,这样做的好处是使得代码可读性更强,更容易维护,当然,也更符合面向对象的思想:处处皆对象。

效果图览

在这一篇中,主要涉及的内容是新增的QQ表情功能。这个功能的设计牵涉到了正则表达式,我们先来看看截图:

弹出选择表情面板:

3个用户的具体聊天内容:

看到GIF图像在跳动

其中有一个用户已经下线

下面是设计的准备工作:

首先,我们需要一个能够支持图片输入的TextBox,这里我选择了这篇文章中介绍的控件:,这个控件通过QQ自带的ImageOle.dll ActiveX控件实现插入动画表情,所以说在使用之前,需要先利用regsvr32.exe命令注册这个dll,具体命令为:regsvr32.exe ImageOle.dll。

当然做完了之后,直接在VS中添加COM引用即可。

其次,弹出选择表情面板是必不可少的,这里我们利用一个二维的PictureBox数组来存储GIF表情动画,并放置到Panel容器中:

View Code
private void LoadingEmotion()        {            PictureBox[,] picList = new PictureBox[5,10];            for (int i = 0; i < 5; i++)            {                for (int j = 0; j < 10; j++)                {                    int emotionSequenceCount = i * 10 + j;                    picList[i,j] = new PictureBox();                    picList[i, j].Height = picList[i, j].Width = 24;                    picList[i, j].Image = Image.FromFile(".\\Face2\\" + emotionSequenceCount + ".gif");                    picList[i, j].Top = i * 24;                    picList[i, j].Left = j * 24;                    picList[i, j].Tag = "#(" + emotionSequenceCount + ")#";                    picList[i, j].Parent = panImg;                    picList[i, j].Click += new EventHandler((sender, e) =>                     {                         this.rSendContent.AppendText("#(" + emotionSequenceCount + ")#");                        emotionFlag = false;                        this.panImg.Visible = emotionFlag;                    });                    panImg.Controls.Add(picList[i,j]);                }            }        }

上面的GIF动画位置是通过对象的Top和Left方法来控制的,非常的方便;同时,把每个GIF表情的代码放到了Tag中进行保存,以方便调用,并且利用了匿名方法来注册PictureBox的点击事件。每次点击图标,会自动在发送文本框中生成类似#(0)#或者#(1)#等的代码,这些代码代表了是哪个表情,比如#(0)#就代表了第一行一列的表情,#(1)#代表了第1行2列的表情,依次类推。

最后就是输入的时候,如何进行表情匹配了。比如用户输入了如下的内容:

Hello Shi#(0)#, How are you today?#(1)##(2)#

其中#(0)#,#(1)#,#(2)#是由我们通过点击表情输入进去的,那么发送到对方的机器上的时候,就需要被解析成

Hello Shi, How are you today?

,该如何进行呢? 

其实,我的做法就是在这句话的头部和尾部加上#(S)#和#(E)#标记以区别头尾,

#(S)#Hello Shi#(0)#, How are you today?#(1)##(2)##(E)#

然后,通过如下的正则来进行分段匹配,其中,?=的作用主要是负向前查找,但是不包含本身。具体内容请参见

Regex regex = new Regex(@"(#\([0-9|S|E]+\)#).*?(?=#\([0-9|S|E]+\)#)", RegexOptions.Singleline | RegexOptions.IgnoreCase);

那么得到的结果就被分成了几段:

#(S)#Hello Shi

#(0)#, How are you today?

#(1)#

#(2)#

#(S)#

这就是分成的5段,然后观察这5段就发现,每段开始都是一个图片的标记(#(S)#和#(E)#除外,那是开始结束标志),然后跟着的是一段文本或者是什么都不跟。

那么这样的话,我们再继续对这些段进行区分:

Regex regexImage = new Regex(@"(#\([0-9|S|E]+\)#)", RegexOptions.Singleline | RegexOptions.IgnoreCase);Regex regexPlainText = new Regex(@"(?<=(#\([0-9|S|E]+\)#)).*", RegexOptions.Singleline | RegexOptions.IgnoreCase);

其中

regexImage主要匹配其中的图片标记,比如#(0)#,

regexPlainText主要匹配其中的文本或者空白字段,拿第二段来说,匹配的结果就是:

#(0)#

, How are you today?

这样就将表情和文本完全分离出来了,最后直接将表情文字替换为真实图片,并添加到信息窗体中:

if (!String.IsNullOrEmpty(matchImage.Value)){    if (!matchImage.Value.Contains("#(S)#"))    {        rAllContent.InsertImageUseImageOle(".\\Face2\\" + matchImage.Value.Replace("#(", string.Empty).Replace(")#", string.Empty) + ".gif");    }    rAllContent.AppendText(matchPlainText.Value);}

这里补充一下本新增功能中用到的正则知识:

  • 向前查找

从语法上看,一个向前查找模式其实就是一个以?=开头的子表达式,需要匹配的文本跟在=的后面。

比如我们需要知道一些URL用的是http还是https,则可以利用向前查找:

http://www.cnblogs.com

正则匹配为:.+(?=:)

结果为: http

如果利用.+(:) ,则为 http:

  • 向后查找

也就是查找出现在被匹配文本之前的字符(但不消费它),操作符是?<=

文本为:ABC0:  $12.56

匹配为:  (?<=\$)[0-9.]+

如果不佳?<=,结果为$12.56,反之为12.56

  • 向前向后查找集合

例如以下文本:

<head>

<title>Ben Forta’s HomePage</title>

</head>

这里我们如果想得到<title>与</title>标签内的内容,但是不包含<title>和</title>标签,如果不利用向前向后查找的话,将显得异常麻烦。利用向前向后匹配,只需要一个正则表达式就可以搞定:

正则匹配为:(?<=<title>).*?(?=</title>)

刚刚说道的向前向后查找,说准确点应该叫做正向前查找和正向后查找。当然,这里还存在这负向前查找和负向后查找:

操作符

说明

(?=)

正向前查找

(?!)

负向前查找

(?<=)

正向后查找

(?<!)

负向后查找

  • 负向后查找

文本为:I paid $30 for 100 apples.

匹配为:\b(?<!\$)\d+\b

这个的意思是查找不带有$符号的数字,这里的匹配结果是100

当然,负向前查找和这个使用方式类似,暂略。

全部代码如下:

View Code
public static void AddContent(string text,ChatRichTextBox rAllContent)        {            //解析发送的内容,实现表情匹配。                string sendText = "#(S)#" + text + "#(E)#";                Regex regex = new Regex(@"(#\([0-9|S|E]+\)#).*?(?=#\([0-9|S|E]+\)#)", RegexOptions.Singleline | RegexOptions.IgnoreCase);                Regex regexImage = new Regex(@"(#\([0-9|S|E]+\)#)", RegexOptions.Singleline | RegexOptions.IgnoreCase);                Regex regexPlainText = new Regex(@"(?<=(#\([0-9|S|E]+\)#)).*", RegexOptions.Singleline | RegexOptions.IgnoreCase);                MatchCollection matches = regex.Matches(sendText);                foreach (Match match in matches)                {                    string matchedValue = match.Value;                    Match matchImage = regexImage.Match(matchedValue);                    Match matchPlainText = regexPlainText.Match(matchedValue);                    if (!String.IsNullOrEmpty(matchImage.Value))                    {                        if (!matchImage.Value.Contains("#(S)#"))                        {                            rAllContent.InsertImageUseImageOle(".\\Face2\\" + matchImage.Value.Replace("#(", string.Empty).Replace(")#", string.Empty) + ".gif");                        }                        rAllContent.AppendText(matchPlainText.Value);                    }                }        }

源码下载

转载于:https://www.cnblogs.com/scy251147/archive/2012/10/31/2748840.html

你可能感兴趣的文章
在Server上得到数据组装成HTML后导出到Excel。两种方法。
查看>>
浅谈项目需求变更管理
查看>>
经典算法系列一-快速排序
查看>>
设置java web工程中默认访问首页的几种方式
查看>>
ASP.NET MVC 拓展ViewResult实现word文档下载
查看>>
8、RDD持久化
查看>>
第二次团队冲刺--2
查看>>
VMware Tools安装
查看>>
Linux上架设boost的安装及配置过程
查看>>
[转载]加密算法库Crypto——nodejs中间件系列
查看>>
zoj 2286 Sum of Divisors
查看>>
OO5~7次作业总结
查看>>
使用Xshell密钥认证机制远程登录Linux
查看>>
OpenCV之响应鼠标(三):响应鼠标信息
查看>>
Android 画图之 Matrix(一)
查看>>
List<T>列表通用过滤模块设计
查看>>
【模板】最小生成树
查看>>
设计模式之结构型模式
查看>>
poj2569
查看>>
使用pygal_maps_world.i18n中数据画各大洲地图
查看>>