老赵的《趣味编程:从字符串中提取信息》的一个解决方案
足足3个月没有打开我的Visual Studio 2008,超过半年没有认认真真写过C# ,今天突然看到老赵提出了一个问题——趣味编程:从字符串中提取信息 ,勾起了我对大学时光的怀念,特写此文。
老赵的问题简单的来说,就是从以下这段字符串:
cpu-3.0g--color-red-green-black--price-5000-8000--weight-'3-'--keywords-'levi''s'
解析得出需要的信息:
new List{ new string[] { "cpu", "3.0g" }, new string[] { "color", "red", "green", "black" }, new string[] { "price", "5000", "8000" }, new string[] { "weight", "3-" }, new string[] { "keywords", "levi's" }, }
看到这个问题一下子就让我想起了大学的《编译原理》的词法分析器,我十分怀疑老赵是不是在某本编译原理的书里面得到灵感的,呵呵呵呵。
这是我们当时的课本:
好难懂,认认真真学一周,竟然看不明白一章书,唉……,现在基本上都忘得差不多了……,下文有什么说得不恰当的地方欢迎大家指正。
我的程序是基于状态机来做的,优点就是对输入仅作一次向前的扫描。
缺点非常明显——难维护,相当难维护,用时髦一点的语言来说叫不够敏捷。
还是言归正传吧,我用的是状态机的思路去解决这个问题。
我将老赵所说的字符归为三类:
1。平常字符,例如:A-Za-z0-9.,+等,简称Common、C。
2。分隔符,这里只有一个 - , 简称 Splitor、S 。
3。块符,这里也只有一个 ' 简称 Block 、 B 。
其实可以简单地认为,除了第二、第三种剩下的就是第一种了。
构建一个状态跳转图,知识忘记得太多了,可能图不标准。
我用哈希表存储这张图:
//构建跳转表 DictionaryJumpWorkTable=newDictionary (); JumpTable.Add("0,Common",1); JumpTable.Add("0,Block",4); JumpTable.Add("0,Splitor",0); JumpTable.Add("1,Splitor",2); JumpTable.Add("1,Common",1); JumpTable.Add("2,Common",1); JumpTable.Add("2,Splitor",3); JumpTable.Add("2,Block",4); JumpTable.Add("3,Common",1); JumpTable.Add("3,Block",4); JumpTable.Add("4,Common",4); JumpTable.Add("4,Splitor",4); JumpTable.Add("4,Block",5); JumpTable.Add("5,Block",4); JumpTable.Add("5,Splitor",2);
每一步跳转都会依据输入进行一定的处理,对应的表如下:
DictionaryJumpWorkTable = new Dictionary (); JumpWorkTable.Add("0,1", start); JumpWorkTable.Add("0,0", idle); JumpWorkTable.Add("1,1", inputCommonChar); JumpWorkTable.Add("1,2", endToken); JumpWorkTable.Add("2,1", startToken); JumpWorkTable.Add("2,3", endGroup); JumpWorkTable.Add("2,4", startToken); JumpWorkTable.Add("3,1", startToken); JumpWorkTable.Add("3,4", startToken); JumpWorkTable.Add("4,4", inputCommonChar); JumpWorkTable.Add("4,5", idle); JumpWorkTable.Add("5,4", inputCommonChar); JumpWorkTable.Add("5,2", endToken);
根据这两张表做的动作就很简单了:
////// 从一个状态跳转到另一个状态 /// /// 现在所处的状态 /// 输入的char ///跳转到新的状态 public int Jump(int nowPosition, char c) { if (((int)c) > 0) { string jump = nowPosition.ToString() + "," + JudgeChar(c); int nextPosition = JumpTable[jump]; string work = nowPosition.ToString() + "," + nextPosition; JumpWorkTable[work](c); return nextPosition; } else { //不重要 } }
主程序调用也很简单了:
InputSource source = new InputSource(); FSM fsm = new FSM(); int nowPosition = 0; while (source.HashToken()) { char c = source.GetNextChar(); nowPosition = fsm.Jump(nowPosition, c); }
fsm.Jump(nowPosition, (char)0);
可以说,整个程序的核心就是要弄明白上面的状态跳转图,以及每步跳转要做什么事情。虽然程序不长,但是基本上的脑力都是耗费在状态跳转图上面,如果状态跳转有少许差错就废了。