学习新的图形库时有个习惯,就是将罗云彬先生的一个闹钟小程序移植到这个图形库下。
原理也很简单,就是通过图片以第一个点的颜色为透明色创建窗体,但Gtk#创建就有些费劲了,主要是因为Gtk#的原生库Gtk+为C写的,有很多指针操作,在C#下有些麻烦,查了好多资料才写了下面几句代码。
然后用Cairo绘制时钟的时间,也是一张图片,然后按时、分、秒截取图形的一部分绘制到不透明窗体上。
在Windows 7、Ubunu 11.04下运行,效果相同。
见代码:
2 using System.Collections.Generic;
3 using System.Linq;
4 using System.Text;
5 using Cairo;
6 using Gdk;
7 using Gtk;
8 using Window = Gtk.Window;
9 using WindowType = Gtk.WindowType;
10
11 namespace GClock
12 {
13 public class MainWindow : Window
14 {
15 bool timer = true;
16 readonly Pixbuf _pixbufNumber; // 时钟时间图片,为0-9十个数字和:以及空白部分组成
17
18 public MainWindow()
19 : base(WindowType.Toplevel)
20 {
21 _pixbufNumber = new Pixbuf(GetType().Assembly, "GClock.Bmp.Number06.bmp");
22
23 // 从嵌入资源中获取Pixbuf
24 var pixbufO = new Pixbuf(GetType().Assembly, "GClock.Bmp.Clock03.bmp");
25 // 取Pixbuf第一个点的rgb值
26 byte r, g, b;
27 unsafe {
28 // Pixbuf的Pixels未记录图片每个点信息值
29 // 取(x,y)的rgb值,以下代码中x,y均为0
30 // var p = (byte*)pixbufO.Pixels.ToPointer() + y * pixbufO.Rowstride + x * pixbufO.NChannels;
31 var p = pixbufO.Pixels.ToPointer();
32 r = *(byte*)p;
33 g = *((byte*)p + 1);
34 b = *((byte*)p + 2);
35 }
36 // 依据第一个点的rgb值增加透明通道
37 var pixbuf = pixbufO.AddAlpha(true, r, g, b);
38
39 Decorated = false; // 不设置窗体边框
40 AppPaintable = true; // 应用程序绘制界面
41 SetDefaultIconFromFile("Clock00.ico");
42 //SetDefaultSize(pixbuf.Width, pixbuf.Height);
43 SetDefaultSize(500, 500);
44 SetPosition(WindowPosition.Center);
45
46 Realize(); // 创建Window的资源,若不调用则this.GdkWindow为null
47
48 DeleteEvent += delegate { Application.Quit(); };
49 // 添加鼠标点击事件
50 AddEvents((int)EventMask.ButtonPressMask);
51 GLib.Timeout.Add(100, OnTimer); // 时间事件
52
53 Pixmap pixmap, pixmask;
54 // 依据Pixbuf创建Pixmap以及对应的蒙板Pixmap
55 pixbuf.RenderPixmapAndMask(out pixmap, out pixmask, 128);
56 ShapeCombineMask(pixmask, 0, 0);
57 GdkWindow.SetBackPixmap(pixmap, false);
58
59 ShowAll();
60 }
61
62 protected override bool OnButtonPressEvent(EventButton evnt)
63 {
64 // 鼠标左键点击移动窗体
65 if (evnt.Type == EventType.ButtonPress && evnt.Button == 1) {
66 BeginMoveDrag((int)evnt.Button, (int)evnt.XRoot, (int)evnt.YRoot, evnt.Time);
67 }
68 return base.OnButtonPressEvent(evnt);
69 }
70
71 protected override bool OnExposeEvent(EventExpose evnt)
72 {
73 base.OnExposeEvent(evnt);
74
75 using (var cr = CairoHelper.Create(evnt.Window)) {
76 var curTime = DateTime.Now;
77 int tenDigit, entriesDigit;
78
79 var x = 25;
80 const int y = 80;
81 cr.Save();
82 // 绘制左侧空白
83 cr.Translate(x, y);
84 CairoHelper.SetSourcePixbuf(cr, _pixbufNumber, -(15 * 10 + 8), 0);
85 cr.Rectangle(0, 0, 2, 25);
86 cr.Clip();
87 cr.Paint();
88
89
90 // HH
91 DecToBcd(curTime.Hour, out tenDigit, out entriesDigit);
92 x += 2;
93 cr.Restore();
94 cr.Save();
95 cr.Translate(x, y);
96 CairoHelper.SetSourcePixbuf(cr, _pixbufNumber, -15 * tenDigit, 0);
97 cr.Rectangle(0, 0, 15, 25);
98 cr.Clip();
99 cr.Paint();
100
101 x += 15;
102 cr.Restore();
103 cr.Save();
104 cr.Translate(x, y);
105 CairoHelper.SetSourcePixbuf(cr, _pixbufNumber, -15 * entriesDigit, 0);
106 cr.Rectangle(0, 0, 15, 25);
107 cr.Clip();
108 cr.Paint();
109
110 // :
111 x += 15;
112 cr.Restore();
113 cr.Save();
114 cr.Translate(x, y);
115 CairoHelper.SetSourcePixbuf(cr, _pixbufNumber, -15 * 10, 0);
116 cr.Rectangle(0, 0, 8, 25);
117 cr.Clip();
118 cr.Paint();
119
120 // MM
121 DecToBcd(curTime.Minute, out tenDigit, out entriesDigit);
122 x += 8;
123 cr.Restore();
124 cr.Save();
125 cr.Translate(x, y);
126 CairoHelper.SetSourcePixbuf(cr, _pixbufNumber, -15 * tenDigit, 0);
127 cr.Rectangle(0, 0, 15, 25);
128 cr.Clip();
129 cr.Paint();
130
131 x += 15;
132 cr.Restore();
133 cr.Save();
134 cr.Translate(x, y);
135 CairoHelper.SetSourcePixbuf(cr, _pixbufNumber, -15 * entriesDigit, 0);
136 cr.Rectangle(0, 0, 15, 25);
137 cr.Clip();
138 cr.Paint();
139
140 // :
141 x += 15;
142 cr.Restore();
143 cr.Save();
144 cr.Translate(x, y);
145 CairoHelper.SetSourcePixbuf(cr, _pixbufNumber, -15 * 10, 0);
146 cr.Rectangle(0, 0, 8, 25);
147 cr.Clip();
148 cr.Paint();
149
150 // SS
151 DecToBcd(curTime.Second, out tenDigit, out entriesDigit);
152 x += 8;
153 cr.Restore();
154 cr.Save();
155 cr.Translate(x, y);
156 CairoHelper.SetSourcePixbuf(cr, _pixbufNumber, -15 * tenDigit, 0);
157 cr.Rectangle(0, 0, 15, 25);
158 cr.Clip();
159 cr.Paint();
160
161 x += 15;
162 cr.Restore();
163 cr.Save();
164 cr.Translate(x, y);
165 CairoHelper.SetSourcePixbuf(cr, _pixbufNumber, -15 * entriesDigit, 0);
166 cr.Rectangle(0, 0, 15, 25);
167 cr.Clip();
168 cr.Paint();
169
170 // 绘制右侧空白
171 x += 15;
172 cr.Restore();
173 cr.Save();
174 cr.Translate(x, y);
175 CairoHelper.SetSourcePixbuf(cr, _pixbufNumber, -(15 * 10 + 8), 0);
176 cr.Rectangle(0, 0, 2, 25);
177 cr.Clip();
178 cr.Paint();
179
180 cr.Restore();
181 }
182
183 return true;
184 }
185
186 bool OnTimer()
187 {
188 if (!timer) return false;
189
190 QueueDraw();
191 return true;
192 }
193
194 // 依据两位数获取该数字的十位、个位数
195 private static void DecToBcd(int decimalNum, out int tenDigit, out int entriesDigit)
196 {
197 tenDigit = decimalNum / 10;
198 entriesDigit = decimalNum % 10;
199 }
200 }
201 }
主窗体背景图片:
时钟数字图片:
程序运行图片:
问题:请写一个方法,两个参数,来判断第二个参数在第一个参数中出现了几次
1)用正则表达式,非常容易得出结果:
static int FindCountInStringByRegex(string str, string pattern)
{
if (string.IsNullOrEmpty(str) || string.IsNullOrEmpty(pattern)) {
return 0;
}
if (pattern.Length > str.Length) {
return 0;
}
return Regex.Matches(str, pattern).Count;
}
很简洁,但问题是,若str为"aaaa"、pattern为"aa",上面的方法结果为2,而实际要得到的应该是3。 这个用正则表达式还真就不知道怎么实现了。
2)无奈还是用最基本的方法实现吧:
static int FindCountInString(string str, string pattern)
{
if (string.IsNullOrEmpty(str) || string.IsNullOrEmpty(pattern)) {
return 0;
}
if (pattern.Length > str.Length) {
return 0;
}
int count = 0, index = 0;
while (index < str.Length) {
index = str.IndexOf(pattern, index);
if (index >= 0) count++;
else break;
index++;
}
return count;
}
问题:一个日志文件,记录形如访问时间、url这样的记录,每行一条记录。请按每个url访问的次数进行逆排序
一、很简单的一个问题,首先用很普通的思维来解决:
1)先模拟来生成一个类似的文件:
private static void GenerateUrlLog()
{
string[] urlList = new string[7];
urlList[0] = "http://www.sohu.com";
urlList[1] = "http://www.baidu.com";
urlList[2] = "http://www.google.com";
urlList[3] = "http://www.sina.com.cn";
urlList[4] = "http://www.cnblogs.com";
urlList[5] = "http://www.yahoo.com";
urlList[6] = "http://www.csdn.net";
StreamWriter writer = new StreamWriter("url.data");
Random random = new Random(20);
int i = 0;
while (i < 40) {
int index = random.Next(7);
DateTime now = DateTime.Now;
Console.WriteLine(string.Format("{0}\t{1}", now, urlList[index]));
writer.WriteLine(string.Format("{0}\t{1}", now, urlList[index]));
i++;
Thread.Sleep(2000);
}
writer.Flush();
writer.Dispose();
Console.WriteLine("-------------------------------------");
}
2)顺序遍历文件,用Dictionary<string, int>来记录每个url的访问次数:
private static void ParseUrlLog()
{
StreamReader reader = new StreamReader("url.data");
string urlLog = null;
Dictionary<string, int> dicLogInfo = new Dictionary<string, int>();
while ((urlLog = reader.ReadLine()) != null) {
string[] logProperty = urlLog.Split('\t');
if (dicLogInfo.ContainsKey(logProperty[1])) {
dicLogInfo[logProperty[1]] = dicLogInfo[logProperty[1]] + 1;
} else {
dicLogInfo[logProperty[1]] = 1;
}
}
reader.Dispose();
Console.WriteLine("-------------------------------------");
int urlLogCount = dicLogInfo.Count;
for (int i = 0; i < urlLogCount; i++) {
int max = 0;
string maxUrl = "";
foreach (string key in dicLogInfo.Keys) {
if (max < dicLogInfo[key]) {
max = dicLogInfo[key];
maxUrl = key;
}
}
dicLogInfo.Remove(maxUrl);
Console.WriteLine(string.Format("{0}\t{1}", maxUrl, max));
}
Console.WriteLine(result.Count());
Console.WriteLine("-------------------------------------");
}
二、既然都.net 4.0了,就用新的方式来处理一下:
1)还是生成日志文件,改用.net 4的并行方式:
private static void GenerateUrlLog()
{
string[] urlList = new string[7];
urlList[0] = "http://www.sohu.com";
urlList[1] = "http://www.baidu.com";
urlList[2] = "http://www.google.com";
urlList[3] = "http://www.sina.com.cn";
urlList[4] = "http://www.cnblogs.com";
urlList[5] = "http://www.yahoo.com";
urlList[6] = "http://www.csdn.net";
StreamWriter writer = new StreamWriter("url.data");
Random random = new Random(20);
Parallel.For(0, 40, j => {
int index = random.Next(7);
DateTime now = DateTime.Now;
Console.WriteLine(string.Format("{0}\t{1}", now, urlList[index]));
writer.WriteLine(string.Format("{0}\t{1}", now, urlList[index]));
Thread.Sleep(2000);
});
writer.Flush();
writer.Dispose();
Console.WriteLine("-------------------------------------");
}
生成文件速度比之前的方式快,毕竟并行嘛,当然之前的方式也可以改用多线程来做,类似。
2)用LinQ来查询数据:
private static void ParseUrlLog()
{
StreamReader reader = new StreamReader("url.data");
string urlLog = null;
Dictionary<string, int> dicLogInfo = new Dictionary<string, int>();
while ((urlLog = reader.ReadLine()) != null) {
string[] logProperty = urlLog.Split('\t');
PrintParseLog(logProperty[0], logProperty[2]);
if (dicLogInfo.ContainsKey(logProperty[2])) {
dicLogInfo[logProperty[2]] = dicLogInfo[logProperty[2]] + 1;
} else {
dicLogInfo[logProperty[2]] = 1;
}
}
reader.Dispose();
Console.WriteLine("-------------------------------------");
var result = from url in dicLogInfo orderby url.Value descending select url;
foreach (var item in result) {
Console.WriteLine("{0}\t{1}", item.Key, item.Value);
}
Console.WriteLine(result.Count());
Console.WriteLine("-------------------------------------");
}
查数据部分少了很多代码,最重要的是打印出url访问次数后,Dictionary<string,int>还可以使用,不像上面的方式打印完后Dictionary也就清空了,而且result也可以多次使用。
3)既然LinQ是查数据的,为什么还要构建一个Dictionary,直接查不可以吗?可以:
private static void ParseUrlLog2()
{
StreamReader reader = new StreamReader("url.data");
List<string> urlLog = new List<string>();
string tempUrl = null;
while ((tempUrl = reader.ReadLine()) != null) {
urlLog.Add(tempUrl);
}
reader.Dispose();
Console.WriteLine("-------------------------------------");
var result = urlLog.GroupBy(url => url.Split('\t')[1]).OrderByDescending(s => s.Count());
foreach (var item in result) {
Console.WriteLine("{0}\t{1}", item.Key, item.Count());
}
Console.WriteLine(result.Count());
Console.WriteLine("-------------------------------------");
}
周五快下班时,现场测试发邮件说中午发过去的补丁有bug,并标注了操作的步骤。
看完,我直接给本部测试大姐发腾讯通:“大姐,那边的大姐比你测得还变态...”,大姐有些不乐意:“你是说我...”
直接跑到大姐工位:明天反正我也得来,这问题明天再说吧。
大姐:可我不想来,今天加会儿班吧。
Boss(部门经理)也威胁我:要不你下周出差去现场得了。
我:我出差也行,这边的bug得有人给我清,别我回来上百个等我清。
Boss:你可以连远程清。
...
不废话了,改吧。改完发现按现场测试的测法是没完没了的问题(靠,不定个规则,让我处理所有情况啊,而且也不看客户到底想要什么样的),后来跟大姐和Boss说了一下,就告诉那边必须按规则输入。然后出补丁,结果平台给的出补丁工具巨麻烦,用了一个多小时出了补丁,又发现跟以前出的补丁冲突(新出的版本比旧的反而早),又重出...
好不容易九点半发出去了,跟Boss说:明天不想来了
Boss来了句:你不来怎么行
...
公司所在地巨偏僻,打不着车,搭Boss的车去城铁。
路上Boss说:可能现在的房价涨得太狠了,我发现你们这帮80后不如以前人踏实,X副总以前当老师的,住宿舍住了10年,后来才出来....
我:我承认,我们也想在领域里有建树,可这房价让我们看不到希望。
另一个同事:生活都有困难啊
...
无语啊...
Boss其实应该感到高兴,毕竟我们这些人都是比较老实的,不像其他部门,要么吵吵加薪要么离职的。
天天一堆bug,被人说成代码质量不高,拜托,大部分都是我维护的其他人的,我自己从头做到尾的bug又有多少?说没自测,看看一天那么多事,还老催着提交单元,哪有时间自测?如果我自测了,延误了提交单元,责任不还在我这儿?从来不考虑是不是决策层的问题。
还有需求、设计这些岗位,一天到晚琢磨客户可能需要的是什么样吧,为什么不去现场问问?闭门造车做出的需求、设计,还指望程序员做出让客户满意的产品?整个项目组已经背离了公司创立当年的宗旨了...
周六大早上醒来发现正在下雨,好像还挺大,打开电脑查了一下周日没雨,那就周日去吧...
注:周五晚上那个其实也没什么大不了的功能,只不过是两个文本框一个录入前缀,一个录入流水号(按理流水号应该只能是数字字符组成) ,然后根据前缀+流水号生成一个序号。
比如前缀lot,流水号是0000
那么要生成lot0000、lot0001这样的序号(后一个是前一个的数字加1)
杯具在于没有规定前缀可以录几个字符,是不是可以录入数字,以及是怎么组合的;还有个杯具的是平台封装的文本框不支持正则表达式设置隐藏码,于是就可能流水号会录入非数字字符;于是就出现了杯具的形如前缀为lot00,流水号0000,那我怎么去判断lot000000是不是前缀lot,流水号000000?
最后处理为前缀必须为字符结尾或者干脆前缀为空:"(?<prefix>[\D*|\d*]*[\D]+)*(?<lotTrailNo>[0-9]+) "
其中A安装有SQL Server 2K,B和C都没有安装SQL Server 2K,但C安装有SQL Server 2005 Express
同样的程序,.net 1.1编译后在A、B、C运行都能访问A机器的SQL Server 2K
.net 2.0编译后,在A中能访问SQL Server 2K,在B中不能,C中却可以
请问哪位大侠遇到过这种情况?该怎么做才能使B中的程序也能访问A的SQL Server 2K
2006-2-23:
刚做完测试,问题解决了,是端口问题,狂郁闷啊!
想当年学Oracle的时候客户端也总是连不上服务器,郁闷了好几天最后把防火墙关掉就好使了,类似的问题这次却没想到。
不过还是有些不明白为什么1.1编译的程序为什么可以在不打开端口的情况下访问数据库。
最近在看Bill Wagner的《Effectie C#》,本来想把书中提到的50条提高效率的方法逐一翻译出来也写成一系列博客,后来在MSDN看到李建忠的《Effective C#》 翻译札记,知道他在翻译这本书,于是我也就不再献丑了,只写一些体会吧。
Item 1: Always Use Properties Instand of Accessbile Data Members
Item 2: Prefer readonly to const
关于第一条李建忠已经有很好的说明了,但我在这里想说的却是如果一开始用一个public字段而以后发现有必要改成属性的时候带来的麻烦。
Effective C#更多的是以大型的组件式的开发为讨论基础的,所以往往是很多模块的,你在一个DLL里定义的public字段如果改成属性,那么所有使用这个DLL的模块都得重新编译一次才行(public字段和属性编译成IL后的代码就不再贴出来了,有兴趣的朋友可以自己尝试一下),而如果一开始就使用属性,那么只要重新编译一次修改的DLL就可以了。孰优孰劣,不同的情景要进行不同的权衡。
关于第二条,就会提到一件令我汗颜很久的事。学C那会儿,声明一个常量数组是很方便的,到C#后发现用private const int[] m=new int[] {10, 20, 30};是编译不过去的,后来想想也就明白为什么了,因为数组是按引用类型处理的,而const却只能用在一些简单类型上,比如int、float、enums、string等简单数据上,引用类型是不行的。但怎么去声明一个只读的数组我却很久没有解决,直到看了Effective C#,也很简单的,就是用readonly来做(汗颜啊)。
这里要说的也和第一条类似,在一个DLL里定义一个类:
public class UsefulValues2


{3
public static readonly int StartValue = 5;4
public const int EndValue = 10;5
}
for(int i = UsefulValues.StartValue; i < UsefulValues.EndValue; i++)2
Console.WriteLine("value is {0}", i);value is 5
value is 6
……
value is 9
好,现在把UsefulValues里的数据改一下,StartValue=105,EndValue = 120,然后重新编译这个DLL,然后再运行一下第二个程序集,你希望的结果是
value is 105
value is 106
……
value is 119
但结果呢,你什么输出也没得到。还是看IL代码,会发现在第二个程序集里EndValue是被替换成10了,这是编译时的行为,而如果用readonly来定义的只读值却是在运行时才确定是哪个值的。
Effective C#很多时候都考虑到发生改变的情况,正如李建忠所说,如果是一个“一次编写、N年都run”的项目,这些规则其实并不是一定要遵守的。还是那句话,权衡很重要。

