<?xml version='1.0' encoding='UTF-8'?><?xml-stylesheet href="http://www.blogger.com/styles/atom.css" type="text/css"?><feed xmlns='http://www.w3.org/2005/Atom' xmlns:openSearch='http://a9.com/-/spec/opensearchrss/1.0/' xmlns:georss='http://www.georss.org/georss' xmlns:gd='http://schemas.google.com/g/2005' xmlns:thr='http://purl.org/syndication/thread/1.0'><id>tag:blogger.com,1999:blog-5349536066535334815</id><updated>2012-02-07T23:29:02.966-08:00</updated><category term='数据库'/><category term='java 技术'/><category term='技术资源'/><title type='text'>Yichuan Cai 's technique blog</title><subtitle type='html'>Let's waste time, chasing dream</subtitle><link rel='http://schemas.google.com/g/2005#feed' type='application/atom+xml' href='http://yourmagicsearchengine.blogspot.com/feeds/posts/default'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5349536066535334815/posts/default?max-results=100'/><link rel='alternate' type='text/html' href='http://yourmagicsearchengine.blogspot.com/'/><link rel='hub' href='http://pubsubhubbub.appspot.com/'/><author><name>Yichuan Cai's home page</name><uri>http://www.blogger.com/profile/10921376371265973337</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><generator version='7.00' uri='http://www.blogger.com'>Blogger</generator><openSearch:totalResults>31</openSearch:totalResults><openSearch:startIndex>1</openSearch:startIndex><openSearch:itemsPerPage>100</openSearch:itemsPerPage><entry><id>tag:blogger.com,1999:blog-5349536066535334815.post-4945577274844984596</id><published>2010-07-07T16:47:00.000-07:00</published><updated>2010-07-07T16:48:03.866-07:00</updated><title type='text'>see all running process in Linux</title><content type='html'>&lt;p&gt;&lt;span class="drop_cap"&gt;H&lt;/span&gt;ow do I see all running process in  Linux?&lt;br /&gt;&lt;span id="more-380"&gt;&lt;/span&gt;&lt;br /&gt;You need to use the ps command. It provide information about the  currently running processes, including their process identification  numbers (PIDs). Both Linux and UNIX support ps command to display  information about all running process. ps command gives a snapshot of  the current processes. If you want a repetitive update of this status,  use top command.&lt;/p&gt; &lt;h2&gt;ps command&lt;/h2&gt; &lt;p&gt;Type the following &lt;strong&gt;ps command&lt;/strong&gt; to display all running  process:&lt;br /&gt;&lt;code&gt;# ps aux | less&lt;/code&gt;&lt;/p&gt; &lt;p&gt;Where,&lt;/p&gt; &lt;ul&gt;&lt;li&gt;-A: select all processes&lt;/li&gt;&lt;li&gt;a: select all processes on a terminal, including those of other  users&lt;/li&gt;&lt;li&gt;x: select processes without controlling ttys&lt;/li&gt;&lt;/ul&gt; &lt;h2&gt;Task: see every process on the system&lt;/h2&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5349536066535334815-4945577274844984596?l=yourmagicsearchengine.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://yourmagicsearchengine.blogspot.com/feeds/4945577274844984596/comments/default' title='帖子评论'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=5349536066535334815&amp;postID=4945577274844984596' title='2 条评论'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5349536066535334815/posts/default/4945577274844984596'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5349536066535334815/posts/default/4945577274844984596'/><link rel='alternate' type='text/html' href='http://yourmagicsearchengine.blogspot.com/2010/07/see-all-running-process-in-linux.html' title='see all running process in Linux'/><author><name>Yichuan Cai's home page</name><uri>http://www.blogger.com/profile/10921376371265973337</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5349536066535334815.post-8943638617958662017</id><published>2010-07-07T16:42:00.001-07:00</published><updated>2010-07-07T16:42:26.288-07:00</updated><title type='text'>Abstract Class in Java (reproduced)</title><content type='html'>&lt;div id="PageTitle"&gt;Abstract Methods and Classes&lt;/div&gt;             &lt;blockquote&gt; &lt;!-- Abstract Methods and Classes --&gt;  An &lt;i&gt;abstract class&lt;/i&gt; is a class that is declared &lt;code&gt;abstract&lt;/code&gt;—it  may or may not  include abstract methods. Abstract classes cannot be instantiated, but  they can be subclassed.  &lt;p&gt; An &lt;i&gt;abstract method&lt;/i&gt; is a method that is declared without an  implementation  (without braces, and followed by a semicolon), like this: &lt;/p&gt;&lt;blockquote&gt;&lt;pre&gt;abstract void moveTo(double deltaX, double deltaY);&lt;br /&gt;&lt;/pre&gt;&lt;/blockquote&gt; If a class includes abstract methods, the class itself &lt;i&gt;must&lt;/i&gt; be  declared &lt;code&gt;abstract&lt;/code&gt;, as in: &lt;blockquote&gt;&lt;pre&gt;public abstract class GraphicObject {&lt;br /&gt;  // declare fields&lt;br /&gt;  // declare non-abstract methods&lt;br /&gt;  abstract void draw();&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;/blockquote&gt;  When an abstract class is subclassed, the subclass usually  provides implementations for all of the abstract methods in its parent  class. However,  if it does not, the subclass  must also be declared &lt;code&gt;abstract&lt;/code&gt;. &lt;blockquote&gt;&lt;hr /&gt;&lt;strong&gt;Note:&lt;/strong&gt; All of the  methods in an &lt;i&gt;interface&lt;/i&gt; (see the  &lt;a class="TutorialLink" target="_top" href="http://download.oracle.com/docs/cd/E17409_01/javase/tutorial/java/IandI/createinterface.html"&gt;&lt;code&gt;Interfaces&lt;/code&gt;&lt;/a&gt;  section) are &lt;i&gt;implicitly&lt;/i&gt; abstract, so the &lt;code&gt;abstract&lt;/code&gt;  modifier is not used with  interface methods (it could be—it's just not necessary). &lt;hr /&gt;&lt;/blockquote&gt;  &lt;h3 fmstyle="C-Head"&gt;Abstract Classes versus Interfaces&lt;/h3&gt;   Unlike interfaces, abstract classes can contain fields that are not &lt;code&gt;static&lt;/code&gt;  and  &lt;code&gt;final&lt;/code&gt;, and they can contain implemented methods. Such  abstract classes  are similar to interfaces, except that they provide a partial  implementation, leaving it to subclasses to complete the implementation.   If an abstract class  contains &lt;i&gt;only&lt;/i&gt; abstract method declarations, it should be declared  as an interface instead.  &lt;p&gt;  &lt;/p&gt;&lt;p&gt; Multiple interfaces can be implemented by classes anywhere in the class  hierarchy, whether or not they are  related to one another in any way. Think of &lt;code&gt;Comparable&lt;/code&gt; or &lt;code&gt;Cloneable&lt;/code&gt;,  for example.  &lt;/p&gt;&lt;p&gt; By comparison, abstract classes  are most commonly subclassed to share pieces of implementation. A single  abstract class  is subclassed by similar classes that have a lot in common  (the implemented parts of the abstract class), but also have some  differences (the abstract methods).    &lt;/p&gt;&lt;h3 fmstyle="C-Head"&gt;An Abstract Class Example&lt;/h3&gt;   In an object-oriented drawing application, you can draw circles,  rectangles, lines, Bezier curves, and many other graphic objects. These  objects all have certain states (for example: position, orientation,  line color, fill color)  and behaviors (for example: moveTo, rotate, resize, draw) in common.  Some of these states and  behaviors are the same for all graphic objects—for example: position,  fill color, and moveTo. Others  require different implementations—for example, resize or draw. All &lt;code&gt;GraphicObject&lt;/code&gt;s   must know how to draw or resize themselves; they just differ in how they  do it.  This is a perfect situation for an abstract superclass. You can take  advantage of the similarities and declare all the graphic objects to  inherit from the same abstract parent object—for example,  &lt;code&gt;GraphicObject&lt;/code&gt;, as shown in &lt;span id="figure:classes-graphicObject.gif"&gt;the following figure.&lt;/span&gt; &lt;center&gt;&lt;p&gt;&lt;img src="http://download.oracle.com/docs/cd/E17409_01/javase/tutorial/figures/java/classes-graphicObject.gif" alt="Classes Rectangle, Line, Bezier, and Circle inherit from  GraphicObject " align="BOTTOM" width="372" height="80" /&gt;&lt;/p&gt;&lt;p class="FigureCaption"&gt;Classes Rectangle, Line, Bezier, and Circle  inherit from GraphicObject&lt;/p&gt;&lt;/center&gt;  &lt;p&gt; First, you declare an abstract class, &lt;code&gt;GraphicObject&lt;/code&gt;,  to provide member variables and methods that are wholly shared by  all subclasses, such as the current position and the  &lt;code&gt;moveTo&lt;/code&gt; method. &lt;code&gt;GraphicObject&lt;/code&gt;  also declares abstract methods for methods, such as  &lt;code&gt;draw&lt;/code&gt; or &lt;code&gt;resize&lt;/code&gt;, that need to be implemented by  all  subclasses but must be implemented in different  ways. The &lt;code&gt;GraphicObject&lt;/code&gt; class can look  something like this:   &lt;/p&gt;&lt;blockquote&gt;&lt;pre&gt;abstract class GraphicObject {&lt;br /&gt;   int x, y;&lt;br /&gt;   ...&lt;br /&gt;   void moveTo(int newX, int newY) {&lt;br /&gt;       ...&lt;br /&gt;   }&lt;br /&gt;   abstract void draw();&lt;br /&gt;   abstract void resize();&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;/blockquote&gt;  Each non-abstract subclass of &lt;code&gt;GraphicObject&lt;/code&gt;, such  as &lt;code&gt;Circle&lt;/code&gt; and &lt;code&gt;Rectangle&lt;/code&gt;, must provide  implementations for the &lt;code&gt;draw&lt;/code&gt; and &lt;code&gt;resize&lt;/code&gt;  methods:  &lt;blockquote&gt;&lt;pre&gt;class Circle extends GraphicObject {&lt;br /&gt;   void draw() {&lt;br /&gt;       ...&lt;br /&gt;   }&lt;br /&gt;   void resize() {&lt;br /&gt;       ...&lt;br /&gt;   }&lt;br /&gt;}&lt;br /&gt;class Rectangle extends GraphicObject {&lt;br /&gt;   void draw() {&lt;br /&gt;       ...&lt;br /&gt;   }&lt;br /&gt;   void resize() {&lt;br /&gt;       ...&lt;br /&gt;   }&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;/blockquote&gt;    &lt;h3 fmstyle="C-Head"&gt;When an Abstract Class Implements an Interface&lt;/h3&gt;   In the section on  &lt;a class="TutorialLink" target="_top" href="http://download.oracle.com/docs/cd/E17409_01/javase/tutorial/java/IandI/createinterface.html"&gt;&lt;code&gt;Interfaces&lt;/code&gt;&lt;/a&gt;  , it was noted that a class that implements an interface must implement   &lt;i&gt;all&lt;/i&gt; of the interface's methods. It is possible, however, to  define a class  that does not implement all of the interface methods, provided that the  class is declared to be  &lt;code&gt;abstract&lt;/code&gt;. For example, &lt;blockquote&gt;&lt;pre&gt;abstract class X implements Y {&lt;br /&gt; // implements all but one method of Y&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;class XX extends X {&lt;br /&gt; // implements the remaining method in Y&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;/blockquote&gt; In this case, class &lt;code&gt;X&lt;/code&gt; must be &lt;code&gt;abstract&lt;/code&gt; because  it does not fully  implement &lt;code&gt;Y&lt;/code&gt;, but class &lt;code&gt;XX&lt;/code&gt; does, in fact,  implement &lt;code&gt;Y&lt;/code&gt;.  &lt;h3 fmstyle="C-Head"&gt;Class Members&lt;/h3&gt; An abstract class may have &lt;code&gt;static&lt;/code&gt; fields and &lt;code&gt;static&lt;/code&gt;  methods.  You can use these static members with a class reference—for example, &lt;code&gt;AbstractClass.staticMethod()&lt;/code&gt;—as   you would with any other class.          &lt;/blockquote&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5349536066535334815-8943638617958662017?l=yourmagicsearchengine.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://yourmagicsearchengine.blogspot.com/feeds/8943638617958662017/comments/default' title='帖子评论'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=5349536066535334815&amp;postID=8943638617958662017' title='0 条评论'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5349536066535334815/posts/default/8943638617958662017'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5349536066535334815/posts/default/8943638617958662017'/><link rel='alternate' type='text/html' href='http://yourmagicsearchengine.blogspot.com/2010/07/abstract-class-in-java-reproduced.html' title='Abstract Class in Java (reproduced)'/><author><name>Yichuan Cai's home page</name><uri>http://www.blogger.com/profile/10921376371265973337</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5349536066535334815.post-7350016096966495385</id><published>2010-07-06T21:37:00.000-07:00</published><updated>2010-07-06T21:38:28.843-07:00</updated><title type='text'>Multi-threaded Debugging Techniques  By Shameem Akhter and Jason Roberts, April 23, 2007</title><content type='html'>&lt;span class="story-teaser"&gt;Debugging multi-threaded applications can be a  challenging task. &lt;/span&gt;&lt;br /&gt;                                 &lt;i&gt;Shameem Akhter is a platform architect at Intel  Corporation, focusing on single socket multi-core architecture and  performance analysis. Jason Roberts is a senior software engineer at  Intel, and has worked on a number of different multi-threaded software  products that span a wide range of applications targeting desktop,  handheld, and embedded DSP platforms. This article was excerpted from  their book &lt;a href="http://www.intel.com/intelpress/sum_mcp.htm"&gt;&lt;i&gt;Multi-Core  Programming&lt;/i&gt;&lt;/a&gt;. Copyright (c) 2006 Intel Corporation. All rights  reserved.&lt;/i&gt; &lt;hr /&gt; &lt;p&gt;  &lt;/p&gt;&lt;p&gt; Debugging multi-threaded applications can be a challenging task. The  increased complexity of multi-threaded programs results in a large  number of possible states that the program may be in at any given time.  Determining the state of the program at the time of failure can be  difficult; understanding why a particular state is troublesome can be  even more difficult. Multi-threaded programs often fail in unexpected  ways, and often in a nondeterministic fashion. Bugs may manifest  themselves in a sporadic fashion, frustrating developers who are  accustomed to troubleshooting issues that are consistently reproducible  and predictable. Finally, multi-threaded applications can fail in a  drastic fashion-deadlocks cause an application or worse yet, the entire  system, to hang. Users tend to find these types of failures to be  unacceptable.  &lt;/p&gt;&lt;p&gt; &lt;/p&gt;&lt;h3&gt;General Debug Techniques&lt;/h3&gt; &lt;p&gt;  &lt;/p&gt;&lt;p&gt; Regardless of which library or platform that you are developing on,  several general principles can be applied to debugging multi-threaded  software applications.  &lt;/p&gt;&lt;p&gt;  &lt;/p&gt;&lt;p&gt; The first technique for eliminating bugs in multi-threaded code is to  avoid introducing the bug in the first place. Many software defects can  be prevented by using proper software development practices. The later a  problem is found in the product development lifecycle, the more  expensive it is to fix. Given the complexity of multi-threaded programs,  it is critical that multi-threaded applications are properly designed  up front.   &lt;/p&gt;&lt;p&gt;  How often have you, as a software developer, experienced the following  situation? Someone on the team that you're working on gets a great idea  for a new product or feature. A quick prototype that illustrates the  idea is implemented and a quick demo, using a trivial use-case, is  presented to management. Management loves the idea and immediately  informs sales and marketing of the new product or feature. Marketing  then informs the customer of the feature, and in order to make a sale,  promises the customer the feature in the next release. Meanwhile, the  engineering team, whose original intent of presenting the idea was to  get resources to properly implement the product or feature sometime in  the future, is now faced with the task of delivering on a customer  commitment immediately. As a result of time constraints, it is often the  case that the only option is to take the prototype, and try to turn it  into production code.   &lt;/p&gt;&lt;p&gt;  While this example illustrates a case where marketing and management may  be to blame for the lack of following an appropriate process, software  developers are often at fault in this regard as well. For many  developers, writing software is the most interesting part of the job.  There's a sense of instant gratification when you finish writing your  application and press the run button. The results of all the effort and  hard work appear instantly. In addition, modern debuggers provide a wide  range of tools that allow developers to quickly identify and fix simple  bugs. As a result, many programmers fall into the trap of coding now,  deferring design and testing work to a later time. Taking this approach  on a multi-threaded application is a recipe for disaster for several  reasons: &lt;/p&gt;&lt;p&gt; &lt;/p&gt;&lt;ul&gt;&lt;li&gt;Multi-threaded applications are inherently more complicated than  single-threaded applications. Hacking out a reliable, scalable  implementation of a multi-threaded application is hard; even for  experienced parallel programmers. The primary reason for this is the  large number of corner cases that can occur and the wide range of  possible paths of the application. Another consideration is the type of  run-time environment the application is running on. The access patterns  may vary wildly depending on whether or not the application is running  on a single-core or multi-core platform, and whether or not the platform  supports simultaneous multithreading hardware. These different run-time  scenarios need to be thoroughly thought out and handled to guarantee  reliability in a wide range of environments and use cases.  &lt;p&gt; &lt;/p&gt;&lt;/li&gt;&lt;li&gt;Multi-threaded bugs may not surface when running under the  debugger. Multi-threading bugs are very sensitive to the timing of  events in an application. Running the application under the debugger  changes the timing, and as a result, may mask problems. When your  application fails in a test or worse, the customer environment, but runs  reliably under the debugger, it is almost certainly a timing issue in  the code. While following a software process can feel like a nuisance at  times, taking the wrong approach and not following any process at all  is a perilous path when writing all but the most trivial applications.  This holds true for parallel programs. While designing your  multi-threaded applications, you should keep these points in mind. &lt;p&gt; &lt;/p&gt;&lt;/li&gt;&lt;li&gt;Design the application so that it can run sequentially. An  application should always have a valid means of sequential execution.  The application should be validated in this run mode first. This allows  developers to eliminate bugs in the code that are not related to  threading. If a problem is still present in this mode of execution, then  the task of debugging reduces to single-threaded debugging. In many  circumstances, it is very easy to generate a sequential version of an  application. For example, an OpenMP application compiled with one of the  Intel compilers can use the openmp-stubs option to tell the compiler to  generate sequential OpenMP code.  &lt;p&gt; &lt;/p&gt;&lt;/li&gt;&lt;li&gt;Use established parallel programming patterns. The best  defense against defects is to use parallel patterns that are known to be  safe. Established patterns solve many of the common parallel  programming problems in a robust manner. Reinventing the wheel is not  necessary in many cases.   &lt;p&gt; &lt;/p&gt;&lt;/li&gt;&lt;li&gt;Include built-in debug support in the application. When  trying to root cause an application fault, it is often useful for  programmers to be able to examine the state of the system at any  arbitrary point in time. Consider adding functions that display the  state of a thread-or all active threads. Trace buffers, described in the  next section, may be used to record the sequence of accesses to a  shared resource. Many modern debuggers support the capability of calling  a function while stopped at a breakpoint. This mechanism allows  developers to extend the capabilities of the debugger to suit their  particular application's needs.  &lt;/li&gt;&lt;/ul&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5349536066535334815-7350016096966495385?l=yourmagicsearchengine.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://yourmagicsearchengine.blogspot.com/feeds/7350016096966495385/comments/default' title='帖子评论'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=5349536066535334815&amp;postID=7350016096966495385' title='0 条评论'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5349536066535334815/posts/default/7350016096966495385'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5349536066535334815/posts/default/7350016096966495385'/><link rel='alternate' type='text/html' href='http://yourmagicsearchengine.blogspot.com/2010/07/multi-threaded-debugging-techniques-by.html' title='Multi-threaded Debugging Techniques  By Shameem Akhter and Jason Roberts, April 23, 2007'/><author><name>Yichuan Cai's home page</name><uri>http://www.blogger.com/profile/10921376371265973337</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5349536066535334815.post-54666253244995252</id><published>2010-07-05T23:46:00.000-07:00</published><updated>2010-07-05T23:47:36.151-07:00</updated><title type='text'>大数阶乘</title><content type='html'>大数的操作问题，用基本的数据类型都有溢出的风险。在此，介绍一种比较好的思路，来计算大数的阶乘，比如。100！&lt;br /&gt;&lt;br /&gt;首先，定义两个整型的数组：&lt;br /&gt;int fac[1000];//暂且先设定是1000位，我称之为“结果数组”&lt;br /&gt;int add[1000];//我称之为“进位数组”&lt;br /&gt;&lt;br /&gt;现在具体说明两个数组的作用：&lt;br /&gt;&lt;br /&gt;1.fac[1000]&lt;br /&gt;比如说，一个数5的阶乘是120，那么我就用这个数组存储它：&lt;br /&gt;fac[0]=0&lt;br /&gt;fac[1]=2&lt;br /&gt;fac[2]=1&lt;br /&gt;现在明白了数组 fac的作用了吧。用这样的数组我们可以放阶乘后结果是1000位的数。&lt;br /&gt;&lt;br /&gt;2.在介绍add[1000]之前，我介绍一下算法的思想，就以 6！为例：&lt;br /&gt;从上面我们知道了5！是怎样存储的。&lt;br /&gt;就在5！的基础上来计算6！，演示如下：&lt;br /&gt;&lt;br /&gt;fac[0]=fac[0]*6=0&lt;br /&gt;fac[1]=fac[1]*6=12&lt;br /&gt;fac[2]=fac[2]*6=6&lt;br /&gt;&lt;br /&gt;3. 现在就用到了我们的：“进位数组”add[1000].&lt;br /&gt;先得说明一下：add[i]就是在第2步中用算出的结果中，第i位向第i+1位的进位数值。还是接上例：&lt;br /&gt;add[0]=0;&lt;br /&gt;add[1]=1;  // 计算过程：就是 (fac[1]+add[0])  /  10＝1&lt;br /&gt;add[2]=0;  // 计算过程：就是 (fac[2]+add[1]) /10=0&lt;br /&gt;.......&lt;br /&gt;.......&lt;br /&gt;.......&lt;br /&gt;&lt;br /&gt;add[i+1] =( fac[i+1] + add[i] ) / 10&lt;br /&gt;&lt;br /&gt;现在就知道了我们的数组add[]是干什么的了吧，并且明白了add[i]是如何求得的了吧。&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;4.知道了add[1000]的值，现在就在 1. 和 3 . 两步的基础上来计算最终的结果。（第2步仅作为我们理解的步骤，我们下面的计算已经包含了它）&lt;br /&gt;&lt;br /&gt;fac[0] = ( fac[0]*6 )  mod 10 =0&lt;br /&gt;fac[1] = ( fac[1]*6 + add[0] ) mod 10 ＝2&lt;br /&gt;fac[2] = ( fac[2]*6 + add[1] ) mod 10=7&lt;br /&gt;&lt;br /&gt;到这里我们已经计算完了6！。然后就可以将数组fac[1000]中的数，以字符的形式按位输出到屏幕上了。&lt;br /&gt;&lt;br /&gt;5.还有一点需要说明，就是我们需要一个变量来记录fac[1000]中实际用到了几位，比如5！用了前 3位；&lt;br /&gt;我们在这里定义为 top&lt;br /&gt;    为了计算top，我们有用到了add[1000],还是以上面的为例：&lt;br /&gt; &lt;br /&gt;    5！时，top=3,在此基础上我们来看6！时top=?&lt;br /&gt;    由于  add[2]=0&lt;br /&gt;    所以  top=3（没有变）&lt;br /&gt;也就是说，如果最高位有进位时，我们的top=top+1,否则，top值不变。&lt;br /&gt;&lt;br /&gt;6.总结一下，可以发现，我们把阶乘转化为 两个10以内的数的乘法，还有两个10以内的数的家法了。&lt;br /&gt; 因此，不管再大的数，基本上都能算出了，只要你的数组够大就行了。&lt;br /&gt;&lt;br /&gt;程序实现：&lt;br /&gt;&lt;br /&gt;#include "stdafx.h"&lt;br /&gt;#include &lt;iostream&gt;&lt;br /&gt;&lt;br /&gt;using namespace std;&lt;br /&gt;&lt;br /&gt;#define  MAX 1000&lt;br /&gt;&lt;br /&gt;int fac[MAX]={1,};&lt;br /&gt;int add[MAX]={0,};&lt;br /&gt;&lt;br /&gt;int top=1;//计算结果位数&lt;br /&gt;&lt;br /&gt;void calculate(int n);&lt;br /&gt;&lt;br /&gt;void print();&lt;br /&gt;&lt;br /&gt;int main(int argc, char * argv[])&lt;br /&gt;{&lt;br /&gt;cout&lt;&lt;"please input the number:"; int n; cin&gt;&gt;n;&lt;br /&gt;calculate(n);&lt;br /&gt;cout&lt;&lt;n&gt;&lt;&lt;"!="; print(); cout&lt;&lt;" 总共"&lt;&lt;top&gt;&lt;&lt;"位"&lt;&lt;endl; return="" void="" int="" if="" n=""&gt;&lt;=0) {  exit(1); }  for (int i=1;i&lt;=n;i++) {  int m=top;   for (int j=0;j&lt;m;j++) int="" tmp="fac[j]*i;" if="" j="=0)" else="" while=""&gt;0){&lt;br /&gt;    top++;&lt;br /&gt;    int tmp=fac[top-1];&lt;br /&gt;    fac[top-1]=(tmp+add[top-2])%10;&lt;br /&gt;   add[top-1]=(tmp+add[top-2])/10;&lt;br /&gt;   }&lt;br /&gt;  }&lt;br /&gt; }&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;// 打印输出结果&lt;br /&gt;void print(){&lt;br /&gt; int j=0;&lt;br /&gt; for(int i=top-1;i&gt;=0;i--)&lt;br /&gt; {&lt;br /&gt;  if ((j++)%20==0)&lt;br /&gt;  {&lt;br /&gt;   cout&lt;&lt;endl;&gt;&lt;br /&gt;  }&lt;br /&gt;  cout&lt;&lt;fac[i];&gt;&lt;br /&gt;  }&lt;br /&gt;   cout&lt;&lt;endl;&gt;&lt;br /&gt;}&lt;/endl;&gt;&lt;/fac[i];&gt;&lt;/endl;&gt;&lt;/m;j++)&gt;&lt;/endl;&gt;&lt;/top&gt;&lt;/n&gt;&lt;/iostream&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5349536066535334815-54666253244995252?l=yourmagicsearchengine.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://yourmagicsearchengine.blogspot.com/feeds/54666253244995252/comments/default' title='帖子评论'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=5349536066535334815&amp;postID=54666253244995252' title='0 条评论'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5349536066535334815/posts/default/54666253244995252'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5349536066535334815/posts/default/54666253244995252'/><link rel='alternate' type='text/html' href='http://yourmagicsearchengine.blogspot.com/2010/07/blog-post.html' title='大数阶乘'/><author><name>Yichuan Cai's home page</name><uri>http://www.blogger.com/profile/10921376371265973337</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5349536066535334815.post-6019311823745399245</id><published>2010-06-27T15:47:00.001-07:00</published><updated>2010-06-27T15:47:53.021-07:00</updated><title type='text'>C++ interview questions</title><content type='html'>C/C++ Programming interview questions and answers&lt;br /&gt;&lt;br /&gt;By Satish Shetty, July 14th, 2004&lt;br /&gt;&lt;br /&gt;What is encapsulation??&lt;br /&gt;&lt;br /&gt;Containing and hiding information about an object, such as internal data structures and code. Encapsulation isolates the internal complexity of an object's operation from the rest of the application. For example, a client component asking for net revenue from a business object need not know the data's origin.&lt;br /&gt;&lt;br /&gt;What is inheritance?&lt;br /&gt;&lt;br /&gt;Inheritance allows one class to reuse the state and behavior of another class. The derived class inherits the properties and method implementations of the base class and extends it by overriding methods and adding additional properties and methods.&lt;br /&gt;&lt;br /&gt;What is Polymorphism??&lt;br /&gt;&lt;br /&gt;Polymorphism allows a client to treat different objects in the same way even if they were created from different classes and exhibit different behaviors.&lt;br /&gt;&lt;br /&gt;You can use implementation inheritance to achieve polymorphism in languages such as C++ and Java.&lt;br /&gt;&lt;br /&gt;Base class object's pointer can invoke methods in derived class objects.&lt;br /&gt;&lt;br /&gt;You can also achieve polymorphism in C++ by function overloading and operator overloading.&lt;br /&gt;&lt;br /&gt;What is constructor or ctor?&lt;br /&gt;&lt;br /&gt;Constructor creates an object and initializes it. It also creates vtable for virtual functions. It is different from other methods in a class.&lt;br /&gt;&lt;br /&gt;What is destructor?&lt;br /&gt;&lt;br /&gt;Destructor usually deletes any extra resources allocated by the object. &lt;br /&gt;&lt;br /&gt;What is default constructor?&lt;br /&gt;&lt;br /&gt;Constructor with no arguments or all the arguments has default values.&lt;br /&gt;&lt;br /&gt;What is copy constructor?&lt;br /&gt;&lt;br /&gt;Constructor which initializes the it's object member variables ( by shallow copying) with another object of the same class. If you don't implement one in your class then compiler implements one for you.&lt;br /&gt;&lt;br /&gt;for example:&lt;br /&gt;Boo Obj1(10); // calling Boo constructor&lt;br /&gt;&lt;br /&gt;Boo Obj2(Obj1); // calling boo copy constructor&lt;br /&gt;Boo Obj2 = Obj1;// calling boo copy constructor&lt;br /&gt;&lt;br /&gt;When are copy constructors called?&lt;br /&gt;&lt;br /&gt;Copy constructors are called in following cases: &lt;br /&gt;a) when a function returns an object of that class by value&lt;br /&gt;b) when the object of that class is passed by value as an argument to a function&lt;br /&gt;c) when you construct an object based on another object of the same class&lt;br /&gt;d) When compiler generates a temporary object&lt;br /&gt;&lt;br /&gt;What is assignment operator?&lt;br /&gt;&lt;br /&gt;Default assignment operator handles assigning one object to another of the same class. Member to member copy (shallow copy)&lt;br /&gt;&lt;br /&gt;What are all the implicit member functions of the class? Or what are all the functions which compiler implements for us if we don't define one.??&lt;br /&gt;&lt;br /&gt;default ctor&lt;br /&gt;copy ctor&lt;br /&gt;assignment operator&lt;br /&gt;default destructor&lt;br /&gt;address operator&lt;br /&gt;&lt;br /&gt;What is conversion constructor?&lt;br /&gt;&lt;br /&gt;constructor with a single argument makes that constructor as conversion ctor and it can be used for type conversion.&lt;br /&gt;&lt;br /&gt;for example:&lt;br /&gt;&lt;br /&gt;class Boo&lt;br /&gt;{&lt;br /&gt;  public:&lt;br /&gt;    Boo( int i );&lt;br /&gt;};&lt;br /&gt;&lt;br /&gt;Boo BooObject = 10 ; // assigning int 10 Boo object&lt;br /&gt;&lt;br /&gt;What is conversion operator??&lt;br /&gt;&lt;br /&gt;class can have a public method for specific data type conversions.&lt;br /&gt;&lt;br /&gt;for example:&lt;br /&gt;class Boo&lt;br /&gt;{&lt;br /&gt;  double value;&lt;br /&gt;  public:&lt;br /&gt;    Boo(int i )&lt;br /&gt;    operator double() &lt;br /&gt;    { &lt;br /&gt;  return value;&lt;br /&gt;    }&lt;br /&gt;};&lt;br /&gt;&lt;br /&gt;Boo BooObject;&lt;br /&gt;&lt;br /&gt;double i  = BooObject; // assigning object to variable i of type double. now conversion  operator gets called to assign the value.&lt;br /&gt;&lt;br /&gt;What is diff between malloc()/free() and new/delete?&lt;br /&gt;&lt;br /&gt;malloc allocates memory for object in heap but doesn't invoke object's constructor to initiallize the object.&lt;br /&gt;&lt;br /&gt;new allocates memory and also invokes constructor to initialize the object.&lt;br /&gt;&lt;br /&gt;malloc() and free() do not support object semantics &lt;br /&gt;Does not construct and destruct objects &lt;br /&gt;string * ptr = (string *)(malloc (sizeof(string)))&lt;br /&gt;Are not safe &lt;br /&gt;Does not calculate the size of the objects that it construct &lt;br /&gt;Returns a pointer to void &lt;br /&gt;int *p = (int *) (malloc(sizeof(int)));&lt;br /&gt;int *p = new int;&lt;br /&gt;Are not extensible &lt;br /&gt;new and delete can be overloaded in a class &lt;br /&gt;&lt;br /&gt;"delete" first calls the object's termination routine (i.e. its destructor) and then releases the space the object occupied on the heap memory. If an array of objects was created using new, then delete must be told that it is dealing with an array by preceding the name with an empty []:-&lt;br /&gt;&lt;br /&gt;Int_t *my_ints = new Int_t[10];&lt;br /&gt;&lt;br /&gt;...&lt;br /&gt;&lt;br /&gt;delete []my_ints;&lt;br /&gt;&lt;br /&gt;what is the diff between "new" and "operator new" ?&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;"operator new" works like malloc.&lt;br /&gt;&lt;br /&gt;What is difference between template and macro??&lt;br /&gt;&lt;br /&gt;There is no way for the compiler to verify that the macro parameters are of compatible types. The macro is expanded without any special type checking.&lt;br /&gt;&lt;br /&gt;If macro parameter has a postincremented variable ( like c++ ), the increment is performed two times.&lt;br /&gt;&lt;br /&gt;Because macros are expanded by the preprocessor, compiler error messages will refer to the expanded macro, rather than the macro definition itself. Also, the macro will show up in expanded form during debugging.&lt;br /&gt;&lt;br /&gt;for example:&lt;br /&gt;&lt;br /&gt;Macro:&lt;br /&gt;&lt;br /&gt;#define min(i, j) (i &lt; j ? i : j)&lt;br /&gt;&lt;br /&gt;template:&lt;br /&gt;template&lt;class T&gt; &lt;br /&gt;T min (T i, T j) &lt;br /&gt;{ &lt;br /&gt;return i &lt; j ? i : j;&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;What are C++ storage classes?&lt;br /&gt;&lt;br /&gt;auto&lt;br /&gt;register&lt;br /&gt;static&lt;br /&gt;extern&lt;br /&gt;&lt;br /&gt;auto: the default. Variables are automatically created and initialized when they are defined and are destroyed at the end of the block containing their definition. They are not visible outside that block&lt;br /&gt;&lt;br /&gt;register: a type of auto variable. a suggestion to the compiler to use a CPU register for performance&lt;br /&gt;&lt;br /&gt;static: a variable that is known only in the function that contains its definition but is never destroyed and retains its value between calls to that function. It exists from the time the program begins execution&lt;br /&gt;&lt;br /&gt;extern: a static variable whose definition and placement is determined when all object and library modules are combined (linked) to form the executable code file. It can be visible outside the file where it is defined.&lt;br /&gt;&lt;br /&gt;What are storage qualifiers in C++ ?&lt;br /&gt;&lt;br /&gt;They are..&lt;br /&gt;&lt;br /&gt;const&lt;br /&gt;volatile&lt;br /&gt;mutable&lt;br /&gt;&lt;br /&gt;Const keyword indicates that memory once initialized, should not be altered by a program.&lt;br /&gt;&lt;br /&gt;volatile keyword indicates that the value in the memory location can be altered even though nothing in the program&lt;br /&gt;code modifies the contents. for example if you have a pointer to hardware location that contains the time, where hardware changes the value of this pointer variable and not the program. The intent of this keyword to improve the optimization ability of the compiler.   &lt;br /&gt;&lt;br /&gt;mutable keyword indicates that particular member of a structure or class can be altered even if a particular structure variable, class, or class member function is constant.&lt;br /&gt;&lt;br /&gt;struct data&lt;br /&gt;{&lt;br /&gt;char name[80];&lt;br /&gt;mutable double salary;&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;const data MyStruct = { "Satish Shetty", 1000 }; //initlized by complier&lt;br /&gt;&lt;br /&gt;strcpy ( MyStruct.name, "Shilpa Shetty"); // compiler error&lt;br /&gt;MyStruct.salaray = 2000 ; // complier is happy allowed&lt;br /&gt;&lt;br /&gt;What is reference ??&lt;br /&gt;&lt;br /&gt;reference is a name that acts as an alias, or alternative name, for a previously defined variable or an object.&lt;br /&gt;&lt;br /&gt;prepending variable with "&amp;" symbol makes it as reference.&lt;br /&gt;&lt;br /&gt;for example:&lt;br /&gt;&lt;br /&gt;int a;&lt;br /&gt;int &amp;b = a; &lt;br /&gt; &lt;br /&gt;&lt;br /&gt;What is passing by reference?&lt;br /&gt;&lt;br /&gt;Method of passing arguments to a function which takes parameter of type reference.&lt;br /&gt;&lt;br /&gt;for example:&lt;br /&gt;&lt;br /&gt;void swap( int &amp; x, int &amp; y )&lt;br /&gt;{&lt;br /&gt; int temp = x;&lt;br /&gt; x = y;&lt;br /&gt; y = temp;&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;int a=2, b=3;&lt;br /&gt;&lt;br /&gt;swap( a, b );&lt;br /&gt;&lt;br /&gt;Basically, inside the function there won't be any copy of the arguments "x" and "y" instead they refer to original variables a and b. so no extra memory needed to pass arguments and it is more efficient. &lt;br /&gt; &lt;br /&gt;&lt;br /&gt;When do use "const" reference arguments in function?&lt;br /&gt;&lt;br /&gt;a) Using const protects you against programming errors that inadvertently alter data.&lt;br /&gt;b) Using const allows function to process both const and non-const actual arguments, while a function without const in the prototype can only accept non constant arguments.&lt;br /&gt;c) Using a const reference allows the function to generate and use a temporary variable appropriately.&lt;br /&gt; &lt;br /&gt;&lt;br /&gt;When are temporary variables created by C++ compiler?&lt;br /&gt;&lt;br /&gt;Provided that function parameter is a "const reference", compiler generates temporary variable in following 2 ways.&lt;br /&gt;&lt;br /&gt;a) The actual argument is the correct type, but it isn't Lvalue&lt;br /&gt;&lt;br /&gt;double Cube(const double &amp; num)&lt;br /&gt;{&lt;br /&gt;  num = num * num * num;&lt;br /&gt;  return num;&lt;br /&gt;&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;double temp = 2.0;&lt;br /&gt;double value = cube(3.0 + temp); // argument is a expression and not a Lvalue;&lt;br /&gt;&lt;br /&gt;b) The actual argument is of the wrong type, but of a type that can be converted to the correct type&lt;br /&gt;&lt;br /&gt;long temp = 3L;&lt;br /&gt;double value = cuberoot ( temp); // long to double conversion &lt;br /&gt;&lt;br /&gt;&lt;br /&gt;What is virtual function?&lt;br /&gt;&lt;br /&gt;When derived class overrides the base class method by redefining the same function, then if client wants to access redefined the method from derived class through a pointer from base class object, then you must define this function in base class as virtual function.&lt;br /&gt;&lt;br /&gt;class parent&lt;br /&gt;{&lt;br /&gt;   void Show() &lt;br /&gt;{ &lt;br /&gt;cout &lt;&lt; "i'm parent" &lt;&lt; endl;&lt;br /&gt;}&lt;br /&gt;};&lt;br /&gt;&lt;br /&gt;class child: public parent&lt;br /&gt;{&lt;br /&gt;   void Show() &lt;br /&gt;{ &lt;br /&gt;cout &lt;&lt; "i'm child" &lt;&lt; endl;&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;};&lt;br /&gt;&lt;br /&gt;parent * parent_object_ptr = new child;&lt;br /&gt;&lt;br /&gt;parent_object_ptr-&gt;show() // calls parent-&gt;show() i &lt;br /&gt;&lt;br /&gt;now we goto virtual world...&lt;br /&gt;&lt;br /&gt;class parent&lt;br /&gt;{&lt;br /&gt;   virtual void Show() &lt;br /&gt;{ &lt;br /&gt;cout &lt;&lt; "i'm parent" &lt;&lt; endl;&lt;br /&gt;}&lt;br /&gt;};&lt;br /&gt;&lt;br /&gt;class child: public parent&lt;br /&gt;{&lt;br /&gt;   void Show() &lt;br /&gt;{ &lt;br /&gt;cout &lt;&lt; "i'm child" &lt;&lt; endl;&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;};&lt;br /&gt;&lt;br /&gt;parent * parent_object_ptr = new child;&lt;br /&gt;&lt;br /&gt;parent_object_ptr-&gt;show() // calls child-&gt;show()  &lt;br /&gt;&lt;br /&gt;&lt;br /&gt;What is pure virtual function? or what is abstract class?&lt;br /&gt;&lt;br /&gt;When you define only function prototype in a base class without implementation and do the complete implementation in derived class. This base class is called abstract class and client won't able to instantiate an object using this base class.&lt;br /&gt;&lt;br /&gt;You can make a pure virtual function or abstract class this way..&lt;br /&gt;&lt;br /&gt;class Boo&lt;br /&gt;{&lt;br /&gt;void foo() = 0;&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;Boo MyBoo; // compilation error&lt;br /&gt;&lt;br /&gt; &lt;br /&gt;What is Memory alignment??&lt;br /&gt;&lt;br /&gt;The term alignment primarily means the tendency of an address pointer value to be a multiple of some power of two. So a pointer with two byte alignment has a zero in the least significant bit. And a pointer with four byte alignment has a zero in both the two least significant bits. And so on. More alignment means a longer sequence of zero bits in the lowest bits of a pointer.&lt;br /&gt;&lt;br /&gt;What problem does the namespace feature solve?&lt;br /&gt;&lt;br /&gt;Multiple providers of libraries might use common global identifiers causing a name collision when an application tries to link with two or more such libraries. The namespace feature surrounds a library's external declarations with a unique namespace that eliminates the potential for those collisions.&lt;br /&gt;&lt;br /&gt;namespace [identifier] { namespace-body }&lt;br /&gt;&lt;br /&gt;A namespace declaration identifies and assigns a name to a declarative region.&lt;br /&gt;The identifier in a namespace declaration must be unique in the declarative region in which it is used. The identifier is the name of the namespace and is used to reference its members.&lt;br /&gt;&lt;br /&gt;What is the use of 'using' declaration?&lt;br /&gt;&lt;br /&gt;A using declaration makes it possible to use a name from a namespace without the scope operator. &lt;br /&gt;&lt;br /&gt;What is an Iterator class?&lt;br /&gt;&lt;br /&gt;A class that is used to traverse through the objects maintained by a container class. There are five categories of iterators: input iterators, output iterators, forward iterators, bidirectional iterators, random access. An iterator is an entity that gives access to the contents of a container object without violating encapsulation constraints. Access to the contents is granted on a one-at-a-time basis in order. The order can be storage order (as in lists and queues) or some arbitrary order (as in array indices) or according to some ordering relation (as in an ordered binary tree). The iterator is a construct, which provides an interface that, when called, yields either the next element in the container, or some value denoting the fact that there are no more elements to examine. Iterators hide the details of access to and update of the elements of a container class. Something like a pointer. &lt;br /&gt;&lt;br /&gt;What is a dangling pointer?&lt;br /&gt;&lt;br /&gt;A dangling pointer arises when you use the address of an object after its lifetime is over. This may occur in situations like returning addresses of the automatic variables from a function or using the address of the memory block after it is freed.&lt;br /&gt;&lt;br /&gt;What do you mean by Stack unwinding?&lt;br /&gt;&lt;br /&gt;It is a process during exception handling when the destructor is called for all local objects in the stack between the place where the exception was thrown and where it is caught.&lt;br /&gt;&lt;br /&gt;Name the operators that cannot be overloaded??&lt;br /&gt;&lt;br /&gt;sizeof, ., .*, .-&gt;, ::, ?: &lt;br /&gt;&lt;br /&gt;What is a container class? What are the types of container classes?&lt;br /&gt;&lt;br /&gt;A container class is a class that is used to hold objects in memory or external storage. A container class acts as a generic holder. A container class has a predefined behavior and a well-known interface. A container class is a supporting class whose purpose is to hide the topology used for maintaining the list of objects in memory. When a container class contains a group of mixed objects, the container is called a heterogeneous container; when the container is holding a group of objects that are all the same, the container is called a homogeneous container. &lt;br /&gt;&lt;br /&gt;What is inline function??&lt;br /&gt;&lt;br /&gt;The __inline keyword tells the compiler to substitute the code within the function definition for every instance of a function call. However, substitution occurs only at the compiler's discretion. For example, the compiler does not inline a function if its address is taken or if it is too large to inline.&lt;br /&gt;   &lt;br /&gt;&lt;br /&gt;What is overloading??&lt;br /&gt;&lt;br /&gt;With the C++ language, you can overload functions and operators. Overloading is the practice of supplying more than one definition for a given function name in the same scope.&lt;br /&gt;&lt;br /&gt;- Any two functions in a set of overloaded functions must have different argument lists.&lt;br /&gt;- Overloading functions with argument lists of the same types, based on return type alone, is an error. &lt;br /&gt;&lt;br /&gt;What is Overriding?&lt;br /&gt;&lt;br /&gt;To override a method, a subclass of the class that originally declared the method must declare a method with the same name, return type (or a subclass of that return type), and same parameter list.&lt;br /&gt;The definition of the method overriding is: &lt;br /&gt;· Must have same method name.  &lt;br /&gt;· Must have same data type.  &lt;br /&gt;· Must have same argument list.  &lt;br /&gt;Overriding a method means that replacing a method functionality in child class. To imply overriding functionality we need parent and child classes. In the child class you define the same method signature as one defined in the parent class.&lt;br /&gt;&lt;br /&gt;What is "this" pointer?&lt;br /&gt;&lt;br /&gt;The this pointer is a pointer accessible only within the member functions of a class, struct, or union type. It points to the object for which the member function is called. Static member functions do not have a this pointer.&lt;br /&gt;&lt;br /&gt;When a nonstatic member function is called for an object, the address of the object is passed as a hidden argument to the function. For example, the following function call&lt;br /&gt;&lt;br /&gt;myDate.setMonth( 3 );&lt;br /&gt;&lt;br /&gt;can be interpreted this way:&lt;br /&gt;&lt;br /&gt;setMonth( &amp;myDate, 3 );&lt;br /&gt;&lt;br /&gt;The object's address is available from within the member function as the this pointer. It is legal, though unnecessary, to use the this pointer when referring to members of the class.&lt;br /&gt;&lt;br /&gt;What happens when you make call "delete this;" ??&lt;br /&gt;&lt;br /&gt;The code has two built-in pitfalls. First, if it executes in a member function for an extern, static, or automatic object, the program will probably crash as soon as the delete statement executes. There is no portable way for an object to tell that it was instantiated on the heap, so the class cannot assert that its object is properly instantiated. Second, when an object commits suicide this way, the using program might not know about its demise. As far as the instantiating program is concerned, the object remains in scope and continues to exist even though the object did itself in. Subsequent dereferencing of the pointer can and usually does lead to disaster.&lt;br /&gt;&lt;br /&gt;You should never do this. Since compiler does not know whether the object was allocated on the stack or on the heap, "delete this" could cause a disaster.&lt;br /&gt;&lt;br /&gt;How virtual functions are implemented C++?&lt;br /&gt;&lt;br /&gt;Virtual functions are implemented using a table of function pointers, called the vtable.  There is one entry in the table per virtual function in the class.  This table is created by the constructor of the class.  When a derived class is constructed, its base class is constructed first which creates the vtable.  If the derived class overrides any of the base classes virtual functions, those entries in the vtable are overwritten by the derived class constructor.  This is why you should never call virtual functions from a constructor: because the vtable entries for the object may not have been set up by the derived class constructor yet, so you might end up calling base class implementations of those virtual functions&lt;br /&gt;&lt;br /&gt;What is name mangling in C++??&lt;br /&gt;&lt;br /&gt;The process of encoding the parameter types with the function/method name into a unique name is called name mangling. The inverse process is called demangling.&lt;br /&gt;&lt;br /&gt;For example Foo::bar(int, long) const is mangled as `bar__C3Fooil'. &lt;br /&gt;For a constructor, the method name is left out. That is Foo::Foo(int, long) const is mangled as `__C3Fooil'.&lt;br /&gt;&lt;br /&gt;What is the difference between a pointer and a reference?&lt;br /&gt;&lt;br /&gt;A reference must always refer to some object and, therefore, must always be initialized; pointers do not have such restrictions. A pointer can be reassigned to point to different objects while a reference always refers to an object with which it was initialized.&lt;br /&gt;&lt;br /&gt;How are prefix and postfix versions of operator++() differentiated?&lt;br /&gt;&lt;br /&gt;The postfix version of operator++() has a dummy parameter of type int. The prefix version does not have dummy parameter.&lt;br /&gt;&lt;br /&gt;What is the difference between const char *myPointer and char *const myPointer?&lt;br /&gt;&lt;br /&gt;Const char *myPointer is a non constant pointer to constant data; while char *const myPointer is a constant pointer to non constant data.&lt;br /&gt;&lt;br /&gt;How can I handle a constructor that fails?&lt;br /&gt;&lt;br /&gt;throw an exception. Constructors don't have a return type, so it's not possible to use return codes. The best way to signal constructor failure is therefore to throw an exception.&lt;br /&gt;&lt;br /&gt;How can I handle a destructor that fails?&lt;br /&gt;&lt;br /&gt;Write a message to a log-file. But do not throw an exception. &lt;br /&gt;The C++ rule is that you must never throw an exception from a destructor that is being called during the "stack unwinding" process of another exception. For example, if someone says throw Foo(), the stack will be unwound so all the stack frames between the throw Foo() and the } catch (Foo e) { will get popped. This is called stack unwinding. &lt;br /&gt;During stack unwinding, all the local objects in all those stack frames are destructed. If one of those destructors throws an exception (say it throws a Bar object), the C++ runtime system is in a no-win situation: should it ignore the Bar and end up in the } catch (Foo e) { where it was originally headed? Should it ignore the Foo and look for a } catch (Bar e) { handler? There is no good answer -- either choice loses information. &lt;br /&gt;So the C++ language guarantees that it will call terminate() at this point, and terminate() kills the process. Bang you're dead. &lt;br /&gt;&lt;br /&gt;What is Virtual Destructor?&lt;br /&gt;&lt;br /&gt;Using virtual destructors, you can destroy objects without knowing their type - the correct destructor for the object is invoked using the virtual function mechanism. Note that destructors can also be declared as pure virtual functions for abstract classes.&lt;br /&gt;&lt;br /&gt;if someone will derive from your class, and if someone will say "new Derived", where "Derived" is derived from your class, and if someone will say delete p, where the actual object's type is "Derived" but the pointer p's type is your class.&lt;br /&gt;&lt;br /&gt; &lt;br /&gt;Can you think of a situation where your program would crash without reaching the breakpoint which you set at the beginning of main()?&lt;br /&gt;&lt;br /&gt;C++ allows for dynamic initialization of global variables before main() is invoked. It is possible that initialization of global will invoke some function. If this function crashes the crash will occur before main() is entered. &lt;br /&gt;&lt;br /&gt;Name two cases where you MUST use initialization list as opposed to assignment in constructors.&lt;br /&gt;&lt;br /&gt;Both non-static const data members and reference data members cannot be assigned values; instead, you should use initialization list to initialize them. &lt;br /&gt;&lt;br /&gt;Can you overload a function based only on whether a parameter is a value or a reference?&lt;br /&gt;&lt;br /&gt;No. Passing by value and by reference looks identical to the caller. &lt;br /&gt;&lt;br /&gt;What are the differences between a C++ struct and C++ class?&lt;br /&gt;&lt;br /&gt;The default member and base class access specifiers are different.&lt;br /&gt;&lt;br /&gt;The C++ struct has all the features of the class. The only differences are that a struct defaults to public member access and public base class inheritance, and a class defaults to the private access specifier and private base class inheritance. &lt;br /&gt;&lt;br /&gt;What does extern "C" int func(int *, Foo) accomplish?&lt;br /&gt;&lt;br /&gt;It will turn off "name mangling" for func so that one can link to code compiled by a C compiler. &lt;br /&gt;&lt;br /&gt;How do you access the static member of a class?&lt;br /&gt;&lt;br /&gt;&lt;ClassName&gt;::&lt;StaticMemberName&gt;&lt;br /&gt;&lt;br /&gt;What is multiple inheritance(virtual inheritance)? What are its advantages and disadvantages?&lt;br /&gt;&lt;br /&gt;Multiple Inheritance is the process whereby a child can be derived from more than one parent class. The advantage of multiple inheritance is that it allows a class to inherit the functionality of more than one base class thus allowing for modeling of complex relationships. The disadvantage of multiple inheritance is that it can lead to a lot of confusion(ambiguity) when two base classes implement a method with the same name.&lt;br /&gt;&lt;br /&gt;What are the access privileges in C++? What is the default access level?&lt;br /&gt;&lt;br /&gt;The access privileges in C++ are private, public and protected. The default access level assigned to members of a class is private. Private members of a class are accessible only within the class and by friends of the class. Protected members are accessible by the class itself and it's sub-classes. Public members of a class can be accessed by anyone.&lt;br /&gt;&lt;br /&gt;What is a nested class? Why can it be useful?&lt;br /&gt;&lt;br /&gt;A nested class is a class enclosed within the scope of another class. For example:&lt;br /&gt;&lt;br /&gt;  //  Example 1: Nested class&lt;br /&gt;  //&lt;br /&gt;  class OuterClass&lt;br /&gt;  {&lt;br /&gt;    class NestedClass&lt;br /&gt;    {&lt;br /&gt;      // ...&lt;br /&gt;    };&lt;br /&gt;    // ...&lt;br /&gt;  };&lt;br /&gt;Nested classes are useful for organizing code and controlling access and dependencies. Nested classes obey access rules just like other parts of a class do; so, in Example 1, if NestedClass is public then any code can name it as OuterClass::NestedClass. Often nested classes contain private implementation details, and are therefore made private; in Example 1, if NestedClass is private, then only OuterClass's members and friends can use NestedClass.&lt;br /&gt;&lt;br /&gt;When you instantiate as outer class, it won't instantiate inside class.&lt;br /&gt;&lt;br /&gt;What is a local class? Why can it be useful?&lt;br /&gt;&lt;br /&gt;local class is a class defined within the scope of a function -- any function, whether a member function or a free function. For example:&lt;br /&gt;&lt;br /&gt;  //  Example 2: Local class&lt;br /&gt;  //&lt;br /&gt;  int f()&lt;br /&gt;  {&lt;br /&gt;    class LocalClass&lt;br /&gt;    {&lt;br /&gt;      // ...&lt;br /&gt;    };&lt;br /&gt;    // ...&lt;br /&gt;  };&lt;br /&gt;Like nested classes, local classes can be a useful tool for managing code dependencies. &lt;br /&gt;&lt;br /&gt;&lt;br /&gt;Can a copy constructor accept an object of the same class as parameter, instead of reference of the object?&lt;br /&gt; &lt;br /&gt;&lt;br /&gt;No. It is specified in the definition of the copy constructor itself. It should generate an error if a programmer specifies a copy constructor with a first argument that is an object and not a reference.&lt;br /&gt;&lt;br /&gt;if you have any comments/questions  please email me at&lt;br /&gt;&lt;br /&gt;copyright (c) 2004, Satish Shetty, All rights reserved,&lt;br /&gt;&lt;br /&gt;visit my homepage at http://www.shettysoft.com&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5349536066535334815-6019311823745399245?l=yourmagicsearchengine.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://yourmagicsearchengine.blogspot.com/feeds/6019311823745399245/comments/default' title='帖子评论'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=5349536066535334815&amp;postID=6019311823745399245' title='1 条评论'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5349536066535334815/posts/default/6019311823745399245'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5349536066535334815/posts/default/6019311823745399245'/><link rel='alternate' type='text/html' href='http://yourmagicsearchengine.blogspot.com/2010/06/c-interview-questions.html' title='C++ interview questions'/><author><name>Yichuan Cai's home page</name><uri>http://www.blogger.com/profile/10921376371265973337</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5349536066535334815.post-2616146901120575360</id><published>2010-01-19T14:08:00.000-08:00</published><updated>2010-01-19T14:09:23.773-08:00</updated><title type='text'>some good sentences for paper writing</title><content type='html'>1 precision and recall experiment should be conducted to see whether this method can actually improve the quality of the final ranking list&lt;br /&gt;2 about the similarity threshold, the threshold is relied on the user to set it. However, how such threshold should be set is not clearly illustrated.&lt;br /&gt;3 The authors of this paper tried to address a new problem, of developing a new distance measure for subtrees, which has not been proposed / solved before&lt;br /&gt;&lt;br /&gt;4 and avoided the drawbacks of total mapping &lt;br /&gt;&lt;br /&gt;5 there is just one sentence mentioning a very important information, and no further illustration is presented later &lt;br /&gt;&lt;br /&gt;6 the authors claimed that &lt;br /&gt;&lt;br /&gt;7 However, such claim is not convincing, and more details and illustration are needed to convince the reader that the efficiency of the PPI is really OK, and this should also be demonstrated in the later experiment section&lt;br /&gt;&lt;br /&gt;8 This paper identified two critical problems/drawbacks of previous works &lt;br /&gt;&lt;br /&gt;9 decreasing the page rank of spam sites could be directly achieved by using previous work&lt;br /&gt;&lt;br /&gt;10 We three reviewers agree with some weak points of the paper, such as unavailability of category tags for most of the pages, the to-be-improved presentation and in-comprehensive experiment.&lt;br /&gt;&lt;br /&gt;the which is not significant.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////&lt;br /&gt;some points in writing the &lt;br /&gt;1 Do NOT start a sentence with “AND”. I have said this many times, and hope this is the last time.also not add the First&lt;br /&gt;2 A paper must be consistent, so does a review.Not enough pages should not count as a weak point to reject paper&lt;br /&gt;3 author+"proposed to" &lt;br /&gt;4 "presents" should be "represent"&lt;br /&gt;5 be concise &lt;br /&gt;6 explain that why&lt;br /&gt;7 method-&gt;technique&lt;br /&gt;&lt;br /&gt;////////////////////////////////////////////////&lt;br /&gt;major problem:&lt;br /&gt;1 &lt;br /&gt;&lt;br /&gt;2 's and in the begining&lt;br /&gt;&lt;br /&gt;////////////////////////////////////////////&lt;br /&gt;question: to merge them?&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5349536066535334815-2616146901120575360?l=yourmagicsearchengine.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://yourmagicsearchengine.blogspot.com/feeds/2616146901120575360/comments/default' title='帖子评论'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=5349536066535334815&amp;postID=2616146901120575360' title='0 条评论'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5349536066535334815/posts/default/2616146901120575360'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5349536066535334815/posts/default/2616146901120575360'/><link rel='alternate' type='text/html' href='http://yourmagicsearchengine.blogspot.com/2010/01/some-good-sentences-for-paper-writing.html' title='some good sentences for paper writing'/><author><name>Yichuan Cai's home page</name><uri>http://www.blogger.com/profile/10921376371265973337</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5349536066535334815.post-3930347517094018216</id><published>2009-11-03T10:05:00.001-08:00</published><updated>2009-11-03T10:05:53.022-08:00</updated><title type='text'>c++ Precedence</title><content type='html'>http://www.cppreference.com/wiki/operator_precedencet&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5349536066535334815-3930347517094018216?l=yourmagicsearchengine.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://yourmagicsearchengine.blogspot.com/feeds/3930347517094018216/comments/default' title='帖子评论'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=5349536066535334815&amp;postID=3930347517094018216' title='0 条评论'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5349536066535334815/posts/default/3930347517094018216'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5349536066535334815/posts/default/3930347517094018216'/><link rel='alternate' type='text/html' href='http://yourmagicsearchengine.blogspot.com/2009/11/c-precedence.html' title='c++ Precedence'/><author><name>Yichuan Cai's home page</name><uri>http://www.blogger.com/profile/10921376371265973337</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5349536066535334815.post-8945843784714214162</id><published>2009-10-27T10:12:00.000-07:00</published><updated>2009-10-27T10:13:16.910-07:00</updated><title type='text'>C# string processing</title><content type='html'>trim-&gt; only deal with the case related to the beginning and the end&lt;br /&gt;replace-&gt; can deal with the case in the middle&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5349536066535334815-8945843784714214162?l=yourmagicsearchengine.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://yourmagicsearchengine.blogspot.com/feeds/8945843784714214162/comments/default' title='帖子评论'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=5349536066535334815&amp;postID=8945843784714214162' title='0 条评论'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5349536066535334815/posts/default/8945843784714214162'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5349536066535334815/posts/default/8945843784714214162'/><link rel='alternate' type='text/html' href='http://yourmagicsearchengine.blogspot.com/2009/10/c-string-processing.html' title='C# string processing'/><author><name>Yichuan Cai's home page</name><uri>http://www.blogger.com/profile/10921376371265973337</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5349536066535334815.post-427645663324558463</id><published>2009-07-08T17:33:00.001-07:00</published><updated>2009-07-08T17:35:30.881-07:00</updated><title type='text'>Java' List +template</title><content type='html'>ArrayList&lt;Vertex&gt; m =new ArrayList&lt;Vertex&gt;();&lt;br /&gt;  List&lt;Vertex&gt; minPath =m.subList(0, 0);&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;Java' s List class even can not initialize with the templete, feeling weird about it.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5349536066535334815-427645663324558463?l=yourmagicsearchengine.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://yourmagicsearchengine.blogspot.com/feeds/427645663324558463/comments/default' title='帖子评论'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=5349536066535334815&amp;postID=427645663324558463' title='0 条评论'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5349536066535334815/posts/default/427645663324558463'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5349536066535334815/posts/default/427645663324558463'/><link rel='alternate' type='text/html' href='http://yourmagicsearchengine.blogspot.com/2009/07/java-list-template.html' title='Java&apos; List +template'/><author><name>Yichuan Cai's home page</name><uri>http://www.blogger.com/profile/10921376371265973337</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5349536066535334815.post-5049421187199877181</id><published>2009-07-01T10:28:00.000-07:00</published><updated>2009-07-01T10:29:35.271-07:00</updated><title type='text'>my sql database population</title><content type='html'>1 add bar as the database or table name&lt;br /&gt;SQL_MODE=ANSI_QUOTES&lt;br /&gt;2&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5349536066535334815-5049421187199877181?l=yourmagicsearchengine.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://yourmagicsearchengine.blogspot.com/feeds/5049421187199877181/comments/default' title='帖子评论'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=5349536066535334815&amp;postID=5049421187199877181' title='0 条评论'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5349536066535334815/posts/default/5049421187199877181'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5349536066535334815/posts/default/5049421187199877181'/><link rel='alternate' type='text/html' href='http://yourmagicsearchengine.blogspot.com/2009/07/my-sql-database-population.html' title='my sql database population'/><author><name>Yichuan Cai's home page</name><uri>http://www.blogger.com/profile/10921376371265973337</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5349536066535334815.post-7071465900686090265</id><published>2007-05-29T23:04:00.000-07:00</published><updated>2007-05-29T23:19:36.175-07:00</updated><title type='text'>ASP.NET调用DLL（C#)</title><content type='html'>首先建立一个bin文件夹，然后将dll文件拷贝到该bin文件夹下，&lt;br /&gt;然后在程序里使用&lt;br /&gt;using namespace;&lt;br /&gt;或者&lt;br /&gt;&lt;%@ import namespace="your-namespace" %&gt;&lt;br /&gt; 的方式导入该命名空间，接下来就可以使用dll里面的函数了。&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;、创建C# 类库 (Dll) &lt;br /&gt;以前在VC++中创建一个dll文件不能说简单，但在Visual C# 中，这将同样是轻而易举的事情。下面的介绍分成两部分：1、创建DLL，2、在客户端测试dll。 &lt;br /&gt;（1）创建DLL &lt;br /&gt;首先创建一个空的类库工程。在VS.NET集成环境（IDE）中选择“文件－&gt;新建－&gt;工程文件－&gt;Visual C# 工程－&gt;类库”，点击Browse（浏览）按钮选择工程文件名和相应的目录，再点击 OK。 &lt;br /&gt;接着看看工程和它的相关文件。Solution Explorer（解决方案探测器）向工程中增加两个C# 类，第一个是 AssemblyInfo.cs ，第二个是Class1.cs。我们不讨论AssemblyInfo，重点介绍 Class1.cs。 &lt;br /&gt;&lt;br /&gt; &lt;br /&gt;双击Class1.cs，就能看到一个名称空间mcMath。我们将在客户机引用这个名称空间以使用这个类库： &lt;br /&gt;namespace mcMath &lt;br /&gt;{ &lt;br /&gt;using System; &lt;br /&gt;/// &lt;summary&gt; &lt;br /&gt;/// Summary description for Class1. &lt;br /&gt;/// &lt;/summary&gt; &lt;br /&gt;public class Class1 &lt;br /&gt;{ &lt;br /&gt;public Class1() &lt;br /&gt;{ &lt;br /&gt;// &lt;br /&gt;// TODO: Add Constructor Logic here &lt;br /&gt;// &lt;br /&gt;} &lt;br /&gt;} &lt;br /&gt;} &lt;br /&gt;现在就可以Build（构造）这个工程了。Build（构造）完毕后，就会在工程文件的bin/debug 目录中生成mcMath.dll文件。 &lt;br /&gt;增加一个方法 &lt;br /&gt;从View （视图）菜单中打开ClassView（类视图），开始只显示Class1，没有方法和属性。现在来增加一个方法和一个属性。 &lt;br /&gt;用鼠标右键单击“Class1”，选择“Add（增加）－&gt; Add Method（增加方法）”，这时将弹出C# 方法生成向导： &lt;br /&gt;&lt;br /&gt; &lt;br /&gt;在这个窗口中增加方法名、存取类型、返回类型、参数以及注释信息。使用Add（增加）和Remove（取消）按钮可分别从参数列表中增加和取消参数。这里增加了一个方法long Add( long val1, long val2 )，它负责将两个数字相加并返回和。 &lt;br /&gt;增加一个属性 &lt;br /&gt;同理可以通过C#属性生成向导，向类中增加一个属性： &lt;br /&gt;&lt;br /&gt; &lt;br /&gt;增加了一个方法和一个属性后， Class1变成下图所示的样子： &lt;br /&gt;&lt;br /&gt; &lt;br /&gt;仔细观察这个 Class1，你会发现C#的向导程序向类中增加了如下两个函数： &lt;br /&gt;public long Add (long val1, long val2) &lt;br /&gt;{ &lt;br /&gt;return 0; &lt;br /&gt;} &lt;br /&gt; &lt;br /&gt;public bool Extra &lt;br /&gt;{ &lt;br /&gt;get &lt;br /&gt;{ &lt;br /&gt;return true; &lt;br /&gt;} &lt;br /&gt;set &lt;br /&gt;{ &lt;br /&gt;} &lt;br /&gt;} &lt;br /&gt;向类中增加代码 &lt;br /&gt;这里把Class1修改成为 mcMathComp ，因为 Class1是个容易造成混淆的名字，当想将这个类用在一个客户应用程序中时会造成问题。下面的代码对上面的做了些调整： &lt;br /&gt;namespace mcMath &lt;br /&gt;{ &lt;br /&gt;using System; &lt;br /&gt;public class mcMathComp &lt;br /&gt;{ &lt;br /&gt;private bool bTest = false; &lt;br /&gt;public mcMathComp() &lt;br /&gt;{ &lt;br /&gt;} &lt;br /&gt;public long Add (long val1, long val2) &lt;br /&gt;{ &lt;br /&gt;return val1 + val2; &lt;br /&gt;} &lt;br /&gt;public bool Extra &lt;br /&gt;{ &lt;br /&gt;get &lt;br /&gt;{ &lt;br /&gt;return bTest; &lt;br /&gt;} &lt;br /&gt;set &lt;br /&gt;{ &lt;br /&gt;bTest = Extra ; &lt;br /&gt;} &lt;br /&gt;} &lt;br /&gt;} &lt;br /&gt;} &lt;br /&gt;构造 dll &lt;br /&gt;选择Build菜单创建dll文件，如果一切OK，就会在工程文件的 bin\debug目录生成dll文件。 &lt;br /&gt;（2）在客户端测试 dll &lt;br /&gt;在客户端调用dll的方法和属性也是非常简单的工作，请遵照下面的步骤执行： &lt;br /&gt;① 创建控制台应用程序 &lt;br /&gt;在VS.NET IDE集成环境中选择“文件－&gt; 新建－&gt;工程文件－&gt;Visual C#工程文件－&gt;控制台应用程序”，最终将在这个控制台应用程序中测试dll。 &lt;br /&gt;② 增加名称空间的引用 &lt;br /&gt;选择“工程－&gt;添加引用”（Project-&gt;Add reference），然后浏览文件找到dll，点击 Ok： &lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt; &lt;br /&gt;引用添加向导程序将向当前工程文件中增加对相关库的引用： &lt;br /&gt;&lt;br /&gt; &lt;br /&gt;③ 调用mcMath名称空间，创建 mcMathComp 的对象，并调用其方法和属性。 &lt;br /&gt;现在距离调用组件的方法和属性只有一步之遥了。请按照以下步骤进行： &lt;br /&gt;●引用名称空间：using mcMath &lt;br /&gt;●创建一个 mcMathComp的对象：mcMathComp cls = new mcMathComp(); &lt;br /&gt;●调用方法和属性 &lt;br /&gt;mcMathComp cls = new mcMathComp(); &lt;br /&gt;long lRes = cls.Add( 23, 40 ); &lt;br /&gt;cls.Extra = false; &lt;br /&gt;以下是完整的工程文件代码： &lt;br /&gt;namespace mcClient &lt;br /&gt;{ &lt;br /&gt;using System; &lt;br /&gt;using mcMath; &lt;br /&gt;/// &lt;summary&gt; &lt;br /&gt;/// Summary description for Class1. &lt;br /&gt;/// &lt;/summary&gt; &lt;br /&gt;public class Class1 &lt;br /&gt;{ &lt;br /&gt;public Class1() &lt;br /&gt;{ &lt;br /&gt;// &lt;br /&gt;// TODO: Add Constructor Logic here &lt;br /&gt;// &lt;br /&gt;} &lt;br /&gt;public static int Main(string[] args) &lt;br /&gt;{ &lt;br /&gt;mcMathComp cls = new mcMathComp(); &lt;br /&gt;long lRes = cls.Add( 23, 40 ); &lt;br /&gt;cls.Extra = false; &lt;br /&gt;return 0; &lt;br /&gt;} &lt;br /&gt;}&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5349536066535334815-7071465900686090265?l=yourmagicsearchengine.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://yourmagicsearchengine.blogspot.com/feeds/7071465900686090265/comments/default' title='帖子评论'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=5349536066535334815&amp;postID=7071465900686090265' title='1 条评论'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5349536066535334815/posts/default/7071465900686090265'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5349536066535334815/posts/default/7071465900686090265'/><link rel='alternate' type='text/html' href='http://yourmagicsearchengine.blogspot.com/2007/05/aspnetdllc.html' title='ASP.NET调用DLL（C#)'/><author><name>Yichuan Cai's home page</name><uri>http://www.blogger.com/profile/10921376371265973337</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5349536066535334815.post-7872520840342445301</id><published>2007-05-29T20:03:00.001-07:00</published><updated>2007-05-29T20:03:56.069-07:00</updated><title type='text'>IIS报Service Unavailable错的解决方案</title><content type='html'>过检测，发现我的windows2003的应用程序池被改成DefaultAppPool了！改成原来的MsSharePointAppPool就可以了。&lt;br /&gt;&lt;br /&gt;转载一篇详细的文章：&lt;br /&gt;&lt;br /&gt;浏览 Windows SharePoint Services Web 站点时收到“Service Unavailable”（服务不可用）错误信息 &lt;br /&gt;&lt;br /&gt;症状 &lt;br /&gt;当您浏览一个 Windows SharePoint Services Web 站点时，您可能会收到下面的错误信息： &lt;br /&gt;Service Unavailable &lt;br /&gt;原因 &lt;br /&gt;如果  Microsoft Internet 信息服务 (IIS) 6.0 中没有正确地配置用于虚拟服务器的应用程序池，就可能会发生此问题。此问题可能会在存在下列一种或多种情况时发生： •应用程序池没有运行。 &lt;br /&gt;•应用程序池帐户使用的密码不正确。 &lt;br /&gt;•应用程序池帐户不是服务器上的 IIS_WPG 和 STS_WPG 这两个组的公共成员。 &lt;br /&gt;&lt;br /&gt;解决方案 &lt;br /&gt;要解决此问题，请按照下列步骤操作： 1.验证是否已为虚拟服务器配置了应用程序池。默认的应用程序池是 MSSharePointPortalAppPool。 &lt;br /&gt;&lt;br /&gt;请按照下列步骤来确定虚拟服务器正在使用的应用程序池。 a. 单击“开始”，指向“管理工具”，然后单击“Internet 信息服务 (IIS) 管理器”。 &lt;br /&gt;b. 展开“ServerName”，展开“Web 站点”，右键单击虚拟服务器，然后单击“属性”。 &lt;br /&gt;c. 单击“主目录”选项卡。 &lt;br /&gt;&lt;br /&gt;为虚拟服务器配置的应用程序池列在“应用程序池”框中。 &lt;br /&gt;d. 单击“确定”。 &lt;br /&gt;&lt;br /&gt;2.验证应用程序池帐户使用的密码是否正确。IIS 不会自动轮询 Active Directory 目录服务中的密码更改。如果应用程序池帐户是一个域帐户，其密码已过期，则在为此帐户重新指定一个新密码后，您可能会收到本文“症状”部分所描述的错误信息。 &lt;br /&gt;&lt;br /&gt;请按照下列步骤来验证应用程序池帐户所用的密码是否正确： a. 在 Internet 信息服务 (IIS) 管理器中，展开“应用程序池”。 &lt;br /&gt;b. 右键单击为虚拟服务器配置的应用程序池（例如，右键单击“MSSharePointPortalAppPool”），然后单击“属性”。 &lt;br /&gt;c. 单击“标识”选项卡。 &lt;br /&gt;d. 在“密码”框中，键入列在“用户名”框中的应用程序池帐户所用的密码，然后单击“确定”。 &lt;br /&gt;e. 在“确认密码”对话框中，再次键入密码，然后单击“确定”。 &lt;br /&gt;&lt;br /&gt;3.验证应用程序池帐户是服务器上的 IIS_WPG 组和 STS_WPG 组的成员。 &lt;br /&gt;&lt;br /&gt;根据您的具体情况选用下列方法之一。 a. 在成员服务器上安装了 SharePoint Portal Server 的情况下： 1.单击“开始”，指向“管理工具”，然后单击“计算机管理”。 &lt;br /&gt;2.展开“本地用户和组”，然后展开“用户”。 &lt;br /&gt;3.右键单击虚拟服务器的应用程序池使用的帐户，然后单击“属性”。 &lt;br /&gt;4.单击“成员属于”选项卡。 &lt;br /&gt;&lt;br /&gt;验证 IIS_WPG 和 STS_WPG 是否都出现在“成员属于”列表中。如果其中之一没有列出或者两者均未列出，请根据具体情况将 IIS_WPG 组、STS_WPG 组或者这两个组添加到列表中。 &lt;br /&gt;&lt;br /&gt;b. 在域控制器上安装了 SharePoint Portal Server 的情况下： 1.启动“Active Directory 用户和计算机”。 &lt;br /&gt;2.展开“用户”。 &lt;br /&gt;3.右键单击虚拟服务器的应用程序池使用的帐户，然后单击“属性”。 &lt;br /&gt;4.单击“成员属于”选项卡。 &lt;br /&gt;&lt;br /&gt;验证 IIS_WPG 和 STS_WPG 都出现在“成员属于”列表中。如果其中之一没有列出或者两者均未列出，请根据具体情况将 IIS_WPG 组、STS_WPG 组或者这两个组添加到列表中。 &lt;br /&gt;&lt;br /&gt;&lt;br /&gt;4.重新启动 IIS 以回收应用程序池： a. 在 Internet 信息服务 (IIS) 管理器中，右键单击“ServerName”，指向“所有任务”，然后单击“重新启动 IIS”。 &lt;br /&gt;b. 单击“在 ServerName 上重新启动 Internet 信息服务”，然后单击“确定”。&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5349536066535334815-7872520840342445301?l=yourmagicsearchengine.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://yourmagicsearchengine.blogspot.com/feeds/7872520840342445301/comments/default' title='帖子评论'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=5349536066535334815&amp;postID=7872520840342445301' title='0 条评论'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5349536066535334815/posts/default/7872520840342445301'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5349536066535334815/posts/default/7872520840342445301'/><link rel='alternate' type='text/html' href='http://yourmagicsearchengine.blogspot.com/2007/05/iisservice-unavailable.html' title='IIS报Service Unavailable错的解决方案'/><author><name>Yichuan Cai's home page</name><uri>http://www.blogger.com/profile/10921376371265973337</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5349536066535334815.post-7151561757153310882</id><published>2007-05-16T00:04:00.000-07:00</published><updated>2007-05-29T00:14:52.602-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='数据库'/><title type='text'>文本分类子模块项目开发心得</title><content type='html'>1 第一次遇到MySQL数据库的BUG,晕翻&gt;_&lt;,害得我调试半天，原来是MYSQL把中文的“读”和“多”当成相同的字符串了，真是烂货数据库。。。&lt;br /&gt;2 后来才搞清楚，是MySQl的字符编码设置的问题，搞了一上午和一下午，发现要改：&lt;br /&gt;（1在my.ini处的2处都要改为default character set，&lt;br /&gt;（2建的所有的表都要在最后加上default charset = gbk，&lt;br /&gt;（3   string useGBK = "set names gbk";//成功调试必备的&lt;br /&gt;       DBComm = new MySQLCommand(useGBK, DBConn);&lt;br /&gt;      DBComm.ExecuteNonQuery();&lt;br /&gt;3 一些常用的MySQLConnection 的函数集&lt;br /&gt;static void Main(string[] args)&lt;br /&gt;        {&lt;br /&gt;            string sqlstr = "select * from manavatar";&lt;br /&gt;            MySQLConnection DBConn = new MySQLConnection(new MySQLConnectionString("192.168.0.13", "flashdata", "root", "root", 3306).AsString);&lt;br /&gt;            DBConn.Open();&lt;br /&gt;            //MySQLDataAdapter myadap = new MySQLDataAdapter(sqlstr, conn);&lt;br /&gt;            MySQLCommand DBComm = new MySQLCommand(sqlstr,DBConn);&lt;br /&gt;            MySQLDataReader DBReader = DBComm.ExecuteReaderEx(); //DBComm.ExecuteReaderEx();&lt;br /&gt;            MySQLDataAdapter DTAdapter = new MySQLDataAdapter(sqlstr,DBConn);&lt;br /&gt;            &lt;br /&gt;            DataSet myDataSet = new DataSet();&lt;br /&gt;            DTAdapter.Fill(myDataSet,"manavatar");&lt;br /&gt;          &lt;br /&gt;         &lt;br /&gt;            try&lt;br /&gt;            {&lt;br /&gt;                while (DBReader.Read())&lt;br /&gt;                {&lt;br /&gt;                    //Console.WriteLine("11");&lt;br /&gt;                    Console.WriteLine("DBReader:{0},\t\t\tddddd:{1},\t\t {2}",DBReader.GetString(0), DBReader.GetString(1),DBReader.GetString(3));&lt;br /&gt;                }&lt;br /&gt;                Console.WriteLine("0000");&lt;br /&gt;            }&lt;br /&gt;            catch (Exception e)&lt;br /&gt;            { &lt;br /&gt;                Console.WriteLine("读入失败！"+e.ToString());&lt;br /&gt;            }&lt;br /&gt;            finally&lt;br /&gt;            {&lt;br /&gt;                Console.WriteLine("DBReader关闭");&lt;br /&gt;                Console.WriteLine("DBConn关闭");&lt;br /&gt;                DBReader.Close();&lt;br /&gt;                //DBConn.Close();&lt;br /&gt;            }&lt;br /&gt;            &lt;br /&gt;            for (int i = 0; i &lt; myDataSet.Tables["manavatar"].Rows.Count; i++)&lt;br /&gt;            {&lt;br /&gt;                Console.WriteLine("{0}",myDataSet.Tables["manavatar"].Rows[2]["user"]);&lt;br /&gt;            }&lt;br /&gt;            &lt;br /&gt;            &lt;br /&gt;        }&lt;br /&gt;&lt;br /&gt;这是一个简单的例子。&lt;br /&gt;在这里有个问题：dataset如果没设主键的话，可能会引起一些对数库操作的问题，比如会造成updata出现错误。&lt;br /&gt;&lt;br /&gt;4 项目中导入了一个开源的DLL，MySQLDriverCS，他对C＃调用MySQL做了很好的封装&lt;br /&gt;5出现了诡异的语句INSERT INTO dic VALUES('/|\','军事20',9,1,0.181818181818182);&lt;br /&gt; 主要'/|\'是转义字符，暂时没有很好的解决方法&lt;br /&gt;&lt;br /&gt;5 如果要调用generic 的Sort方法，必须实现CompareTo接口。这个接口有一个叫CompareTo(object)方法，如果“this”大于、小于 ...&lt;br /&gt;如果想使用Array等的Find方法，必须重载Equals()方法，因为任何类都是继承Object类的。&lt;br /&gt;&lt;br /&gt;6 无法显示 XML 页。 &lt;br /&gt;使用 XSL 样式表无法查看 XML 输入。请更正错误然后单击 刷新按钮，或以后重试。 &lt;br /&gt;--------------------------------------------------------------------------------&lt;br /&gt;名称以无效字符开头。处理资源 'http://localhost/Asp.net/Default.aspx' 时出错。第 1 行，位置: 2 &lt;br /&gt;&lt;%@ Page Language="C#" AutoEventWireup="true"  CodeFile="Default.aspx.cs" Inherits="_Default" %&gt;&lt;br /&gt;===================&lt;br /&gt;&lt;br /&gt;解决办法：运行vs2005命令行（开始、所有程序、vs2005、tools，写得不准确），之后找到.net2.0的路径，我的是在C:\WINDOWS\Microsoft.NET\Framework\v2.0.50727，之后运行aspnet_regiis.exe /i，就ok了。&lt;br /&gt;&lt;br /&gt;（待续）&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5349536066535334815-7151561757153310882?l=yourmagicsearchengine.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://yourmagicsearchengine.blogspot.com/feeds/7151561757153310882/comments/default' title='帖子评论'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=5349536066535334815&amp;postID=7151561757153310882' title='0 条评论'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5349536066535334815/posts/default/7151561757153310882'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5349536066535334815/posts/default/7151561757153310882'/><link rel='alternate' type='text/html' href='http://yourmagicsearchengine.blogspot.com/2007/05/blog-post_16.html' title='文本分类子模块项目开发心得'/><author><name>Yichuan Cai's home page</name><uri>http://www.blogger.com/profile/10921376371265973337</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5349536066535334815.post-2700318103179756129</id><published>2007-05-14T23:30:00.000-07:00</published><updated>2007-05-15T20:10:19.299-07:00</updated><title type='text'>MySQL常用命令(ZZ)</title><content type='html'>MySQL常用命令 &lt;br /&gt;&lt;br /&gt;建立库： &lt;br /&gt;create database sss &lt;br /&gt;&lt;br /&gt;使用库： &lt;br /&gt;use sss &lt;br /&gt;&lt;br /&gt;显示库： &lt;br /&gt;show databases &lt;br /&gt;&lt;br /&gt;删除库： &lt;br /&gt;drop database sss &lt;br /&gt;&lt;br /&gt;建立表： &lt;br /&gt;create table object (obj_id numeric(10),obj_ra numeric(8,5),obj_de numeric(8,5),obj_mag numeric(4,2)) &lt;br /&gt;&lt;br /&gt;显示表： &lt;br /&gt;show tables &lt;br /&gt;&lt;br /&gt;显示表的结构： &lt;br /&gt;describe object &lt;br /&gt;&lt;br /&gt;删除表： &lt;br /&gt;drop table object &lt;br /&gt;&lt;br /&gt;授权外部连接： &lt;br /&gt;grant all on sss.object to sss@192.168.1.12 identified by 'mysql' &lt;br /&gt; &lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;#  casularm 发表于2006-04-06 14:59:00  IP: 218.104.71.*&lt;br /&gt;MySQL常用操作 &lt;br /&gt;&lt;br /&gt;启动： &lt;br /&gt;/etc/init.d/mysql start &lt;br /&gt;&lt;br /&gt;关闭： &lt;br /&gt;/usr/bin/mysqladmin -u root -p shutdown &lt;br /&gt;&lt;br /&gt;登陆： &lt;br /&gt;mysql -uroot -p &lt;br /&gt; &lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;#  casularm 发表于2006-08-13 21:13:00  IP: 211.162.10.*&lt;br /&gt;从外部文件向表中批量导入数据 &lt;br /&gt;load data infile 'e:/Data/minifiber.csv' into table minifiber fields terminated by ','; &lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;#  casularm 发表于2006-08-30 11:16:00  IP: 211.162.6.*&lt;br /&gt;１．备份服务器数据： &lt;br /&gt;&lt;br /&gt;mysqldump -h服务器IP地址 -u用户名 -p密码 --opt 数据库名&gt;备份数据库名 &lt;br /&gt;&lt;br /&gt;例如： &lt;br /&gt;&lt;br /&gt;mysqldump -h 192.168.1.100 -u sss -p --opt sss &gt;sss_backup.sql &lt;br /&gt;&lt;br /&gt;这条命令将服务器192.168.1.100上的sss这个数据库备份到本地计算机当前目录的sss_backup.sql这个文件中，这样，当数据库不小心损坏或数据丢失时，就可以由sss_backup.sql这个备份文件恢复了 &lt;br /&gt;&lt;br /&gt;2.　导入.sql数据到mysql数据库 &lt;br /&gt;&lt;br /&gt;mysql -h服务器IP地址 -u用户名 -p密码 -f -D 数据库名 &lt;br /&gt;备份文件名 &lt;br /&gt;&lt;br /&gt;例如： &lt;br /&gt;&lt;br /&gt;mysql -h 192.168.1.100 -u sss -p -f -D sss &lt;sss_backup.sql &lt;br /&gt;这条命令会将sss_backup.sql这个文件中的数据重新恢复到服务器sss数据库中。 &lt;br /&gt;&lt;br /&gt;3.注意事项 &lt;br /&gt;如果是新安装的数据库，需要新建个sss库 &lt;br /&gt;create database sss &lt;br /&gt;备份和导入都要有sss数据库的权限 &lt;br /&gt;grant all on sss.* to sss@192.168.1.100 identified by 'mysql' &lt;br /&gt; &lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;#  casularm 发表于2006-10-29 17:06:00  IP: 211.162.10.*&lt;br /&gt;使用PowerDesigner设计建造MySQL数据库 &lt;br /&gt;&lt;br /&gt;一、使用PowerDesigner制作建库脚本 &lt;br /&gt;1、设计CDM(Conceptual Data Model) &lt;br /&gt;2、选择 Tools -&gt; Generate Physical Data Model ，选择对应的DBMS为MySQL，生成PDM &lt;br /&gt;3、选择 Database -&gt; Generate Database ，在弹出的 Database Generation 对话框中选择脚本存取路径及脚本文件名称 &lt;br /&gt;4、点击确定后生成数据库建库脚本(*.sql) &lt;br /&gt;&lt;br /&gt;二、使用建库脚本建立数据库 &lt;br /&gt;1、登陆 mysql -u root -p &lt;br /&gt;2、建立空的databse create databse sss; &lt;br /&gt;3、建立用户 grant all on sss.* to sss@192.168.1.100 identified by 'mysql'； &lt;br /&gt;4、退出 exit; &lt;br /&gt;5、在终端中输入 mysql -h 192.168.1.100 -u sss -p &lt; Script.sql &lt;br /&gt;6、回车后输入密码即可&lt;br /&gt;&lt;br /&gt;修改某个表的字段类型及指定为空或非空&lt;br /&gt;&gt;alter table 表名称 change 字段名称 字段名称 字段类型 [是否允许非空];&lt;br /&gt;&gt;alter table 表名称 modify 字段名称 字段类型 [是否允许非空]; &lt;br /&gt;&lt;br /&gt;修改某个表的字段名称及指定为空或非空&lt;br /&gt;&gt;alter table 表名称 change 字段原名称 字段新名称 字段类型 [是否允许非空];&lt;br /&gt;&lt;br /&gt;例如:&lt;br /&gt;修改表expert_info中的字段birth,允许其为空&lt;br /&gt;&gt;alter table expert_info change birth birth varchar(20) null;&lt;br /&gt; mysql&gt;   ALTER   TABLE   t2   MODIFY   a   TINYINT   NOT   NULL,   CHANGE   b   c   CHAR(20);   &lt;br /&gt;  添加新字段   d:     &lt;br /&gt;  mysql&gt;   ALTER   TABLE   t2   ADD   d   TIMESTAMP;   &lt;br /&gt;  在a   d   上增加索引:     &lt;br /&gt;  mysql&gt;   ALTER   TABLE   t2   ADD   INDEX   (d),   ADD   INDEX   (a);   &lt;br /&gt;  删除字段c:     &lt;br /&gt;  mysql&gt;   ALTER   TABLE   t2   DROP   COLUMN   c;   &lt;br /&gt;  添加一个自动增长的字段c   ,并且添加c   为主健:     &lt;br /&gt;  mysql&gt;   ALTER   TABLE   t2   ADD   c   INT   UNSIGNED   NOT   NULL   AUTO_INCREMENT,   &lt;br /&gt;  -&gt;           ADD   PRIMARY   KEY   (c);&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5349536066535334815-2700318103179756129?l=yourmagicsearchengine.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://yourmagicsearchengine.blogspot.com/feeds/2700318103179756129/comments/default' title='帖子评论'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=5349536066535334815&amp;postID=2700318103179756129' title='0 条评论'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5349536066535334815/posts/default/2700318103179756129'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5349536066535334815/posts/default/2700318103179756129'/><link rel='alternate' type='text/html' href='http://yourmagicsearchengine.blogspot.com/2007/05/mysqlzz.html' title='MySQL常用命令(ZZ)'/><author><name>Yichuan Cai's home page</name><uri>http://www.blogger.com/profile/10921376371265973337</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5349536066535334815.post-8394241930418100204</id><published>2007-05-13T19:19:00.000-07:00</published><updated>2007-05-13T19:20:28.634-07:00</updated><title type='text'>Equals() 和运算符 == 的重载准则（C# 编程指南） ZZ</title><content type='html'>C# 程序员参考&lt;br /&gt;Equals() 和运算符 == 的重载准则（C# 编程指南）&lt;br /&gt;&lt;br /&gt;C# 中有两种不同的相等：引用相等和值相等。值相等是大家普遍理解的意义上的相等：它意味着两个对象包含相同的值。例如，两个值为 2 的整数具有值相等性。引用相等意味着要比较的不是两个对象，而是两个对象引用，这两个对象引用引用的是同一个对象。这可以通过简单的赋值来实现，如下面的示例所示：&lt;br /&gt;&lt;br /&gt;C# 复制代码&lt;br /&gt;System.Object a = new System.Object();&lt;br /&gt;System.Object b = a;&lt;br /&gt;System.Object.ReferenceEquals(a, b); //returns true&lt;br /&gt;&lt;br /&gt;在上面的代码中，只存在一个对象，但存在对该对象的多个引用：a 和 b。由于它们引用的是同一个对象，因此具有引用相等性。如果两个对象具有引用相等性，则它们也具有值相等性，但是值相等性不能保证引用相等性。&lt;br /&gt;&lt;br /&gt;若要检查引用相等性，应使用 ReferenceEquals。若要检查值相等性，应使用 Equals 或 Equals。&lt;br /&gt;&lt;br /&gt;重写 Equals&lt;br /&gt;&lt;br /&gt;Equals 是一个虚方法，允许任何类重写其实现。表示某个值（本质上可以是任何值类型）或一组值（如复数类）的任何类都应该重写 Equals。如果类型要实现 IComparable，则它应该重写 Equals。&lt;br /&gt;&lt;br /&gt;Equals 的新实现应该遵循 Equals 的所有保证：&lt;br /&gt;&lt;br /&gt;x.Equals(x) 返回 true。&lt;br /&gt;&lt;br /&gt;x.Equals(y) 与 y.Equals(x) 返回相同的值。&lt;br /&gt;&lt;br /&gt;如果 (x.Equals(y) &amp;&amp;amp; y.Equals(z)) 返回 true，则 x.Equals(z) 返回 true。&lt;br /&gt;&lt;br /&gt;只要不修改 x 和 y 所引用的对象，x.Equals(y) 的后续调用就返回相同的值。&lt;br /&gt;&lt;br /&gt;x.Equals(null) 返回 false。&lt;br /&gt;&lt;br /&gt;Equals 的新实现不应该引发异常。建议重写 Equals 的任何类同时也重写 System.Object.GetHashCode。除了实现 Equals（对象）外，还建议所有的类为自己的类型实现 Equals（类型）以增强性能。例如：&lt;br /&gt;&lt;br /&gt;C# 复制代码&lt;br /&gt;class TwoDPoint : System.Object&lt;br /&gt;{&lt;br /&gt;public readonly int x, y;&lt;br /&gt;&lt;br /&gt;public TwoDPoint(int x, int y) //constructor&lt;br /&gt;{&lt;br /&gt;this.x = x;&lt;br /&gt;this.y = y;&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;public override bool Equals(System.Object obj)&lt;br /&gt;{&lt;br /&gt;// If parameter is null return false.&lt;br /&gt;if (obj == null)&lt;br /&gt;{&lt;br /&gt;return false;&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;// If parameter cannot be cast to Point return false.&lt;br /&gt;TwoDPoint p = obj as TwoDPoint;&lt;br /&gt;if ((System.Object)p == null)&lt;br /&gt;{&lt;br /&gt;return false;&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;// Return true if the fields match:&lt;br /&gt;return (x == p.x) &amp;&amp;amp; (y == p.y);&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;public bool Equals(TwoDPoint p)&lt;br /&gt;{&lt;br /&gt;// If parameter is null return false:&lt;br /&gt;if ((object)p == null)&lt;br /&gt;{&lt;br /&gt;return false;&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;// Return true if the fields match:&lt;br /&gt;return (x == p.x) &amp;&amp;amp; (y == p.y);&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;public override int GetHashCode()&lt;br /&gt;{&lt;br /&gt;return x ^ y;&lt;br /&gt;}&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;可调用基类的 Equals 的任何派生类在完成其比较之前都应该这样做。在下面的示例中，Equals 调用基类 Equals，后者将检查空参数并将参数的类型与派生类的类型做比较。这样就把检查派生类中声明的新数据字段的任务留给了派生类中的 Equals 实现：&lt;br /&gt;&lt;br /&gt;C# 复制代码&lt;br /&gt;class ThreeDPoint : TwoDPoint&lt;br /&gt;{&lt;br /&gt;public readonly int z;&lt;br /&gt;&lt;br /&gt;public ThreeDPoint(int x, int y, int z)&lt;br /&gt;: base(x, y)&lt;br /&gt;{&lt;br /&gt;this.z = z;&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;public override bool Equals(System.Object obj)&lt;br /&gt;{&lt;br /&gt;// If parameter cannot be cast to ThreeDPoint return false:&lt;br /&gt;ThreeDPoint p = obj as ThreeDPoint;&lt;br /&gt;if ((object)p == null)&lt;br /&gt;{&lt;br /&gt;return false;&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;// Return true if the fields match:&lt;br /&gt;return base.Equals(obj) &amp;&amp;amp; z == p.z;&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;public bool Equals(ThreeDPoint p)&lt;br /&gt;{&lt;br /&gt;// Return true if the fields match:&lt;br /&gt;return base.Equals((TwoDPoint)p) &amp;&amp;amp; z == p.z;&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;public override int GetHashCode()&lt;br /&gt;{&lt;br /&gt;return base.GetHashCode() ^ z;&lt;br /&gt;}&lt;br /&gt;}&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5349536066535334815-8394241930418100204?l=yourmagicsearchengine.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://yourmagicsearchengine.blogspot.com/feeds/8394241930418100204/comments/default' title='帖子评论'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=5349536066535334815&amp;postID=8394241930418100204' title='0 条评论'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5349536066535334815/posts/default/8394241930418100204'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5349536066535334815/posts/default/8394241930418100204'/><link rel='alternate' type='text/html' href='http://yourmagicsearchengine.blogspot.com/2007/05/equals-c-zz.html' title='Equals() 和运算符 == 的重载准则（C# 编程指南） ZZ'/><author><name>Yichuan Cai's home page</name><uri>http://www.blogger.com/profile/10921376371265973337</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5349536066535334815.post-6523686092415606467</id><published>2007-05-12T19:57:00.000-07:00</published><updated>2007-05-12T20:34:07.903-07:00</updated><title type='text'>近期程序设计竞赛和公司面试的总结</title><content type='html'>1 树型DP的问题:&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;2 C++中讲string 快速转化到Int的问题:&lt;br /&gt;stringstream是用于C++风格的字符串的输入输出的。&lt;br /&gt;stringstream的构造函数原形如下： stringstream::stringstream(string str); #include &lt;sstream&gt;&lt;br /&gt;&lt;br /&gt;string s;&lt;br /&gt;int a;&lt;br /&gt;istringstream ss(s);&lt;br /&gt;ss&gt;&gt;a;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;3 C++中常用的字符串初始化函数&lt;br /&gt;void *memset(void *s, int c, size_t n); Sets n bytes of a block of memory to byte c. memset sets the first n bytes of the array s to the character c.&lt;br /&gt;用这个东东可以快速将字符串初始化.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;4数学问题(发现好久没看数学了,连一些基本的东东都忘了)&lt;br /&gt;递归方程组解的渐进阶的求法——差分方程法&lt;br /&gt;这里只考虑形如：&lt;br /&gt;T(n)=c1T(n-1)+c2T(n-2)+…+ ckT(n-k)+f(n)，n≥k (6.18)&lt;br /&gt;的递归方程。其中ci (i=l，2，…，k)为实常数，且ck≠0。它可改写为一个线性常系数k阶非齐次的差分方程：&lt;br /&gt;T(n)-c1T(n-1)- c2T(n-2)-…-ckT(n-k)=f(n)，n≥k (6.19)&lt;br /&gt;(6.19)与线性常系数k阶非齐次常微分方程的结构十分相似，因而解法类同。限于篇幅，这里直接给出(6.19)的解法，略去其正确性的证明。&lt;br /&gt;第一步，求(6.19)所对应的齐次方程：&lt;br /&gt;T(n)-c1T(n-1)- c2T(n-2)-…-ckT(n-k)=0 (6.20)&lt;br /&gt;的基本解系：写出(6.20)的特征方程：&lt;br /&gt;C(t)=tk-c1tk-1-c2tk-2 -…-ck=0 (6.21)&lt;br /&gt;若t=r是(6.21)的m重实根，则得(6.20)的m个基础解rn，nrn，n2rn，…，nm-1rn；若ρeiθ和ρe-iθ是(6.21)的一对l重的共扼复根，则得(6.20)的2l个基础解ρncosnθ，ρnsinnθ，nρncosnθ，nρnsinnθ，…，nl-1ρncosnθ，nl-1ρncosnθ。如此，求出(6.21)的所有的根，就可以得到(6.20)的k个的基础解。而且，这k个基础解构成了(6.20)的基础解系。即(6.20)的任意一个解都可以表示成这k个基础解的线性组合。&lt;br /&gt;第二步，求(6.19)的一个特解。理论上，(6.19)的特解可以用Lagrange常数变易法得到。但其中要用到(6.20)的通解的显式表达，即(6.20)的基础解系的线性组合，十分麻烦。因此在实际中，常常采用试探法，也就是根据f(n)的特点推测特解的形式，留下若干可调的常数，将推测解代人(6.19)后确定。由于(6.19)的特殊性，可以利用迭加原理，将f(n)线性分解为若干个单项之和并求出各单项相应的特解，然后迭加便得到f(n)相应的特解。这使得试探法更为有效。为了方便，这里对三种特殊形式的f(n)，给出(6.19)的相应特解并列在表6-1中，可供直接套用。其中pi，i=1,2,…,s是待定常数&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5349536066535334815-6523686092415606467?l=yourmagicsearchengine.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://yourmagicsearchengine.blogspot.com/feeds/6523686092415606467/comments/default' title='帖子评论'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=5349536066535334815&amp;postID=6523686092415606467' title='0 条评论'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5349536066535334815/posts/default/6523686092415606467'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5349536066535334815/posts/default/6523686092415606467'/><link rel='alternate' type='text/html' href='http://yourmagicsearchengine.blogspot.com/2007/05/blog-post.html' title='近期程序设计竞赛和公司面试的总结'/><author><name>Yichuan Cai's home page</name><uri>http://www.blogger.com/profile/10921376371265973337</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5349536066535334815.post-7967777909961591156</id><published>2007-05-07T19:52:00.000-07:00</published><updated>2007-05-07T19:54:04.479-07:00</updated><title type='text'>在 C# 中通过 P/Invoke 调用Win32 DLL(ZZ)</title><content type='html'>下载本文的代码： NET0307.exe (133KB)&lt;br /&gt;我在自己最近的编程中注意到一个趋势，正是这个趋势才引出本月的专栏主题。最近，我在基于 Microsoft® .NET Framework 的应用程序中完成了大量的 Win32® Interop。我并不是要说我的应用程序充满了自定义的 interop 代码，但有时我会在 .NET Framework 类库中碰到一些次要但又繁絮、不充分的内容，通过调用该 Windows® API，可以快速减少这样的麻烦。&lt;br /&gt;因此我认为，.NET Framework 1.0 或 1.1 版类库中存在任何 Windows 所没有的功能限制都不足为怪。毕竟，32 位的 Windows（不管何种版本）是一个成熟的操作系统，为广大客户服务了十多年。相比之下，.NET Framework 却是一个新事物。&lt;br /&gt;随着越来越多的开发人员将生产应用程序转到托管代码，开发人员更频繁地研究底层操作系统以图找出一些关键功能显得很自然 — 至少目前是如此。&lt;br /&gt;值得庆幸的是，公共语言运行库 (CLR) 的 interop 功能（称为平台调用 (P/Invoke)）非常完善。在本专栏中，我将重点介绍如何实际使用 P/Invoke 来调用 Windows API 函数。当指 CLR 的 COM Interop 功能时，P/Invoke 当作名词使用；当指该功能的使用时，则将其当作动词使用。我并不打算直接介绍 COM Interop，因为它比 P/Invoke 具有更好的可访问性，却更加复杂，这有点自相矛盾，这使得将 COM Interop 作为专栏主题来讨论不太简明扼要。&lt;br /&gt;走进 P/Invoke&lt;br /&gt;首先从考察一个简单的 P/Invoke 示例开始。让我们看一看如何调用 Win32 MessageBeep 函数，它的非托管声明如以下代码所示：&lt;br /&gt;BOOL MessageBeep(  UINT uType   // beep type);&lt;br /&gt;为了调用 MessageBeep，您需要在 C# 中将以下代码添加到一个类或结构定义中：&lt;br /&gt;[DllImport("User32.dll")]static extern Boolean MessageBeep(UInt32 beepType);&lt;br /&gt;令人惊讶的是，只需要这段代码就可以使托管代码调用非托管的 MessageBeep API。它不是一个方法调用，而是一个外部方法定义。（另外，它接近于一个来自 C 而 C# 允许的直接端口，因此以它为起点来介绍一些概念是有帮助的。）来自托管代码的可能调用如下所示：&lt;br /&gt;MessageBeep(0);&lt;br /&gt;请注意，现在 MessageBeep 方法被声明为 static。这是 P/Invoke 方法所要求的，因为在该 Windows API 中没有一致的实例概念。接下来，还要注意该方法被标记为 extern。这是提示编译器该方法是通过一个从 DLL 导出的函数实现的，因此不需要提供方法体。&lt;br /&gt;说到缺少方法体，您是否注意到 MessageBeep 声明并没有包含一个方法体？与大多数算法由中间语言 (IL) 指令组成的托管方法不同，P/Invoke 方法只是元数据，实时 (JIT) 编译器在运行时通过它将托管代码与非托管的 DLL 函数连接起来。执行这种到非托管世界的连接所需的一个重要信息就是导出非托管方法的 DLL 的名称。这一信息是由 MessageBeep 方法声明之前的 DllImport 自定义属性提供的。在本例中，可以看到，MessageBeep 非托管 API 是由 Windows 中的 User32.dll 导出的。&lt;br /&gt;到现在为止，关于调用 MessageBeep 就剩两个话题没有介绍，请回顾一下，调用的代码与以下所示代码片段非常相似：&lt;br /&gt;[DllImport("User32.dll")]static extern Boolean MessageBeep(UInt32 beepType);&lt;br /&gt;最后这两个话题是与数据封送处理 (data marshaling) 和从托管代码到非托管函数的实际方法调用有关的话题。调用非托管 MessageBeep 函数可以由找到作用域内的extern MessageBeep 声明的任何托管代码执行。该调用类似于任何其他对静态方法的调用。它与其他任何托管方法调用的共同之处在于带来了数据封送处理的需要。&lt;br /&gt;C# 的规则之一是它的调用语法只能访问 CLR 数据类型，例如 System.UInt32 和 System.Boolean。C# 显然不识别 Windows API 中使用的基于 C 的数据类型（例如 UINT 和 BOOL），这些类型只是 C 语言类型的类型定义而已。所以当 Windows API 函数 MessageBeep 按以下方式编写时&lt;br /&gt;BOOL MessageBeep( UINT uType )&lt;br /&gt;外部方法就必须使用 CLR 类型来定义，如您在前面的代码片段中所看到的。需要使用与基础 API 函数类型不同但与之兼容的 CLR 类型是 P/Invoke 较难使用的一个方面。因此，在本专栏的后面我将用完整的章节来介绍数据封送处理。&lt;br /&gt;样式&lt;br /&gt;在 C# 中对 Windows API 进行 P/Invoke 调用是很简单的。但如果类库拒绝使您的应用程序发出嘟声，应该想方设法调用 Windows 使它进行这项工作，是吗？&lt;br /&gt;是的。但是与选择的方法有关，而且关系甚大！通常，如果类库提供某种途径来实现您的意图，则最好使用 API 而不要直接调用非托管代码，因为 CLR 类型和 Win32 之间在样式上有很大的不同。我可以将关于这个问题的建议归结为一句话。当您进行 P/Invoke 时，不要使应用程序逻辑直接属于任何外部方法或其中的构件。如果您遵循这个小规则，从长远看经常会省去许多的麻烦。&lt;br /&gt;图 1 中的代码显示了我所讨论的 MessageBeep 外部方法的最少附加代码。图 1 中并没有任何显著的变化，而只是对无包装的外部方法进行一些普通的改进，这可以使工作更加轻松一些。从顶部开始，您会注意到一个名为 Sound 的完整类型，它专用于 MessageBeep。如果我需要使用 Windows API 函数 PlaySound 来添加对播放波形的支持，则可以重用 Sound 类型。然而，我不会因公开单个公共静态方法的类型而生气。毕竟这只是应用程序代码而已。还应该注意到，Sound 是密封的，并定义了一个空的私有构造函数。这些只是一些细节，目的是使用户不会错误地从 Sound 派生类或者创建它的实例。&lt;br /&gt;图 1 中的代码的下一个特征是，P/Invoke 出现位置的实际外部方法是 Sound 的私有方法。这个方法只是由公共 MessageBeep 方法间接公开，后者接受 BeepTypes 类型的参数。这个间接的额外层是一个很关键的细节，它提供了以下好处。首先，应该在类库中引入一个未来的 beep 托管方法，可以重复地通过公共 MessageBeep 方法来使用托管 API，而不必更改应用程序中的其余代码。&lt;br /&gt;该包装方法的第二个好处是：当您进行 P/Invoke 调用时，您放弃了免受访问冲突和其他低级破坏的权利，这通常是由 CLR 提供的。缓冲方法可以保护您的应用程序的其余部分免受访问冲突及类似问题的影响（即使它不做任何事而只是传递参数）。该缓冲方法将由 P/Invoke 调用引入的任何潜在的错误本地化。&lt;br /&gt;将私有外部方法隐藏在公共包装后面的第三同时也是最后的一个好处是，提供了向该方法添加一些最小的 CLR 样式的机会。例如，在图 1 中，我将 Windows API 函数返回的 Boolean 失败转换成更像 CLR 的异常。我还定义了一个名为 BeepTypes 的枚举类型，它的成员对应于同该 Windows API 一起使用的定义值。由于 C# 不支持定义，因此可以使用托管枚举类型来避免幻数向整个应用程序代码扩散。&lt;br /&gt;包装方法的最后一个好处对于简单的 Windows API 函数（如 MessageBeep）诚然是微不足道的。但是当您开始调用更复杂的非托管函数时，您会发现，手动将 Windows API 样式转换成对 CLR 更加友好的方法所带来的好处会越来越多。越是打算在整个应用程序中重用 interop 功能，越是应该认真地考虑包装的设计。同时我认为，在非面向对象的静态包装方法中使用对 CLR 友好的参数也并非不可以。&lt;br /&gt;DLL Import 属性&lt;br /&gt;现在是更深入地进行探讨的时候了。在对托管代码进行 P/Invoke 调用时，DllImportAttribute 类型扮演着重要的角色。DllImportAttribute 的主要作用是给 CLR 指示哪个 DLL 导出您想要调用的函数。相关 DLL 的名称被作为一个构造函数参数传递给 DllImportAttribute。&lt;br /&gt;如果您无法肯定哪个 DLL 定义了您要使用的 Windows API 函数，Platform SDK 文档将为您提供最好的帮助资源。在 Windows API 函数主题文字临近结尾的位置，SDK 文档指定了 C 应用程序要使用该函数必须链接的 .lib 文件。在几乎所有的情况下，该 .lib 文件具有与定义该函数的系统 DLL 文件相同的名称。例如，如果该函数需要 C 应用程序链接到 Kernel32.lib，则该函数就定义在 Kernel32.dll 中。您可以在 MessageBeep 中找到有关 MessageBeep 的 Platform SDK 文档主题。在该主题结尾处，您会注意到它指出库文件是 User32.lib；这表明 MessageBeep 是从 User32.dll 中导出的。&lt;br /&gt;可选的 DllImportAttribute 属性&lt;br /&gt;除了指出宿主 DLL 外，DllImportAttribute 还包含了一些可选属性，其中四个特别有趣：EntryPoint、CharSet、SetLastError 和 CallingConvention。&lt;br /&gt;EntryPoint 在不希望外部托管方法具有与 DLL 导出相同的名称的情况下，可以设置该属性来指示导出的 DLL 函数的入口点名称。当您定义两个调用相同非托管函数的外部方法时，这特别有用。另外，在 Windows 中还可以通过它们的序号值绑定到导出的 DLL 函数。如果您需要这样做，则诸如“#1”或“#129”的 EntryPoint 值指示 DLL 中非托管函数的序号值而不是函数名。&lt;br /&gt;CharSet 对于字符集，并非所有版本的 Windows 都是同样创建的。Windows 9x 系列产品缺少重要的 Unicode 支持，而 Windows NT 和 Windows CE 系列则一开始就使用 Unicode。在这些操作系统上运行的 CLR 将Unicode 用于 String 和 Char 数据的内部表示。但也不必担心 — 当调用 Windows 9x API 函数时，CLR 会自动进行必要的转换，将其从 Unicode转换为 ANSI。&lt;br /&gt;如果 DLL 函数不以任何方式处理文本，则可以忽略 DllImportAttribute 的 CharSet 属性。然而，当 Char 或 String 数据是等式的一部分时，应该将 CharSet 属性设置为 CharSet.Auto。这样可以使 CLR 根据宿主 OS 使用适当的字符集。如果没有显式地设置 CharSet 属性，则其默认值为 CharSet.Ansi。这个默认值是有缺点的，因为对于在 Windows 2000、Windows XP 和 Windows NT® 上进行的 interop 调用，它会消极地影响文本参数封送处理的性能。&lt;br /&gt;应该显式地选择 CharSet.Ansi 或 CharSet.Unicode 的 CharSet 值而不是使用 CharSet.Auto 的唯一情况是：您显式地指定了一个导出函数，而该函数特定于这两种 Win32 OS 中的某一种。ReadDirectoryChangesW API 函数就是这样的一个例子，它只存在于基于 Windows NT 的操作系统中，并且只支持 Unicode；在这种情况下，您应该显式地使用 CharSet.Unicode。&lt;br /&gt;有时，Windows API 是否有字符集关系并不明显。一种决不会有错的确认方法是在 Platform SDK 中检查该函数的 C 语言头文件。（如果您无法肯定要看哪个头文件，则可以查看 Platform SDK 文档中列出的每个 API 函数的头文件。）如果您发现该 API 函数确实定义为一个映射到以 A 或 W 结尾的函数名的宏，则字符集与您尝试调用的函数有关系。Windows API 函数的一个例子是在 WinUser.h 中声明的 GetMessage API，您也许会惊讶地发现它有 A 和 W 两种版本。&lt;br /&gt;SetLastError 错误处理非常重要，但在编程时经常被遗忘。当您进行 P/Invoke 调用时，也会面临其他的挑战 — 处理托管代码中 Windows API 错误处理和异常之间的区别。我可以给您一点建议。&lt;br /&gt;如果您正在使用 P/Invoke 调用 Windows API 函数，而对于该函数，您使用 GetLastError 来查找扩展的错误信息，则应该在外部方法的 DllImportAttribute 中将 SetLastError 属性设置为 true。这适用于大多数外部方法。&lt;br /&gt;这会导致 CLR 在每次调用外部方法之后缓存由 API 函数设置的错误。然后，在包装方法中，可以通过调用类库的 System.Runtime.InteropServices.Marshal 类型中定义的 Marshal.GetLastWin32Error 方法来获取缓存的错误值。我的建议是检查这些期望来自 API 函数的错误值，并为这些值引发一个可感知的异常。对于其他所有失败情况（包括根本就没意料到的失败情况），则引发在 System.ComponentModel 命名空间中定义的 Win32Exception，并将 Marshal.GetLastWin32Error 返回的值传递给它。如果您回头看一下图 1 中的代码，您会看到我在 extern MessageBeep 方法的公共包装中就采用了这种方法。&lt;br /&gt;CallingConvention 我将在此介绍的最后也可能是最不重要的一个 DllImportAttribute 属性是 CallingConvention。通过此属性，可以给 CLR 指示应该将哪种函数调用约定用于堆栈中的参数。CallingConvention.Winapi 的默认值是最好的选择，它在大多数情况下都可行。然而，如果该调用不起作用，则可以检查 Platform SDK 中的声明头文件，看看您调用的 API 函数是否是一个不符合调用约定标准的异常 API。&lt;br /&gt;通常，本机函数（例如 Windows API 函数或 C- 运行时 DLL 函数）的调用约定描述了如何将参数推入线程堆栈或从线程堆栈中清除。大多数 Windows API 函数都是首先将函数的最后一个参数推入堆栈，然后由被调用的函数负责清理该堆栈。相反，许多 C-运行时 DLL 函数都被定义为按照方法参数在方法签名中出现的顺序将其推入堆栈，将堆栈清理工作交给调用者。&lt;br /&gt;幸运的是，要让 P/Invoke 调用工作只需要让外围设备理解调用约定即可。通常，从默认值 CallingConvention.Winapi 开始是最好的选择。然后，在 C 运行时 DLL 函数和少数函数中，可能需要将约定更改为 CallingConvention.Cdecl。&lt;br /&gt;数据封送处理&lt;br /&gt;数据封送处理是 P/Invoke 具有挑战性的方面。当在托管和非托管代码之间传递数据时，CLR 遵循许多规则，很少有开发人员会经常遇到它们直至可将这些规则记住。除非您是一名类库开发人员，否则在通常情况下没有必要掌握其细节。为了最有效地在 CLR 上使用 P/Invoke，即使只偶尔需要 interop 的应用程序开发人员仍然应该理解数据封送处理的一些基础知识。&lt;br /&gt;在本月专栏的剩余部分中，我将讨论简单数字和字符串数据的数据封送处理。我将从最基本的数字数据封送处理开始，然后介绍简单的指针封送处理和字符串封送处理。&lt;br /&gt;封送数字和逻辑标量&lt;br /&gt;Windows OS 大部分是用 C 编写的。因此，Windows API 所用到的数据类型要么是 C 类型，要么是通过类型定义或宏定义重新标记的 C 类型。让我们看看没有指针的数据封送处理。简单起见，首先重点讨论的是数字和布尔值。&lt;br /&gt;当通过值向 Windows API 函数传递参数时，需要知道以下问题的答案：&lt;br /&gt;• 数据从根本上讲是整型的还是浮点型的？  • 如果数据是整型的，则它是有符号的还是无符号的？  • 如果数据是整型的，则它的位数是多少？  • 如果数据是浮点型的，则它是单精度的还是双精度的？&lt;br /&gt;有时答案很明显，但有时却不明显。Windows API 以各种方式重新定义了基本的 C 数据类型。图 2 列出了 C 和 Win32 的一些公共数据类型及其规范，以及一个具有匹配规范的公共语言运行库类型。&lt;br /&gt;通常，只要您选择一个其规范与该参数的 Win32 类型相匹配的 CLR 类型，您的代码就能够正常工作。不过也有一些特例。例如，在 Windows API 中定义的 BOOL 类型是一个有符号的 32 位整型。然而，BOOL 用于指示 Boolean 值 true 或 false。虽然您不用将 BOOL 参数作为 System.Int32 值封送，但是如果使用 System.Boolean 类型，就会获得更合适的映射。字符类型的映射类似于 BOOL，因为有一个特定的 CLR 类型 (System.Char) 指出字符的含义。&lt;br /&gt;在了解这些信息之后，逐步介绍示例可能是有帮助的。依然采用 beep 主题作为例子，让我们来试一下 Kernel32.dll 低级 Beep，它会通过计算机的扬声器发生嘟声。这个方法的 Platform SDK 文档可以在 Beep 中找到。本机 API 按以下方式进行记录：&lt;br /&gt;BOOL Beep(  DWORD dwFreq,      // Frequency  DWORD dwDuration   // Duration in milliseconds);&lt;br /&gt;在参数封送处理方面，您的工作是了解什么 CLR 数据类型与 Beep API 函数所使用的 DWORD 和 BOOL 数据类型相兼容。回顾一下图 2 中的图表，您将看到 DWORD 是一个 32 位的无符号整数值，如同 CLR 类型 System.UInt32。这意味着您可以使用 UInt32 值作为送往 Beep 的两个参数。BOOL 返回值是一个非常有趣的情况，因为该图表告诉我们，在 Win32 中，BOOL 是一个 32 位的有符号整数。因此，您可以使用 System.Int32 值作为来自 Beep 的返回值。然而，CLR 也定义了 System.Boolean 类型作为 Boolean 值的语义，所以应该使用它来替代。CLR 默认将 System.Boolean 值封送为 32 位的有符号整数。此处所显示的外部方法定义是用于 Beep 的结果 P/Invoke 方法：&lt;br /&gt;[DllImport("Kernel32.dll", SetLastError=true)]static extern Boolean Beep(   UInt32 frequency, UInt32 duration);&lt;br /&gt;指针参数&lt;br /&gt;许多 Windows API 函数将指针作为它们的一个或多个参数。指针增加了封送数据的复杂性，因为它们增加了一个间接层。如果没有指针，您可以通过值在线程堆栈中传递数据。有了指针，则可以通过引用传递数据，方法是将该数据的内存地址推入线程堆栈中。然后，函数通过内存地址间接访问数据。使用托管代码表示此附加间接层的方式有多种。&lt;br /&gt;在 C# 中，如果将方法参数定义为 ref 或 out，则数据通过引用而不是通过值传递。即使您没有使用 Interop 也是这样，但只是从一个托管方法调用到另一个托管方法。例如，如果通过 ref 传递 System.Int32 参数，则在线程堆栈中传递的是该数据的地址，而不是整数值本身。下面是一个定义为通过引用接收整数值的方法的示例：&lt;br /&gt;void FlipInt32(ref Int32 num){   num = -num;}&lt;br /&gt;这里，FlipInt32 方法获取一个 Int32 值的地址、访问数据、对它求反，然后将求反过的值赋给原始变量。在以下代码中，FlipInt32 方法会将调用程序的变量 x 的值从 10 更改为 -10：&lt;br /&gt;Int32 x = 10;FlipInt32(ref x);&lt;br /&gt;在托管代码中可以重用这种能力，将指针传递给非托管代码。例如，FileEncryptionStatus API 函数以 32 位无符号位掩码的形式返回文件加密状态。该 API 按以下所示方式进行记录：&lt;br /&gt;BOOL FileEncryptionStatus(  LPCTSTR lpFileName,  // file name  LPDWORD lpStatus     // encryption status);&lt;br /&gt;请注意，该函数并不使用它的返回值返回状态，而是返回一个 Boolean 值，指示调用是否成功。在成功的情况下，实际的状态值是通过第二个参数返回的。它的工作方式是调用程序向该函数传递指向一个 DWORD 变量的指针，而该 API 函数用状态值填充指向的内存位置。以下代码片段显示了一个调用非托管 FileEncryptionStatus 函数的可能外部方法定义：&lt;br /&gt;[DllImport("Advapi32.dll", CharSet=CharSet.Auto)]static extern Boolean FileEncryptionStatus(String filename,    out UInt32 status);&lt;br /&gt;该定义使用 out 关键字来为 UInt32 状态值指示 by-ref 参数。这里我也可以选择 ref 关键字，实际上在运行时会产生相同的机器码。out 关键字只是一个 by-ref 参数的规范，它向 C# 编译器指示所传递的数据只在被调用的函数外部传递。相反，如果使用 ref 关键字，则编译器会假定数据可以在被调用的函数的内部和外部传递。&lt;br /&gt;托管代码中 out 和 ref 参数的另一个很好的方面是，地址作为 by-ref 参数传递的变量可以是线程堆栈中的一个本地变量、一个类或结构的元素，也可以是具有合适数据类型的数组中的一个元素引用。调用程序的这种灵活性使得 by-ref 参数成为封送缓冲区指针以及单数值指针的一个很好的起点。只有在我发现 ref 或 out 参数不符合我的需要的情况下，我才会考虑将指针封送为更复杂的 CLR 类型（例如类或数组对象）。&lt;br /&gt;如果您不熟悉 C 语法或者调用 Windows API 函数，有时很难知道一个方法参数是否需要指针。一个常见的指示符是看参数类型是否是以字母 P 或 LP 开头的，例如 LPDWORD 或 PINT。在这两个例子中，LP 和 P 指示参数是一个指针，而它们指向的数据类型分别为 DWORD 或 INT。然而，在有些情况下，可以直接使用 C 语言语法中的星号 (*) 将 API 函数定义为指针。以下代码片段展示了这方面的示例：&lt;br /&gt;void TakesAPointer(DWORD* pNum);&lt;br /&gt;可以看到，上述函数的唯一一个参数是指向 DWORD 变量的指针。&lt;br /&gt;当通过 P/Invoke 封送指针时，ref 和 out 只用于托管代码中的值类型。当一个参数的 CLR 类型使用 struct 关键字定义时，可以认为该参数是一个值类型。Out 和 ref 用于封送指向这些数据类型的指针，因为通常值类型变量是对象或数据，而在托管代码中并没有对值类型的引用。相反，当封送引用类型对象时，并不需要 ref 和 out 关键字，因为变量已经是对象的引用了。&lt;br /&gt;如果您对引用类型和值类型之间的差别不是很熟悉，请查阅 2000 年 12 月 发行的 MSDN® Magazine，在 .NET 专栏的主题中可以找到更多信息。大多数 CLR 类型都是引用类型；然而，除了 System.String 和 System.Object，所有的基元类型（例如 System.Int32 和 System.Boolean）都是值类型。&lt;br /&gt;封送不透明 (Opaque) 指针：一种特殊情况&lt;br /&gt;有时在 Windows API 中，方法传递或返回的指针是不透明的，这意味着该指针值从技术角度讲是一个指针，但代码却不直接使用它。相反，代码将该指针返回给 Windows 以便随后进行重用。&lt;br /&gt;一个非常常见的例子就是句柄的概念。在 Windows 中，内部数据结构（从文件到屏幕上的按钮）在应用程序代码中都表示为句柄。句柄其实就是不透明的指针或有着指针宽度的数值，应用程序用它来表示内部的 OS 构造。&lt;br /&gt;少数情况下，API 函数也将不透明指针定义为 PVOID 或 LPVOID 类型。在 Windows API 的定义中，这些类型意思就是说该指针没有类型。&lt;br /&gt;当一个不透明指针返回给您的应用程序（或者您的应用程序期望得到一个不透明指针）时，您应该将参数或返回值封送为 CLR 中的一种特殊类型 — System.IntPtr。当您使用 IntPtr 类型时，通常不使用 out 或 ref 参数，因为 IntPtr 意为直接持有指针。不过，如果您将一个指针封送为一个指针，则对 IntPtr 使用 by-ref 参数是合适的。&lt;br /&gt;在 CLR 类型系统中，System.IntPtr 类型有一个特殊的属性。不像系统中的其他基类型，IntPtr 并没有固定的大小。相反，它在运行时的大小是依底层操作系统的正常指针大小而定的。这意味着在 32 位的 Windows 中，IntPtr 变量的宽度是 32 位的，而在 64 位的 Windows 中，实时编译器编译的代码会将 IntPtr 值看作 64 位的值。当在托管代码和非托管代码之间封送不透明指针时，这种自动调节大小的特点十分有用。&lt;br /&gt;请记住，任何返回或接受句柄的 API 函数其实操作的就是不透明指针。您的代码应该将 Windows 中的句柄封送成 System.IntPtr 值。&lt;br /&gt;您可以在托管代码中将 IntPtr 值强制转换为 32 位或 64 位的整数值，或将后者强制转换为前者。然而，当使用 Windows API 函数时，因为指针应是不透明的，所以除了存储和传递给外部方法外，不能将它们另做它用。这种“只限存储和传递”规则的两个特例是当您需要向外部方法传递 null 指针值和需要比较 IntPtr 值与 null 值的情况。为了做到这一点，您不能将零强制转换为 System.IntPtr，而应该在 IntPtr 类型上使用 Int32.Zero 静态公共字段，以便获得用于比较或赋值的 null 值。&lt;br /&gt;封送文本&lt;br /&gt;在编程时经常要对文本数据进行处理。文本为 interop 制造了一些麻烦，这有两个原因。首先，底层操作系统可能使用 Unicode 来表示字符串，也可能使用 ANSI。在极少数情况下，例如 MultiByteToWideChar API 函数的两个参数在字符集上是不一致的。&lt;br /&gt;第二个原因是，当需要进行 P/Invoke 时，要处理文本还需要特别了解到 C 和 CLR 处理文本的方式是不同的。在 C 中，字符串实际上只是一个字符值数组，通常以 null 作为结束符。大多数 Windows API 函数是按照以下条件处理字符串的：对于 ANSI，将其作为字符值数组；对于 Unicode，将其作为宽字符值数组。&lt;br /&gt;幸运的是，CLR 被设计得相当灵活，当封送文本时问题得以轻松解决，而不用在意 Windows API 函数期望从您的应用程序得到的是什么。这里是一些需要记住的主要考虑事项：&lt;br /&gt;• 是您的应用程序向 API 函数传递文本数据，还是 API 函数向您的应用程序返回字符串数据？或者二者兼有？  • 您的外部方法应该使用什么托管类型？  • API 函数期望得到的是什么格式的非托管字符串？&lt;br /&gt;我们首先解答最后一个问题。大多数 Windows API 函数都带有 LPTSTR 或 LPCTSTR 值。（从函数角度看）它们分别是可修改和不可修改的缓冲区，包含以 null 结束的字符数组。“C”代表常数，意味着使用该参数信息不会传递到函数外部。LPTSTR 中的“T”表明该参数可以是 Unicode 或 ANSI，取决于您选择的字符集和底层操作系统的字符集。因为在 Windows API 中大多数字符串参数都是这两种类型之一，所以只要在 DllImportAttribute 中选择 CharSet.Auto，CLR 就按默认的方式工作。&lt;br /&gt;然而，有些 API 函数或自定义的 DLL 函数采用不同的方式表示字符串。如果您要用到一个这样的函数，就可以采用 MarshalAsAttribute 修饰外部方法的字符串参数，并指明一种不同于默认 LPTSTR 的字符串格式。有关 MarshalAsAttribute 的更多信息，请参阅位于 MarshalAsAttribute Class 的 Platform SDK 文档主题。&lt;br /&gt;现在让我们看一下字符串信息在您的代码和非托管函数之间传递的方向。有两种方式可以知道处理字符串时信息的传递方向。第一个也是最可靠的一个方法就是首先理解参数的用途。例如，您正调用一个参数，它的名称类似 CreateMutex 并带有一个字符串，则可以想像该字符串信息是从应用程序向 API 函数传递的。同时，如果您调用 GetUserName，则该函数的名称表明字符串信息是从该函数向您的应用程序传递的。&lt;br /&gt;除了这种比较合理的方法外，第二种查找信息传递方向的方式就是查找 API 参数类型中的字母“C”。例如，GetUserName API 函数的第一个参数被定义为 LPTSTR 类型，它代表一个指向 Unicode 或 ANSI 字符串缓冲区的长指针。但是 CreateMutex 的名称参数被类型化为 LTCTSTR。请注意，这里的类型定义是一样的，但增加一个字母“C”来表明缓冲区为常数，API 函数不能写入。&lt;br /&gt;一旦明确了文本参数是只用作输入还是用作输入/输出，就可以确定使用哪种 CLR 类型作为参数类型。这里有一些规则。如果字符串参数只用作输入，则使用 System.String 类型。在托管代码中，字符串是不变的，适合用于不会被本机 API 函数更改的缓冲区。&lt;br /&gt;如果字符串参数可以用作输入和/或输出，则使用 System.StringBuilder 类型。StringBuilder 类型是一个很有用的类库类型，它可以帮助您有效地构建字符串，也正好可以将缓冲区传递给本机函数，由本机函数为您填充字符串数据。一旦函数调用返回，您只需要调用 StringBuilder 对象的 ToString 就可以得到一个 String 对象。&lt;br /&gt;GetShortPathName API 函数能很好地用于显示什么时候使用 String、什么时候使用 StringBuilder，因为它只带有三个参数：一个输入字符串、一个输出字符串和一个指明输出缓冲区的字符长度的参数。&lt;br /&gt;图 3 所示为加注释的非托管 GetShortPathName 函数文档，它同时指出了输入和输出字符串参数。它引出了托管的外部方法定义，也如图 3 所示。请注意第一个参数被封送为 System.String，因为它是一个只用作输入的参数。第二个参数代表一个输出缓冲区，它使用了 System.StringBuilder。&lt;br /&gt;小结&lt;br /&gt;本月专栏所介绍的 P/Invoke 功能足够调用 Windows 中的许多 API 函数。然而，如果您大量用到 interop，则会最终发现自己封送了很复杂的数据结构，甚至可能需要在托管代码中通过指针直接访问内存。实际上，本机代码中的 interop 可以是一个将细节和低级比特藏在里面的真正的潘多拉盒子。CLR、C# 和托管 C++ 提供了许多有用的功能；也许以后我会在本专栏介绍高级的 P/Invoke 话题。&lt;br /&gt;同时，只要您觉得 .NET Framework 类库无法播放您的声音或者为您执行其他一些功能，您可以知道如何向原始而优秀的 Windows API 寻求一些帮助。&lt;br /&gt;将您要发给 Jason 的问题和意见发送到 &lt;a href="mailto:dot-net@microsoft.com"&gt;dot-net@microsoft.com&lt;/a&gt;。&lt;br /&gt;Jason Clark 为 Microsoft 和 Wintellect (&lt;a href="http://www.wintellect.com/"&gt;http://www.wintellect.com&lt;/a&gt;) 提供培训和咨询，以前是 Windows NT 和 Windows 2000 Server 团队的开发人员。他与人合著了 Programming Server-side Applications for Microsoft Windows 2000 (Microsoft Press, 2000)。您可以通过 &lt;a href="mailto:JClark@Wintellect.com"&gt;JClark@Wintellect.com&lt;/a&gt; 与 Jason 取得联系。&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5349536066535334815-7967777909961591156?l=yourmagicsearchengine.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://yourmagicsearchengine.blogspot.com/feeds/7967777909961591156/comments/default' title='帖子评论'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=5349536066535334815&amp;postID=7967777909961591156' title='0 条评论'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5349536066535334815/posts/default/7967777909961591156'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5349536066535334815/posts/default/7967777909961591156'/><link rel='alternate' type='text/html' href='http://yourmagicsearchengine.blogspot.com/2007/05/c-pinvoke-win32-dllzz.html' title='在 C# 中通过 P/Invoke 调用Win32 DLL(ZZ)'/><author><name>Yichuan Cai's home page</name><uri>http://www.blogger.com/profile/10921376371265973337</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5349536066535334815.post-2696726473567319910</id><published>2007-04-04T01:29:00.001-07:00</published><updated>2007-04-04T01:29:46.035-07:00</updated><title type='text'>谈谈Web Mining研究者掌握动态脚本语言的必要性发信站: 水木社区 (Fri Mar 23 17:58:30 2007),</title><content type='html'>站内自己平时很喜欢在闲暇时学习一些新的计算机语言，它使我对计算机的兴趣不至于在书写各种申报材料和论文中完全泯灭。特别是在我把工作平台转向Linux后，学习各种开源语言更成为自己掌握在Linux下工作的一种必然选择，除了C/C++、JAVA、C#这些主流开发语言外，我尤其对Perl、Python和Ruby这样的动态脚本语言情有独衷，这里特别谈谈自己在学习它们的过程中的一些体会。         第一次接触Perl语言是自己在作WebMining研究时从CMU大学的著名网页数据集Web-KB开始的，它的代码里有一些Perl语言脚本，往往几行代码就能完成JAVA这样的开发语言几十行代码才能完成的任务。显然它比Bash这样的Linux中的shell脚本功能要强大得多。由于第一次读到Perl语言书写的代码，感觉真的像天书一样难懂，特别是那些到处可见的$、＠、％等奇怪的符号。为了完全弄懂Web-KB的这些代码，我下决心开始学习Perl，结果没想到从此一发不可收拾，很快就成为Perl铁定的忠实拥户。下面是我总结的几点它最突出的优点：   1。实用：它像编码世界里的"瑞士军刀"，很多常用的处理方式特别作了很大的简化，让你用最少的代码量完成最多的功能。在JAVA语言里你需要首先声明各种变量，在程序执行前需要进行编译，在PERL语言里这些都去掉了，这大大简化了程序开发人员的开发效率。在JAVA程序里即使你只是打印一行"HelloWorld"也要先定义一个类，再定义一个static voidmain函数作为程序调用的入口，在PERL程序中这些全都不必了；再比如从网上下载网页在JAVA语言里至少要写上几行的代码，在Perl里只用一句就可以: use LWP::Simple; $html =get('&lt;a href="http://www.sina.com.cn/" target="_blank"&gt;http://www.sina.com.cn&lt;/a&gt;);由于经常需要引入package，所以选择use而不是JAVA里的import，因为它比后者少敲三个键。由于编程时90％的情况是处理文本，它就把正则表达式作为语言的内置功能而不像JAVA那样首先需要importre；在所有动态脚本语言中它也是运行速度最快的。2。第三方类库有集中的CPAN网站及其镜像站点统一管理：这是另一个让JAVA语言开发者嫉妒的优势，浏览一下网页&lt;a href="http://search.cpan.org/吧，它把十年来" target="_blank"&gt;http://search.cpan.org/吧，它把十年来&lt;/a&gt;所有开源的第三方软件包分门别类地集中存储，如果你愿意，也可以把自己开发的觉的有价值的软件包与全世界共享。JAVA虽然有Jarkata这样的第三方类库，但绝大多数还是需要我们自己去寻找。3。它是最像自然语言的计算机语言，不要忘记它的作者以前曾经是一个语言学家，如果你牢记这一点，那学习Perl语言便容易得多。我认为Perl语言也是目前我学到的所有语言中最具创新性的语言（它直接影响了Ruby），深深影响了以后各种新的语言的设计。4。它是完全开源的语言，它的作者是计算机历史上大名鼎鼎的LarryWall，他发明了patch命令和一个新闻组阅读器，但更主要的是他发明了Perl和CGI动态网页技术，他对Web的迅猛发展有直接的影响力。如果你想学习一门很多年后依然保持生命力的语言，那最保险的办法就是学习一门开源的语言，因为如果你去学习Delphi这样的商业语言，等到Borland公司一旦在商业市场上份额下降，那这种语言可能就完全风光不再。现在最热门的语言JAVA是Sun公司的宠儿，虽然Sun公司总裁声称在今年年底会完全公开JAVA源码，但显然这门语言的发展完全掌握在商业的运营模式中。Perl语言当然有它的缺点：不像Python和Ruby一样易学；面向对象功能不是一开始就设计而是后来添加的；代码可读性较差，过于灵活。LarryWall在2000年每年一度的开源大会上宣称开发新一代perl语言（即Perl6）的计划正式发始启动，它将摆脱现在向下兼容的包袱，从内核上对语言进行重新设计，采纳当下各门语言的精华同时保留它最根本的特性。这是让无数perl爱好者兴奋到整夜难眠的事情，现在开发Perl6还没有实现，这中间台湾的唐宗汉（现变性后改为唐凤）起了力挽狂澜的作用，是我们华人在开源领域最骄傲的人物。(见&lt;a href="http://www.linuxeden.com/doc/21782.html）" target="_blank"&gt;http://www.linuxeden.com/doc/21782.html）&lt;/a&gt;我认为学习像perl这样的脚本语言对计算机专业的研究者来说尤其适合，因为它可以大大加快开发原型或验证算法的过程，特别是它对文本处理的支持至今是无与伦比的。以我个人经验来说，作WebMining研究有一个很漂亮的想法很容易，但想得到实验数据的支持必须经过大量的Coding，它占用整个过程99%以上的时间（只关心发论文不在意伪造数据的除外）。如果用C++或JAVA语言来实现，你要花大量的时间用在Debug上面，但用Perl这样的快速开发语言，只花费十分之一的精力就能将其搞定，它免去了声明和编译（包括make过程），同时由于有很多现成的第三方软件包可以使用（在cpan里很容易找到Naive Bayes,Support VectorMachine这些机器学习工具），它的功能却一点也不逊色。特别的，由于它的代码量只有前者的几分甚至十几分之一，debug也相对容易得多。另外，使用perl语言最好结合Unix/Linux系统(虽然它在跨平台特性上一点不比JAVA差），这更能发挥它应有的作用。事实上在Linux系统下Perl是必备的工具（很多系统管理命令甚至带窗口的程序都是用它编写的，比如synaptic），你甚至无须去安装它。Python是另一个非常好的选择，它的好处是代码可读性好，对面向对象支持得非常好，非常易学（一个下午的时间可以学会80%场合用到的20%命令），另外与C/C++和Java的互相调用也作得非常出色（所以它是一种更好的gluelanguage），还有一个Perl语言没有的交互式编译器，它类似于以前苹果机上的Basic，你可以无须编写程序而直接在终端一行行边输入程序边看到结果，所以非常有利于快速开发。它的作者现在为Google工作，据说Google很多的代码都是用Python书写的。Python的名字来自国外非常流行的一个黑色幽默电影，放假时中央电影频道还专门介绍过它(下次我再次学习它时会先那部电影看一遍，找找感觉），开源世界的一大特色就是这种无比轻松的幽默而不是像微软产品包含的那种浓浓的商业气息。   Ruby现在由于Ruby onRails的流行而成为亲的上升之星，有人甚至预言它是未来的JAVA，它从Perl语言和Ada语言吸取了很多东西，自己打算有空闲时间的时候好好学习它。它的发明人是个日本人，有些中国程序员便排斥这门优秀的语言，我当然对此不会在意的。据说敏捷开发之父非常推崇这种语言，因为它是比JAVA还要纯的面向对象语言，像字符串和数学这些都作为对象处理，可以直接调用各种方&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5349536066535334815-2696726473567319910?l=yourmagicsearchengine.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://yourmagicsearchengine.blogspot.com/feeds/2696726473567319910/comments/default' title='帖子评论'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=5349536066535334815&amp;postID=2696726473567319910' title='0 条评论'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5349536066535334815/posts/default/2696726473567319910'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5349536066535334815/posts/default/2696726473567319910'/><link rel='alternate' type='text/html' href='http://yourmagicsearchengine.blogspot.com/2007/04/web-mining-fri-mar-23-175830-2007.html' title='谈谈Web Mining研究者掌握动态脚本语言的必要性发信站: 水木社区 (Fri Mar 23 17:58:30 2007),'/><author><name>Yichuan Cai's home page</name><uri>http://www.blogger.com/profile/10921376371265973337</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5349536066535334815.post-235900410396696542</id><published>2007-03-29T07:53:00.000-07:00</published><updated>2007-03-29T07:58:52.516-07:00</updated><title type='text'>Worker Threads in C#(http://www.codeproject.com/csharp/workerthread.asp)</title><content type='html'>&lt;h2&gt;Introduction&lt;/h2&gt;&lt;br /&gt;&lt;p nd="1"&gt;The .NET framework provides a lot of ways to implement multithreading&lt;br /&gt;programs. I want to show how we can run a worker thread which makes syncronous&lt;br /&gt;calls to a user interface (for example, a thread that reads a long recordset and&lt;br /&gt;fills some control in the form).&lt;br /&gt;&lt;p nd="2"&gt;To run thread I use:&lt;br /&gt;&lt;ul&gt;&lt;br /&gt;&lt;li nd="3"&gt;Thread instance and main thread function&lt;br /&gt;&lt;br /&gt;&lt;li nd="4"&gt;Two events used to stop thread. First event is set when main thread&lt;br /&gt;wants to stop worker thread; second event is set by worker thread when it really&lt;br /&gt;stops.&lt;/li&gt;&lt;/ul&gt;&lt;br /&gt;&lt;p nd="5"&gt;.NET allows you to call &lt;code nd="6"&gt;System.Windows.Forms.Control&lt;/code&gt; functions only from the thread in&lt;br /&gt;which the control was created. To run them from another thread we need to use&lt;br /&gt;the &lt;code nd="7"&gt;Control.Invoke&lt;/code&gt; (synchronous call) or &lt;code nd="8"&gt;Control.BeginInvoke&lt;/code&gt; (asynchronous call) functions. For tasks like&lt;br /&gt;showing &lt;a class="iAs" style="FONT-WEIGHT: normal; FONT-SIZE: 100%; PADDING-BOTTOM: 1px; COLOR: darkgreen; BORDER-BOTTOM: darkgreen 0.07em solid; BACKGROUND-COLOR: transparent; TEXT-DECORATION: underline" href="#" target="_blank" itxtdid="3572726"&gt;database&lt;/a&gt; records we need &lt;code nd="9"&gt;Invoke&lt;/code&gt;.&lt;br /&gt;&lt;p nd="10"&gt;To implement this we will use:&lt;br /&gt;&lt;ul&gt;&lt;br /&gt;&lt;li nd="11"&gt;A Delegate type for calling the form function. Delegate instance and&lt;br /&gt;function called using this delegate&lt;br /&gt;&lt;br /&gt;&lt;li nd="12"&gt;The &lt;code nd="13"&gt;Invoke&lt;/code&gt; call from the worker thread.&lt;br /&gt;&lt;/li&gt;&lt;/ul&gt;&lt;br /&gt;&lt;p nd="14"&gt;The next problem is to stop the worker thread correctly. The steps to&lt;br /&gt;do this are:&lt;br /&gt;&lt;ul&gt;&lt;br /&gt;&lt;li nd="15"&gt;Set the event "Stop Thread"&lt;br /&gt;&lt;li nd="16"&gt;Wait for the event "Thread is stopped"&lt;br /&gt;&lt;li nd="17"&gt;Wait for the event process messages using the &lt;code nd="18"&gt;Application.DoEvents&lt;/code&gt; function. This prevents deadlocks because&lt;br /&gt;the worker thread makes &lt;code nd="19"&gt;Invoke&lt;/code&gt; calls which are processed in&lt;br /&gt;the main thread. &lt;/li&gt;&lt;/ul&gt;&lt;br /&gt;&lt;p nd="20"&gt;The thread function checks every iteration whether the "Stop Thread"&lt;br /&gt;event has been set. If the event is set the function invokes clean-up&lt;br /&gt;operations, sets the event "Thread is stopped" and returns.&lt;br /&gt;&lt;p nd="21"&gt;Demo project has two classes: &lt;code nd="22"&gt;MainForm&lt;/code&gt; and &lt;code nd="23"&gt;LongProcess&lt;/code&gt;. The &lt;code nd="24"&gt;LongProcess.Run&lt;/code&gt; function&lt;br /&gt;runs in a thread and fills the list box with some lines. The worker thread may&lt;br /&gt;finish naturally or may be stopped when user presses the "Stop Thread" button or&lt;br /&gt;closes the form.&lt;br /&gt;&lt;h2&gt;Code fragments&lt;/h2&gt;&lt;br /&gt;&lt;div class="precollapse" id="premain0" style="WIDTH: 100%"&gt;&lt;img id="preimg0" style="CURSOR: hand" height="9" src="http://www.codeproject.com/images/minus.gif" width="9" preid="0" /&gt; Collapse&lt;/div&gt;&lt;pre id="pre0" style="MARGIN-TOP: 0px" nd="26"&gt;// MainForm.cs&lt;br /&gt;&lt;br /&gt;namespace WorkerThread&lt;br /&gt;{&lt;br /&gt;    // delegates used to call MainForm functions from worker thread&lt;br /&gt;    public delegate void DelegateAddString(String s);&lt;br /&gt;    public delegate void DelegateThreadFinished();&lt;br /&gt;&lt;br /&gt;    public class MainForm : System.Windows.Forms.Form&lt;br /&gt;    {&lt;br /&gt;        // ...&lt;br /&gt;&lt;br /&gt;        // worker thread&lt;br /&gt;        Thread m_WorkerThread;&lt;br /&gt;&lt;br /&gt;        // events used to stop worker thread&lt;br /&gt;        ManualResetEvent m_EventStopThread;&lt;br /&gt;        ManualResetEvent m_EventThreadStopped;&lt;br /&gt;&lt;br /&gt;        // Delegate instances used to call user interface functions&lt;br /&gt;        // from worker thread:&lt;br /&gt;        public DelegateAddString m_DelegateAddString;&lt;br /&gt;        public DelegateThreadFinished m_DelegateThreadFinished;&lt;br /&gt;&lt;br /&gt;        // ...&lt;br /&gt;&lt;br /&gt;        public MainForm()&lt;br /&gt;        {&lt;br /&gt;            InitializeComponent();&lt;br /&gt;&lt;br /&gt;            // initialize delegates&lt;br /&gt;            m_DelegateAddString = new DelegateAddString(this.AddString);&lt;br /&gt;            m_DelegateThreadFinished = new DelegateThreadFinished(this.ThreadFinished);&lt;br /&gt;&lt;br /&gt;            // initialize events&lt;br /&gt;            m_EventStopThread = new ManualResetEvent(false);&lt;br /&gt;            m_EventThreadStopped = new ManualResetEvent(false);&lt;br /&gt;&lt;br /&gt;        }&lt;br /&gt;&lt;br /&gt;        // ...&lt;br /&gt;&lt;br /&gt;        // Start thread button is pressed&lt;br /&gt;        private void btnStartThread_Click(object sender, System.EventArgs e)&lt;br /&gt;        {&lt;br /&gt;            // ...&lt;br /&gt;          &lt;br /&gt;            // reset events&lt;br /&gt;            m_EventStopThread.Reset();&lt;br /&gt;            m_EventThreadStopped.Reset();&lt;br /&gt;&lt;br /&gt;            // create worker thread instance&lt;br /&gt;            m_WorkerThread = new Thread(new ThreadStart(this.WorkerThreadFunction));&lt;br /&gt;&lt;br /&gt;            m_WorkerThread.Name = "Worker Thread Sample";   // looks nice in Output window&lt;br /&gt;&lt;br /&gt;            m_WorkerThread.Start();&lt;br /&gt;&lt;br /&gt;        }&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;        // Worker thread function.&lt;br /&gt;        // Called indirectly from btnStartThread_Click&lt;br /&gt;        private void WorkerThreadFunction()&lt;br /&gt;        {&lt;br /&gt;            LongProcess longProcess;&lt;br /&gt;&lt;br /&gt;            longProcess = new LongProcess(m_EventStopThread, m_EventThreadStopped, this);&lt;br /&gt;&lt;br /&gt;            longProcess.Run();&lt;br /&gt;        }&lt;br /&gt;&lt;br /&gt;        // Stop worker thread if it is running.&lt;br /&gt;        // Called when user presses Stop button or form is closed.&lt;br /&gt;        private void StopThread()&lt;br /&gt;        {&lt;br /&gt;            if ( m_WorkerThread != null  &amp;&amp;amp;  m_WorkerThread.IsAlive )  // thread is active&lt;br /&gt;            {&lt;br /&gt;                // set event "Stop"&lt;br /&gt;                m_EventStopThread.Set();&lt;br /&gt;&lt;br /&gt;                // wait when thread  will stop or finish&lt;br /&gt;                while (m_WorkerThread.IsAlive)&lt;br /&gt;                {&lt;br /&gt;                    // We cannot use here infinite wait because our thread&lt;br /&gt;                    // makes syncronous calls to main form, this will cause deadlock.&lt;br /&gt;                    // Instead of this we wait for event some appropriate time&lt;br /&gt;                    // (and by the way give time to worker thread) and&lt;br /&gt;                    // process events. These events may contain Invoke calls.&lt;br /&gt;                    if ( WaitHandle.WaitAll(&lt;br /&gt;                        (new ManualResetEvent[] {m_EventThreadStopped}),&lt;br /&gt;                        100,&lt;br /&gt;                        true) )&lt;br /&gt;                    {&lt;br /&gt;                        break;&lt;br /&gt;                    }&lt;br /&gt;&lt;br /&gt;                    Application.DoEvents();&lt;br /&gt;                }&lt;br /&gt;            }&lt;br /&gt;        }&lt;br /&gt;&lt;br /&gt;        // Add string to list box.&lt;br /&gt;        // Called from worker thread using delegate and Control.Invoke&lt;br /&gt;        private void AddString(String s)&lt;br /&gt;        {&lt;br /&gt;            listBox1.Items.Add(s);&lt;br /&gt;        }&lt;br /&gt;&lt;br /&gt;        // Set initial state of controls.&lt;br /&gt;        // Called from worker thread using delegate and Control.Invoke&lt;br /&gt;        private void ThreadFinished()&lt;br /&gt;        {&lt;br /&gt;            btnStartThread.Enabled = true;&lt;br /&gt;            btnStopThread.Enabled = false;&lt;br /&gt;        }&lt;br /&gt;&lt;br /&gt;    }&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;// LongProcess.cs&lt;br /&gt;&lt;br /&gt;namespace WorkerThread&lt;br /&gt;{&lt;br /&gt;    public class LongProcess&lt;br /&gt;    {&lt;br /&gt;        // ...&lt;br /&gt;  &lt;br /&gt;        // Function runs in worker thread and emulates long process.&lt;br /&gt;        public void Run()&lt;br /&gt;        {&lt;br /&gt;            int i;&lt;br /&gt;            String s;&lt;br /&gt;&lt;br /&gt;            for (i = 1; i &lt;= 10; i++)             &lt;/pre&gt;&lt;pre style="MARGIN-TOP: 0px" nd="26"&gt;  {                 // make step                 &lt;/pre&gt;&lt;pre style="MARGIN-TOP: 0px" nd="26"&gt;  s = "Step number " + i.ToString() + " executed";                  &lt;/pre&gt;&lt;pre style="MARGIN-TOP: 0px" nd="26"&gt;  Thread.Sleep(400);  // Make synchronous call to main form.    &lt;/pre&gt;&lt;pre style="MARGIN-TOP: 0px" nd="26"&gt;             // MainForm.AddString function runs in main thread.  &lt;/pre&gt;&lt;pre style="MARGIN-TOP: 0px" nd="26"&gt;               // (To make asynchronous call use BeginInvoke)   &lt;/pre&gt;&lt;pre style="MARGIN-TOP: 0px" nd="26"&gt;              m_form.Invoke(m_form.m_DelegateAddString, new Object[] {s});                   // check if thread is cancelled             &lt;/pre&gt;&lt;pre style="MARGIN-TOP: 0px" nd="26"&gt;      if ( m_EventStop.WaitOne(0, true) )               &lt;/pre&gt;&lt;pre style="MARGIN-TOP: 0px" nd="26"&gt;    {                     // clean-up operations may be placed here                     // ...                      // inform main thread that this thread stopped                  &lt;/pre&gt;&lt;pre style="MARGIN-TOP: 0px" nd="26"&gt;     m_EventStopped.Set();                 &lt;/pre&gt;&lt;pre style="MARGIN-TOP: 0px" nd="26"&gt;       return;               &lt;/pre&gt;&lt;pre style="MARGIN-TOP: 0px" nd="26"&gt;    }             &lt;/pre&gt;&lt;pre style="MARGIN-TOP: 0px" nd="26"&gt;  } // Make synchronous call to main form             // to inform it that thread finished            &lt;/pre&gt;&lt;pre style="MARGIN-TOP: 0px" nd="26"&gt;   m_form.Invoke(m_form.m_DelegateThreadFinished, null);         &lt;/pre&gt;&lt;pre style="MARGIN-TOP: 0px" nd="26"&gt; }     &lt;/pre&gt;&lt;pre style="MARGIN-TOP: 0px" nd="26"&gt; }&lt;/pre&gt;&lt;pre style="MARGIN-TOP: 0px" nd="26"&gt; }  &lt;/pre&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5349536066535334815-235900410396696542?l=yourmagicsearchengine.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://yourmagicsearchengine.blogspot.com/feeds/235900410396696542/comments/default' title='帖子评论'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=5349536066535334815&amp;postID=235900410396696542' title='1 条评论'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5349536066535334815/posts/default/235900410396696542'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5349536066535334815/posts/default/235900410396696542'/><link rel='alternate' type='text/html' href='http://yourmagicsearchengine.blogspot.com/2007/03/worker-threads-in-c.html' title='Worker Threads in C#(http://www.codeproject.com/csharp/workerthread.asp)'/><author><name>Yichuan Cai's home page</name><uri>http://www.blogger.com/profile/10921376371265973337</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5349536066535334815.post-2038488765312221600</id><published>2007-03-06T23:27:00.000-08:00</published><updated>2007-03-06T23:28:14.239-08:00</updated><title type='text'>用Visual C#调用Windows API函数(ZZ)</title><content type='html'>Api函数是构筑Windws应用程序的基石，每一种Windows应用程序开发工具，它提供的底层函数都间接或直接地调用了Windows API函数，同时为了实现功能扩展，一般也都提供了调用WindowsAPI函数的接口， 也就是说具备调用动态连接库的能力。Visual C#和其它开发工具一样也能够调用动态链接库的API函数。.NET框架本身提供了这样一种服务,允许受管辖的代码调用动态链接库中实现的非受管辖函数,包括操作系统提供的Windows API函数。它能够定位和调用输出函数,根据需要，组织其各个参数(整型、字符串类型、数组、和结构等等)跨越互操作边界。 &lt;br /&gt; &lt;br /&gt;下面以C#为例简单介绍调用API的基本过程：  &lt;br /&gt;动态链接库函数的声明  &lt;br /&gt;　动态链接库函数使用前必须声明，相对于VB,C#函数声明显得更加罗嗦，前者通过 Api Viewer粘贴以后，可以直接使用，而后者则需要对参数作些额外的变化工作。 &lt;br /&gt; &lt;br /&gt;　动态链接库函数声明部分一般由下列两部分组成，一是函数名或索引号，二是动态链接库的文件名。  &lt;br /&gt;　 譬如，你想调用User32.DLL中的MessageBox函数，我们必须指明函数的名字MessageBoxA或MessageBoxW，以及库名字User32.dll,我们知道Win32 API对每一个涉及字符串和字符的函数一般都存在两个版本，单字节字符的ANSI版本和双字节字符的UNICODE版本。 &lt;br /&gt; &lt;br /&gt;　下面是一个调用API函数的例子：  &lt;br /&gt;[DllImport("KERNEL32.DLL", EntryPoint="MoveFileW", SetLastError=true,  &lt;br /&gt;CharSet=CharSet.Unicode, ExactSpelling=true,  &lt;br /&gt;CallingConvention=CallingConvention.StdCall)]  &lt;br /&gt;public static extern bool MoveFile(String src, String dst);  &lt;br /&gt; &lt;br /&gt;　其中入口点EntryPoint标识函数在动态链接库的入口位置，在一个受管辖的工程中，目标函数的原始名字和序号入口点不仅标识一个跨越互操作界限的函数。而且，你还可以把这个入口点映射为一个不同的名字，也就是对函数进行重命名。重命名可以给调用函数带来种种便利，通过重命名，一方面我们不用为函数的大小写伤透脑筋，同时它也可以保证与已有的命名规则保持一致，允许带有不同参数类型的函数共存，更重要的是它简化了对ANSI和Unicode版本的调用。CharSet用于标识函数调用所采用的是Unicode或是ANSI版本，ExactSpelling＝false将告诉编译器,让编译器决定使用Unicode或者是Ansi版本。其它的参数请参考MSDN在线帮助. &lt;br /&gt; &lt;br /&gt;　在C#中，你可以在EntryPoint域通过名字和序号声明一个动态链接库函数，如果在方法定义中使用的函数名与DLL入口点相同，你不需要在EntryPoint域显示声明函数。否则，你必须使用下列属性格式指示一个名字和序号。 &lt;br /&gt; &lt;br /&gt;[DllImport("dllname", EntryPoint="Functionname")]  &lt;br /&gt;[DllImport("dllname", EntryPoint="#123")]  &lt;br /&gt;值得注意的是，你必须在数字序号前加“＃”  &lt;br /&gt;下面是一个用MsgBox替换MessageBox名字的例子：  &lt;br /&gt;[C#]  &lt;br /&gt;using System.Runtime.InteropServices;  &lt;br /&gt; &lt;br /&gt;public class Win32 {  &lt;br /&gt;[DllImport("user32.dll", EntryPoint="MessageBox")]  &lt;br /&gt;public static extern int MsgBox(int hWnd, String text, String caption, uint type);  &lt;br /&gt;}  &lt;br /&gt;许多受管辖的动态链接库函数期望你能够传递一个复杂的参数类型给函数，譬如一个用户定义的结构类型成员或者受管辖代码定义的一个类成员，这时你必须提供额外的信息格式化这个类型，以保持参数原有的布局和对齐。 &lt;br /&gt; &lt;br /&gt;C#提供了一个StructLayoutAttribute类，通过它你可以定义自己的格式化类型，在受管辖代码中，格式化类型是一个用StructLayoutAttribute说明的结构或类成员，通过它能够保证其内部成员预期的布局信息。布局的选项共有三种： &lt;br /&gt; &lt;br /&gt;布局选项  &lt;br /&gt;描述  &lt;br /&gt;LayoutKind.Automatic  &lt;br /&gt;为了提高效率允许运行态对类型成员重新排序。  &lt;br /&gt;注意：永远不要使用这个选项来调用不受管辖的动态链接库函数。  &lt;br /&gt;LayoutKind.Explicit  &lt;br /&gt;对每个域按照FieldOffset属性对类型成员排序  &lt;br /&gt;LayoutKind.Sequential  &lt;br /&gt;对出现在受管辖类型定义地方的不受管辖内存中的类型成员进行排序。  &lt;br /&gt;传递结构成员  &lt;br /&gt;下面的例子说明如何在受管辖代码中定义一个点和矩形类型，并作为一个参数传递给User32.dll库中的PtInRect函数，  &lt;br /&gt;函数的不受管辖原型声明如下：  &lt;br /&gt;BOOL PtInRect(const RECT *lprc, POINT pt);  &lt;br /&gt;注意你必须通过引用传递Rect结构参数，因为函数需要一个Rect的结构指针。  &lt;br /&gt;[C#]  &lt;br /&gt;using System.Runtime.InteropServices;  &lt;br /&gt; &lt;br /&gt;[StructLayout(LayoutKind.Sequential)]  &lt;br /&gt;public struct Point {  &lt;br /&gt;public int x;  &lt;br /&gt;public int y;  &lt;br /&gt;}  &lt;br /&gt; &lt;br /&gt;[StructLayout(LayoutKind.Explicit]  &lt;br /&gt;public struct Rect {  &lt;br /&gt;[FieldOffset(0)] public int left;  &lt;br /&gt;[FieldOffset(4)] public int top;  &lt;br /&gt;[FieldOffset(8)] public int right;  &lt;br /&gt;[FieldOffset(12)] public int bottom;  &lt;br /&gt;}  &lt;br /&gt; &lt;br /&gt;class Win32API {  &lt;br /&gt;[DllImport("User32.dll")]  &lt;br /&gt;public static extern Bool PtInRect(ref Rect r, Point p);  &lt;br /&gt;}  &lt;br /&gt;类似你可以调用GetSystemInfo函数获得系统信息：  &lt;br /&gt;? using System.Runtime.InteropServices;  &lt;br /&gt;[StructLayout(LayoutKind.Sequential)]  &lt;br /&gt;public struct SYSTEM_INFO {  &lt;br /&gt;public uint dwOemId;  &lt;br /&gt;public uint dwPageSize;  &lt;br /&gt;public uint lpMinimumApplicationAddress;  &lt;br /&gt;public uint lpMaximumApplicationAddress;  &lt;br /&gt;public uint dwActiveProcessorMask;  &lt;br /&gt;public uint dwNumberOfProcessors;  &lt;br /&gt;public uint dwProcessorType;  &lt;br /&gt;public uint dwAllocationGranularity;  &lt;br /&gt;public uint dwProcessorLevel;  &lt;br /&gt;public uint dwProcessorRevision;  &lt;br /&gt;}  &lt;br /&gt; &lt;br /&gt; &lt;br /&gt; &lt;br /&gt; &lt;br /&gt;[DllImport("kernel32")]  &lt;br /&gt;static extern void GetSystemInfo(ref SYSTEM_INFO pSI);  &lt;br /&gt; &lt;br /&gt;SYSTEM_INFO pSI = new SYSTEM_INFO();  &lt;br /&gt;GetSystemInfo(ref pSI);  &lt;br /&gt; &lt;br /&gt;类成员的传递  &lt;br /&gt;同样只要类具有一个固定的类成员布局，你也可以传递一个类成员给一个不受管辖的动态链接库函数，下面的例子主要说明如何传递一个sequential顺序定义的MySystemTime类给User32.dll的GetSystemTime函数, 函数用C/C++调用规范如下: &lt;br /&gt; &lt;br /&gt;void GetSystemTime(SYSTEMTIME* SystemTime);  &lt;br /&gt;不像传值类型,类总是通过引用传递参数.  &lt;br /&gt;[C#]  &lt;br /&gt;[StructLayout(LayoutKind.Sequential)]  &lt;br /&gt;public class MySystemTime {  &lt;br /&gt;public ushort wYear;  &lt;br /&gt;public ushort wMonth;  &lt;br /&gt;public ushort wDayOfWeek;  &lt;br /&gt;public ushort wDay;  &lt;br /&gt;public ushort wHour;  &lt;br /&gt;public ushort wMinute;  &lt;br /&gt;public ushort wSecond;  &lt;br /&gt;public ushort wMilliseconds;  &lt;br /&gt;}  &lt;br /&gt;class Win32API {  &lt;br /&gt;[DllImport("User32.dll")]  &lt;br /&gt;public static extern void GetSystemTime(MySystemTime st);  &lt;br /&gt;}  &lt;br /&gt;回调函数的传递:  &lt;br /&gt;从受管辖的代码中调用大多数动态链接库函数,你只需创建一个受管辖的函数定义，然后调用它即可,这个过程非常直接。  &lt;br /&gt;如果一个动态链接库函数需要一个函数指针作为参数，你还需要做以下几步：  &lt;br /&gt;首先，你必须参考有关这个函数的文档，确定这个函数是否需要一个回调；第二，你必须在受管辖代码中创建一个回调函数；最后，你可以把指向这个函数的指针作为一个参数创递给DLL函数,. &lt;br /&gt; &lt;br /&gt;回调函数及其实现:  &lt;br /&gt;回调函数经常用在任务需要重复执行的场合,譬如用于枚举函数,譬如Win32 API 中的EnumFontFamilies(字体枚举), EnumPrinters(打印机), EnumWindows (窗口枚举)函数. 下面以窗口枚举为例,谈谈如何通过调用EnumWindow 函数遍历系统中存在的所有窗口 &lt;br /&gt; &lt;br /&gt;分下面几个步骤:  &lt;br /&gt;1. 在实现调用前先参考函数的声明  &lt;br /&gt;BOOL EnumWindows(WNDENUMPROC lpEnumFunc, LPARMAM IParam)  &lt;br /&gt;显然这个函数需要一个回调函数地址作为参数.  &lt;br /&gt;2. 创建一个受管辖的回调函数,这个例子声明为代表类型(delegate),也就是我们所说的回调,它带有两个参数hwnd和lparam,第一个参数是一个窗口句柄，第二个参数由应用程序定义，两个参数均为整形。 &lt;br /&gt; &lt;br /&gt;　　当这个回调函数返回一个非零值时，标示执行成功，零则暗示失败，这个例子总是返回True值，以便持续枚举。  &lt;br /&gt;3. 最后创建以代表对象(delegate)，并把它作为一个参数传递给EnumWindows 函数，平台会自动地 把代表转化成函数能够识别的回调格式。 &lt;br /&gt; &lt;br /&gt;[C#]  &lt;br /&gt;using System;  &lt;br /&gt;using System.Runtime.InteropServices;  &lt;br /&gt; &lt;br /&gt;public delegate bool CallBack(int hwnd, int lParam);  &lt;br /&gt; &lt;br /&gt;public class EnumReportApp {  &lt;br /&gt; &lt;br /&gt;[DllImport("user32")]  &lt;br /&gt;public static extern int EnumWindows(CallBack x, int y);  &lt;br /&gt; &lt;br /&gt;public static void Main()  &lt;br /&gt;{  &lt;br /&gt;CallBack myCallBack = new CallBack(EnumReportApp.Report);  &lt;br /&gt;EnumWindows(myCallBack, 0);  &lt;br /&gt;}  &lt;br /&gt; &lt;br /&gt;public static bool Report(int hwnd, int lParam) {  &lt;br /&gt;Console.Write("窗口句柄为");  &lt;br /&gt;Console.WriteLine(hwnd);  &lt;br /&gt;return true;  &lt;br /&gt;}  &lt;br /&gt;}  &lt;br /&gt; &lt;br /&gt; &lt;br /&gt; &lt;br /&gt;指针类型参数传递：  &lt;br /&gt;　在Windows API函数调用时，大部分函数采用指针传递参数，对一个结构变量指针，我们除了使用上面的类和结构方法传递参数之外，我们有时还可以采用数组传递参数。 &lt;br /&gt; &lt;br /&gt;　下面这个函数通过调用GetUserName获得用户名  &lt;br /&gt;BOOL GetUserName(  &lt;br /&gt;LPTSTR lpBuffer, // 用户名缓冲区  &lt;br /&gt;LPDWORD nSize // 存放缓冲区大小的地址指针  &lt;br /&gt;);  &lt;br /&gt;　  &lt;br /&gt;[DllImport("Advapi32.dll",  &lt;br /&gt;EntryPoint="GetComputerName",  &lt;br /&gt;ExactSpelling=false,  &lt;br /&gt;SetLastError=true)]  &lt;br /&gt;static extern bool GetComputerName (  &lt;br /&gt;[MarshalAs(UnmanagedType.LPArray)] byte[] lpBuffer,  &lt;br /&gt;　 [MarshalAs(UnmanagedType.LPArray)] Int32[] nSize );  &lt;br /&gt;　这个函数接受两个参数，char * 和int *,因为你必须分配一个字符串缓冲区以接受字符串指针，你可以使用String类代替这个参数类型，当然你还可以声明一个字节数组传递ANSI字符串，同样你也可以声明一个只有一个元素的长整型数组，使用数组名作为第二个参数。上面的函数可以调用如下： &lt;br /&gt; &lt;br /&gt;byte[] str=new byte[20];  &lt;br /&gt;Int32[] len=new Int32[1];  &lt;br /&gt;len[0]=20;  &lt;br /&gt;GetComputerName (str,len);  &lt;br /&gt;MessageBox.Show(System.Text.Encoding.ASCII.GetString(str));  &lt;br /&gt;　最后需要提醒的是，每一种方法使用前必须在文件头加上：  &lt;br /&gt;　using System.Runtime.InteropServices;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5349536066535334815-2038488765312221600?l=yourmagicsearchengine.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://yourmagicsearchengine.blogspot.com/feeds/2038488765312221600/comments/default' title='帖子评论'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=5349536066535334815&amp;postID=2038488765312221600' title='0 条评论'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5349536066535334815/posts/default/2038488765312221600'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5349536066535334815/posts/default/2038488765312221600'/><link rel='alternate' type='text/html' href='http://yourmagicsearchengine.blogspot.com/2007/03/visual-cwindows-apizz.html' title='用Visual C#调用Windows API函数(ZZ)'/><author><name>Yichuan Cai's home page</name><uri>http://www.blogger.com/profile/10921376371265973337</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5349536066535334815.post-7830106224576946142</id><published>2007-03-05T23:22:00.000-08:00</published><updated>2007-03-05T23:23:25.600-08:00</updated><title type='text'>利用Lucene搜索Java源代码  (ZZ)</title><content type='html'>package com.infosys.lucene.code JavaSourceCodeAnalyzer.;import java.io.Reader;import java.util.Set;import org.apache.lucene.analysis.*;public class JavaSourceCodeAnalyzer extends Analyzer {      private Set javaStopSet;      private Set englishStopSet;      private static final String[] JAVA_STOP_WORDS = {         "public","private","protected","interface",            "abstract","implements","extends","null""new",           "switch","case", "default" ,"synchronized" ,            "do", "if", "else", "break","continue","this",           "assert" ,"for","instanceof", "transient",            "final", "static" ,"void","catch","try",            "throws","throw","class", "finally","return",            "const" , "native", "super","while", "import",            "package" ,"true", "false" };     private static final String[] ENGLISH_STOP_WORDS ={            "a", "an", "and", "are","as","at","be" "but",            "by", "for", "if", "in", "into", "is", "it",            "no", "not", "of", "on", "or", "s", "such",            "that", "the", "their", "then", "there","these",            "they", "this", "to", "was", "will", "with" };     public SourceCodeAnalyzer(){            super();            javaStopSet = StopFilter.makeStopSet(JAVA_STOP_WORDS);            englishStopSet = StopFilter.makeStopSet(ENGLISH_STOP_WORDS);     }     public TokenStream tokenStream(String fieldName, Reader reader) {            if (fieldName.equals("comment"))                     return   new PorterStemFilter(new StopFilter(                        new LowerCaseTokenizer(reader),englishStopSet));            else                     return   new StopFilter(                   new LowerCaseTokenizer(reader),javaStopSet);     }}&lt;br /&gt;&lt;br /&gt;　　编写类JavaSourceCodeIndexer&lt;br /&gt;　　第二步生成索引。用来建立索引的非常重要的类有IndexWriter、Analyzer、Document和Field。对每一个源代码文件建立Lucene的一个Document实例。解析源代码文件并且摘录出与代码相关的语法元素，主要包括：导入声明、类名称、所继承的类、实现的接口、实现的方法、方法使用的参数和每个方法的代码等。然后把这些句法元素添加到Document实例中每个独立的Field实例中。然后使用存储索引的IndexWriter实例将Document实例添加到索引中。&lt;br /&gt;&lt;br /&gt;　　下面列出了JavaSourceCodeIndexer类的源代码。该类使用了JavaParser类解析Java文件和摘录语法元素，也可以使用Eclipse3.0 ASTParser。这里就不探究JavaParser类的细节了，因为其它解析器也可以用于提取相关源码元素。在源代码文件提取元素的过程中，创建Filed实例并添加到Document实例中。&lt;br /&gt;import org.apache.lucene.document.*;&lt;br /&gt;import org.apache.lucene.index.*;&lt;br /&gt;import com.infosys.lucene.code.JavaParser.*;&lt;br /&gt;&lt;br /&gt;public class JavaSourceCodeIndexer {&lt;br /&gt;    private static JavaParser parser = new JavaParser();&lt;br /&gt;        private static final String IMPLEMENTS = "implements";&lt;br /&gt;        private static final String IMPORT = "import";&lt;br /&gt;        ...&lt;br /&gt;        public static void main(String[] args) {&lt;br /&gt;                File indexDir = new File("C:\\Lucene\\Java");&lt;br /&gt;                File dataDir = new File("C:\\JavaSourceCode ");&lt;br /&gt;                IndexWriter writer = new IndexWriter(indexDir,&lt;br /&gt;                    new JavaSourceCodeAnalyzer(), true);&lt;br /&gt;                indexDirectory(writer, dataDir);&lt;br /&gt;                writer.close();&lt;br /&gt;        }&lt;br /&gt;        public static void indexDirectory(IndexWriter writer, File dir){&lt;br /&gt;            File[] files = dir.listFiles();&lt;br /&gt;            for (int i = 0; i &lt; files.length; i++) {&lt;br /&gt;                    File f = files[i];&lt;br /&gt;                // Create a Lucene Document&lt;br /&gt;                Document doc = new Document();&lt;br /&gt;                //  Use JavaParser to parse file&lt;br /&gt;                parser.setSource(f);&lt;br /&gt;                addImportDeclarations(doc, parser);&lt;br /&gt;                        addComments(doc, parser);&lt;br /&gt;                 // Extract Class elements Using Parser&lt;br /&gt;                JClass cls = parser.getDeclaredClass();&lt;br /&gt;                addClass(doc, cls);&lt;br /&gt;                 // Add field to the Lucene Document&lt;br /&gt;                       doc.add(Field.UnIndexed(FILENAME, f.getName()));&lt;br /&gt;                writer.addDocument(doc);&lt;br /&gt;                    }&lt;br /&gt;        }&lt;br /&gt;        private static void addClass(Document doc, JClass cls) {&lt;br /&gt;                   //For each class add Class Name field&lt;br /&gt;            doc.add(Field.Text(CLASS, cls.className));&lt;br /&gt;            String superCls = cls.superClass;&lt;br /&gt;            if (superCls != null)&lt;br /&gt;                   //Add the class it extends as extends field&lt;br /&gt;        doc.add(Field.Text(EXTENDS, superCls));&lt;br /&gt;            // Add interfaces it implements&lt;br /&gt;            ArrayList interfaces = cls.interfaces;&lt;br /&gt;            for (int i = 0; i &lt; interfaces.size(); i++)&lt;br /&gt;                doc.add(Field.Text(IMPLEMENTS, (String) interfaces.get(i)));&lt;br /&gt;                    //Add details  on methods declared&lt;br /&gt;            addMethods(cls, doc);&lt;br /&gt;            ArrayList innerCls = cls.innerClasses;&lt;br /&gt;                   for (int i = 0; i &lt; innerCls.size(); i++)&lt;br /&gt;                addClass(doc, (JClass) innerCls.get(i));&lt;br /&gt;        }&lt;br /&gt;        private static void addMethods(JClass cls, Document doc) {&lt;br /&gt;            ArrayList methods = cls.methodDeclarations;&lt;br /&gt;            for (int i = 0; i &lt; methods.size(); i++) {&lt;br /&gt;                       JMethod method = (JMethod) methods.get(i);&lt;br /&gt;                // Add method name field&lt;br /&gt;                doc.add(Field.Text(METHOD, method.methodName));&lt;br /&gt;                // Add return type field&lt;br /&gt;                doc.add(Field.Text(RETURN, method.returnType));&lt;br /&gt;                ArrayList params = method.parameters;&lt;br /&gt;                for (int k = 0; k &lt; params.size(); k++)&lt;br /&gt;                // For each method add parameter types&lt;br /&gt;                    doc.add(Field.Text(PARAMETER, (String)params.get(k)));&lt;br /&gt;                String code = method.codeBlock;&lt;br /&gt;                if (code != null)&lt;br /&gt;                //add the method code block&lt;br /&gt;                    doc.add(Field.UnStored(CODE, code));&lt;br /&gt;            }&lt;br /&gt;        }&lt;br /&gt;        private static void addImportDeclarations(Document doc, JavaParser parser) {&lt;br /&gt;                   ArrayList imports = parser.getImportDeclarations();&lt;br /&gt;            if (imports == null)     return;&lt;br /&gt;            for (int i = 0; i &lt; imports.size(); i++)&lt;br /&gt;                    //add import declarations as keyword&lt;br /&gt;                doc.add(Field.Keyword(IMPORT, (String) imports.get(i)));&lt;br /&gt;        }&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;　　Lucene有四种不同的字段类型：Keyword，UnIndexed，UnStored和Text，用于指定建立最佳索引。&lt;br /&gt;&amp;#61548;        Keyword字段是指不需要分析器解析但需要被编入索引并保存到索引中的部分。JavaSourceCodeIndexer类使用该字段来保存导入类的声明。&lt;br /&gt;&amp;#61548;        UnIndexed字段是既不被分析也不被索引，但是要被逐字逐句的将其值保存到索引中。由于我们一般要存储文件的位置但又很少用文件名作为关键字来搜索，所以用该字段来索引Java文件名。&lt;br /&gt;&amp;#61548;        UnStored字段和UnIndexed字段相反。该类型的Field要被分析并编入索引，但其值不会被保存到索引中。由于存储方法的全部源代码需要大量的空间。所以用UnStored字段来存储被索引的方法源代码。可以直接从Java源文件中取出方法的源代码，这样作可以控制我们的索引的大小。&lt;br /&gt;&amp;#61548;        Text字段在索引过程中是要被分析、索引并保存的。类名是作为Text字段来保存。下表展示了JavaSourceCodeIndexer类使用Field字段的一般情况。&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;1.   用Lucene建立的索引可以用Luke预览和修改，Luke是用于理解索引很有用的一个开源工具。图1中是Luke工具的一张截图，它显示了JavaSourceCodeIndexer类建立的索引。&lt;br /&gt;&lt;br /&gt; &lt;br /&gt;图1：在Luke中索引截图&lt;br /&gt;&lt;br /&gt;如图所见，导入类的声明没有标记化或分析就被保存了。类名和方法名被转换为小写字母后，才保存的。&lt;br /&gt;&lt;br /&gt;　　查询Java源代码&lt;br /&gt;　　建立多字段索引后，可以使用Lucene来查询这些索引。它提供了这两个重要类分别是IndexSearcher和QueryParser，用于搜索文件。QueryParser类则用于解析由用户输入的查询表达式，同时IndexSearcher类在文件中搜索满足查询条件的结果。下列表格显示了一些可能发生的查询及它的含义：&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;用户通过索引不同的语法元素组成有效的查询条件并搜索代码。下面列出了用于搜索的示例代码。&lt;br /&gt;public class JavaCodeSearch {&lt;br /&gt;public static void main(String[] args) throws Exception{&lt;br /&gt;    File indexDir = new File(args[0]);&lt;br /&gt;    String q =  args[1]; //parameter:JGraph code:insert&lt;br /&gt;    Directory fsDir = FSDirectory.getDirectory(indexDir,false);&lt;br /&gt;    IndexSearcher is = new IndexSearcher(fsDir);&lt;br /&gt;&lt;br /&gt;    PerFieldAnalyzerWrapper analyzer = new&lt;br /&gt;        PerFieldAnalyzerWrapper( new&lt;br /&gt;                JavaSourceCodeAnalyzer());&lt;br /&gt;&lt;br /&gt;    analyzer.addAnalyzer("import", new KeywordAnalyzer());&lt;br /&gt;    Query query = QueryParser.parse(q, "code", analyzer);&lt;br /&gt;    long start = System.currentTimeMillis();&lt;br /&gt;    Hits hits = is.search(query);&lt;br /&gt;    long end = System.currentTimeMillis();&lt;br /&gt;    System.err.println("Found " + hits.length() +&lt;br /&gt;                " docs in " + (end-start) + " millisec");&lt;br /&gt;    for(int i = 0; i &lt; hits.length(); i++){&lt;br /&gt;    Document doc = hits.doc(i);&lt;br /&gt;        System.out.println(doc.get("filename")&lt;br /&gt;                + " with a score of " + hits.score(i));&lt;br /&gt;    }&lt;br /&gt;    is.close();&lt;br /&gt;}&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;　　IndexSearcher实例用FSDirectory来打开包含索引的目录。然后使用Analyzer实例分析搜索用的查询字符串，以确保它与索引（还原词根，转换小写字母，过滤掉，等等）具有同样的形式。为了避免在查询时将Field作为一个关键字索引，Lucene做了一些限制。Lucene用Analyzer分析在QueryParser实例里传给它的所有字段。为了解决这个问题，可以用Lucene提供的PerFieldAnalyzerWrapper类为查询中的每个字段指定必要的分析。因此，查询字符串import：org.w3c.* AND code:Document将用KeywordAnalyzer来解析字符串org.w3c.*并且用JavaSourceCodeAnalyzer来解析Document。QueryParser实例如果查询没有与之相符的字段，就使用默认的字段：code，使用PerFieldAnalyzerWrapper来分析查询字符串，并返回分析后的Query实例。IndexSearcher实例使用Query实例并返回一个Hits实例，它包含了满足查询条件的文件。&lt;br /&gt;&lt;br /&gt;　　结束语&lt;br /&gt;&lt;br /&gt;　　这篇文章介绍了Lucene——文本搜索引擎，其可以通过加载分析器及多字段索引来实现源代码搜索。文章只介绍了代码搜索引擎的基本功能，同时在源码检索中使用愈加完善的分析器可以提高检索性能并获得更好的查询结果。这种搜索引擎可以允许用户在软件开发社区搜索和共享源代码。&lt;br /&gt;&lt;br /&gt;　　资源&lt;br /&gt;这篇文章的示例Sample code&lt;br /&gt;Matrix:http://www.matrix.org.cn/&lt;br /&gt;Onjava:http://www.onjava.com/&lt;br /&gt;Lucene home page &lt;br /&gt;"Introduction to Text Indexing with Apache Jakarta Lucene:" 这是篇简要介绍使用Lucene的文章。 &lt;br /&gt;Lucene in Action: 在使用Lucene方面进行了深入地讲解。&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5349536066535334815-7830106224576946142?l=yourmagicsearchengine.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://yourmagicsearchengine.blogspot.com/feeds/7830106224576946142/comments/default' title='帖子评论'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=5349536066535334815&amp;postID=7830106224576946142' title='0 条评论'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5349536066535334815/posts/default/7830106224576946142'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5349536066535334815/posts/default/7830106224576946142'/><link rel='alternate' type='text/html' href='http://yourmagicsearchengine.blogspot.com/2007/03/lucenejava-zz.html' title='利用Lucene搜索Java源代码  (ZZ)'/><author><name>Yichuan Cai's home page</name><uri>http://www.blogger.com/profile/10921376371265973337</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5349536066535334815.post-1210214044466100306</id><published>2007-03-05T23:18:00.000-08:00</published><updated>2007-03-05T23:20:56.723-08:00</updated><title type='text'>Nutch爬虫工作流程及文件格式详细分析 (ZZ)</title><content type='html'>Nutch爬虫工作流程及文件格式详细分析    &lt;br /&gt;&lt;br /&gt;&lt;br /&gt;　　Crawler和Searcher两部分尽量分开的目的主要是为了使两部分可以分布式配置在硬件平台上，例如将Crawler和Searcher分别放在两个主机上，这样可以提升性能。&lt;br /&gt;&lt;br /&gt;　　爬虫，Crawler： &lt;br /&gt;&lt;br /&gt;　　Crawler的重点在两个方面，Crawler的工作流程和涉及的数据文件的格式和含义。数据文件主要包括三类，分别是web database，一系列的segment加上index，三者的物理文件分别存储在爬行结果目录下的db目录下webdb子文件夹内，segments文件夹和index文件夹。那么三者分别存储的信息是什么呢？&lt;br /&gt;&lt;br /&gt;　　Web database，也叫WebDB，其中存储的是爬虫所抓取网页之间的链接结构信息，它只在爬虫Crawler工作中使用而和Searcher的工作没有任何关系。WebDB内存储了两种实体的信息：page和link。Page实体通过描述网络上一个网页的特征信息来表征一个实际的网页，因为网页有很多个需要描述，WebDB中通过网页的URL和网页内容的MD5两种索引方法对这些网页实体进行了索引。Page实体描述的网页特征主要包括网页内的link数目，抓取此网页的时间等相关抓取信息，对此网页的重要度评分等。同样的，Link实体描述的是两个page实体之间的链接关系。WebDB构成了一个所抓取网页的链接结构图，这个图中Page实体是图的结点，而Link实体则代表图的边。&lt;br /&gt;&lt;br /&gt;　　一次爬行会产生很多个segment，每个segment内存储的是爬虫Crawler在单独一次抓取循环中抓到的网页以及这些网页的索引。Crawler爬行时会根据WebDB中的link关系按照一定的爬行策略生成每次抓取循环所需的fetchlist，然后Fetcher通过fetchlist中的URLs抓取这些网页并索引，然后将其存入segment。Segment是有时限的，当这些网页被Crawler重新抓取后，先前抓取产生的segment就作废了。在存储中。Segment文件夹是以产生时间命名的，方便我们删除作废的segments以节省存储空间。&lt;br /&gt;&lt;br /&gt;　　Index是Crawler抓取的所有网页的索引，它是通过对所有单个segment中的索引进行合并处理所得的。Nutch利用Lucene技术进行索引，所以Lucene中对索引进行操作的接口对Nutch中的index同样有效。但是需要注意的是，Lucene中的segment和Nutch中的不同，Lucene中的segment是索引index的一部分，但是Nutch中的segment只是WebDB中各个部分网页的内容和索引，最后通过其生成的index跟这些segment已经毫无关系了。&lt;br /&gt;&lt;br /&gt;　　Crawler工作流程： &lt;br /&gt;&lt;br /&gt;　　在分析了Crawler工作中设计的文件之后，接下来我们研究一下Crawler的抓取流程以及这些文件在抓取中扮演的角色。Crawler的工作原理主要是：首先Crawler根据WebDB生成一个待抓取网页的URL集合叫做Fetchlist，接着下载线程Fetcher开始根据Fetchlist将网页抓取回来，如果下载线程有很多个，那么就生成很多个Fetchlist，也就是一个Fetcher对应一个Fetchlist。然后Crawler根据抓取回来的网页WebDB进行更新，根据更新后的WebDB生成新的Fetchlist，里面是未抓取的或者新发现的URLs，然后下一轮抓取循环重新开始。这个循环过程可以叫做“产生/抓取/更新”循环。&lt;br /&gt;&lt;br /&gt;　　指向同一个主机上Web资源的URLs通常被分配到同一个Fetchlist中，这样的话防止过多的Fetchers对一个主机同时进行抓取造成主机负担过重。另外Nutch遵守Robots Exclusion Protocol，网站可以通过自定义Robots.txt控制Crawler的抓取。&lt;br /&gt;&lt;br /&gt;　　在Nutch中，Crawler操作的实现是通过一系列子操作的实现来完成的。这些子操作Nutch都提供了子命令行可以单独进行调用。下面就是这些子操作的功能描述以及命令行，命令行在括号中。&lt;br /&gt;&lt;br /&gt;　　1.       创建一个新的WebDb (admin db -create). &lt;br /&gt;&lt;br /&gt;　　2.       将抓取起始URLs写入WebDB中 (inject). &lt;br /&gt;&lt;br /&gt;　　3.       根据WebDB生成fetchlist并写入相应的segment(generate). &lt;br /&gt;&lt;br /&gt;　　4.       根据fetchlist中的URL抓取网页 (fetch). &lt;br /&gt;&lt;br /&gt;　　5.       根据抓取网页更新WebDb (updatedb). &lt;br /&gt;&lt;br /&gt;　　6.       循环进行3－5步直至预先设定的抓取深度。 &lt;br /&gt;&lt;br /&gt;　　7.       根据WebDB得到的网页评分和links更新segments (updatesegs). &lt;br /&gt;&lt;br /&gt;　　8.       对所抓取的网页进行索引(index). &lt;br /&gt;&lt;br /&gt;　　9.       在索引中丢弃有重复内容的网页和重复的URLs (dedup). &lt;br /&gt;&lt;br /&gt;　　10.   将segments中的索引进行合并生成用于检索的最终index(merge). &lt;br /&gt;&lt;br /&gt;　　Crawler详细工作流程是：在创建一个WebDB之后(步骤1), “产生/抓取/更新”循环(步骤3－6)根据一些种子URLs开始启动。当这个循环彻底结束，Crawler根据抓取中生成的segments创建索引（步骤7－10）。在进行重复URLs清除（步骤9）之前，每个segment的索引都是独立的（步骤8）。最终，各个独立的segment索引被合并为一个最终的索引index（步骤10）。&lt;br /&gt;&lt;br /&gt;　　其中有一个细节问题，Dedup操作主要用于清除segment索引中的重复URLs，但是我们知道，在WebDB中是不允许重复的URL存在的，那么为什么这里还要进行清除呢？原因在于抓取的更新。比方说一个月之前你抓取过这些网页，一个月后为了更新进行了重新抓取，那么旧的segment在没有删除之前仍然起作用，这个时候就需要在新旧segment之间进行除重。&lt;br /&gt;&lt;br /&gt;　　另外这些子操作在第一次进行Crawler运行时可能并不用到，它们主要用于接下来的索引更新，增量搜索等操作的实现。这些在以后的文章中我再详细讲。&lt;br /&gt;&lt;br /&gt;　　个性化设置： &lt;br /&gt;&lt;br /&gt;　　Nutch中的所有配置文件都放置在总目录下的conf子文件夹中，最基本的配置文件是conf/nutch-default.xml。这个文件中定义了Nutch的所有必要设置以及一些默认值，它是不可以被修改的。如果你想进行个性化设置，你需要在conf/nutch-site.xml进行设置，它会对默认设置进行屏蔽。&lt;br /&gt;&lt;br /&gt;　　Nutch考虑了其可扩展性，你可以自定义插件plugins来定制自己的服务，一些plugins存放于plugins子文件夹。Nutch的网页解析与索引功能是通过插件形式进行实现的，例如，对HTML文件的解析与索引是通过HTML document parsing plugin, parse-html实现的。所以你完全可以自定义各种解析插件然后对配置文件进行修改，然后你就可以抓取并索引各种类型的文件了。&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5349536066535334815-1210214044466100306?l=yourmagicsearchengine.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://yourmagicsearchengine.blogspot.com/feeds/1210214044466100306/comments/default' title='帖子评论'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=5349536066535334815&amp;postID=1210214044466100306' title='0 条评论'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5349536066535334815/posts/default/1210214044466100306'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5349536066535334815/posts/default/1210214044466100306'/><link rel='alternate' type='text/html' href='http://yourmagicsearchengine.blogspot.com/2007/03/nutch-zz.html' title='Nutch爬虫工作流程及文件格式详细分析 (ZZ)'/><author><name>Yichuan Cai's home page</name><uri>http://www.blogger.com/profile/10921376371265973337</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5349536066535334815.post-6469816597730638141</id><published>2007-03-02T05:59:00.000-08:00</published><updated>2007-03-02T06:19:26.669-08:00</updated><title type='text'>Nutch初体验(ZZ)</title><content type='html'>&lt;div class="post"&gt;&lt;div class="postTitle"&gt;&lt;a href="http://blog.csdn.net/setok/articles/499791.aspx"&gt;http://blog.csdn.net/setok/articles/499791.aspx&lt;/a&gt;&lt;p&gt;&lt;/p&gt;前几天看到卢亮的 &lt;a href="&lt;a"&gt;&lt;span style="color:#0000cc;"&gt;Larbin 一种高效的搜索引擎爬虫工具&lt;/span&gt;&lt;/a&gt; 一文提到 &lt;a href="&lt;a"&gt;http://hedong.3322.org/"&gt;&lt;span style="color:#0000cc;"&gt;竹笋炒肉&lt;/span&gt;&lt;/a&gt;中对 Nutch 进行了一下&lt;a href="&lt;a"&gt;&lt;color="#0000cc"&gt;介绍&lt;/span&gt;&lt;/a&gt;。 &lt;p&gt;&lt;/p&gt;&lt;div id="a000051more"&gt;&lt;div id="more"&gt;&lt;p&gt;&lt;strong&gt;Nutch vs Lucene&lt;/strong&gt;&lt;br /&gt;Lucene 不是完整的应用程序，而是一个用于实现全文检索的软件库。&lt;br /&gt;Nutch 是一个应用程序，可以以 Lucene 为基础实现搜索引擎应用。&lt;/p&gt;&lt;p&gt;&lt;strong&gt;Nutch vs GRUB&lt;/strong&gt;&lt;br /&gt;&lt;a href="&lt;a"&gt;http://www.wespoke.com/archives/000879.php"&gt;&lt;span style="color:#0000cc;"&gt;这里&lt;/span&gt;&lt;/a&gt;］&lt;br /&gt;Nutch 则还可以存储到数据库并建立索引。&lt;br /&gt;&lt;img height="220" alt="Nutch Architecture.png" src="http://www.dbanotes.net/archives/images/Nutch%20Architecture.png" width="316" /&gt;&lt;br /&gt;［引自&lt;a href="&lt;a"&gt;&lt;color="#0000cc"&gt;这里&lt;/span&gt;&lt;/a&gt;］&lt;br /&gt;&lt;/p&gt;&lt;p&gt;Nutch 的早期版本不支持中文搜索，而最新的版本(2004-Aug-04 发布了 &lt;a href="&lt;a"&gt;http://www.dbanotes.net&lt;/a&gt;)为例，先进行一下针对企业内部网的测试。&lt;/p&gt;&lt;p&gt;在 nutch 目录中创建一个包含该网站顶级网址的文件 urls ，包含如下内容： &lt;/p&gt;&lt;blockquote&gt;&lt;pre&gt;http://www.dbanotes.net/&lt;/pre&gt;&lt;/blockquote&gt;&lt;p&gt;然后编辑conf/crawl-urlfilter.txt 文件，设定过滤信息，我这里只修改了MY.DOMAIN.NAME:&lt;/p&gt;&lt;blockquote&gt;&lt;pre&gt;# accept hosts in MY.DOMAIN.NAME+^http://([a-z0-9]*\.)*dbanotes.net/&lt;/pre&gt;&lt;/blockquote&gt;&lt;p&gt;运行如下命令开始抓取分析网站内容：&lt;/p&gt;&lt;blockquote&gt;&lt;pre&gt;[root@fc3 nutch]# bin/nutch crawl urls -dir crawl.demo -depth 2 -threads 4 &amp;gt;&amp; crawl.log&lt;/pre&gt;&lt;/blockquote&gt;&lt;p&gt;depth 参数指爬行的深度，这里处于测试的目的，选择深度为 2 ；&lt;br /&gt;threads 参数指定并发的进程 这是设定为 4 ；&lt;/p&gt;&lt;p&gt;在该命令运行的过程中，可以从 crawl.log 中查看 nutch 的行为以及过程:&lt;/p&gt;&lt;blockquote&gt;&lt;pre&gt;......050102 200336 loading file:/u01/nutch/conf/nutch-site.xml050102 200336 crawl started in: crawl.demo 050102 200336 rootUrlFile = urls 050102 200336 threads = 4050102 200336 depth = 2050102 200336 Created webdb at crawl.demo/db......050102 200336 loading file:/u01/nutch/conf/nutch-site.xml050102 200336 crawl started in: crawl.demo050102 200336 rootUrlFile = urls050102 200336 threads = 4050102 200336 depth = 2050102 200336 Created webdb at crawl.demo/db050102 200336 Starting &lt;acronym title="Unified Resource Locator"&gt;URL&lt;/acronym&gt; processing050102 200336 &lt;strong&gt;Using &lt;acronym title="Unified Resource Locator"&gt;URL&lt;/acronym&gt; filter: net.nutch.net.RegexURLFilter&lt;/strong&gt;......                               050102 200337 Plugins: looking in: /u01/nutch/plugins                  050102 200337 parsing: /u01/nutch/plugins/parse-html/plugin.xml        050102 200337 parsing: /u01/nutch/plugins/parse-pdf/plugin.xml         050102 200337 parsing: /u01/nutch/plugins/parse-ext/plugin.xml         050102 200337 parsing: /u01/nutch/plugins/parse-msword/plugin.xml      050102 200337 parsing: /u01/nutch/plugins/query-site/plugin.xml        050102 200337 parsing: /u01/nutch/plugins/protocol-http/plugin.xml     050102 200337 parsing: /u01/nutch/plugins/creativecommons/plugin.xml050102 200337 parsing: /u01/nutch/plugins/language-identifier/plugin.xml050102 200337 parsing: /u01/nutch/plugins/query-basic/plugin.xml       050102 200337 logging at INFO                                          050102 200337 fetching &lt;a href="http://www.dbanotes.net/"&gt;http://www.dbanotes.net/&lt;/a&gt;                        050102 200337 http.proxy.host = null                                   050102 200337 http.proxy.port = 8080                                   050102 200337 http.timeout = 10000                                     050102 200337 http.content.limit = 65536                               050102 200337 http.agent = NutchCVS/0.05 (Nutch; &lt;a href="http://www.nutch.org/docs/en/bot.html"&gt;http://www.nutch.org/docs/en/bot.html&lt;/a&gt;; &lt;a href="mailto:nutch-agent@lists.sourceforge.net)050102"&gt;nutch-agent@lists.sourceforge.net)050102&lt;/a&gt; 200337 fetcher.server.delay = 1000                              050102 200337 http.max.delays = 100                                    050102 200338 &lt;a href="http://www.dbanotes.net/"&gt;http://www.dbanotes.net/&lt;/a&gt;: setting encoding to GB18030    050102 200338 &lt;acronym title="Creative Commons"&gt;CC&lt;/acronym&gt;: found &lt;a href="http://creativecommons.org/licenses/by-nc-sa/2.0/"&gt;http://creativecommons.org/licenses/by-nc-sa/2.0/&lt;/a&gt; in rdf of &lt;a href="http://www.dbanotes.net/050102"&gt;http://www.dbanotes.net/050102&lt;/a&gt; 200338 &lt;acronym title="Creative Commons"&gt;CC&lt;/acronym&gt;: found text in &lt;a href="http://www.dbanotes.net/"&gt;http://www.dbanotes.net/&lt;/a&gt;               050102 200338 status: 1 pages, 0 errors, 12445 bytes, 1067 ms          050102 200338 status: 0.9372071 pages/s, 91.12142 kb/s, 12445.0 bytes/page050102 200339 Updating crawl.demo/db                                   050102 200339 Updating for crawl.demo/segments/20050102200336          050102 200339 Finishing update                                                                                                                64,1           7%050102 200337 parsing: /u01/nutch/plugins/query-basic/plugin.xml050102 200337 logging at INFO050102 200337 fetching &lt;a href="http://www.dbanotes.net/050102"&gt;http://www.dbanotes.net/050102&lt;/a&gt; 200337 http.proxy.host = null050102 200337 http.proxy.port = 8080050102 200337 http.timeout = 10000050102 200337 http.content.limit = 65536050102 200337 http.agent = NutchCVS/0.05 (Nutch; &lt;a href="http://www.nutch.org/docs/en/bot.html"&gt;http://www.nutch.org/docs/en/bot.html&lt;/a&gt;; &lt;a href="mailto:nutch-agent@lists.sourceforge.net)050102"&gt;nutch-agent@lists.sourceforge.net)050102&lt;/a&gt; 200337 fetcher.server.delay = 1000050102 200337 http.max.delays = 100......&lt;/pre&gt;&lt;/blockquote&gt;&lt;p&gt;之后配置 Tomcat (我的 tomcat 安装在 /opt/Tomcat) ， &lt;/p&gt;&lt;blockquote&gt;&lt;pre&gt;[root@fc3 nutch]# rm -rf /opt/Tomcat/webapps/ROOT*[root@fc3 nutch]# cp nutch*.war /opt/Tomcat/webapps/ROOT.war[root@fc3 webapps]# cd /opt/Tomcat/webapps/[root@fc3 webapps]# jar xvf ROOT.war[root@fc3 webapps]# ../bin/catalina.sh start&lt;/pre&gt;&lt;/blockquote&gt;&lt;p&gt;浏览器中输入 &lt;a href="http://localhost:8080/"&gt;http://localhost:8080/&lt;/a&gt; 查看结果(远程查看需要将 localhost 换成相应的IP)：&lt;/p&gt;&lt;p&gt;&lt;img height="360" alt="nutch web search interface.png" src="http://www.dbanotes.net/archives/images/nutch%20web%20search%20interface.png" width="648" /&gt; &lt;/p&gt;&lt;p&gt;搜索测试：&lt;/p&gt;&lt;p&gt;&lt;img height="367" alt="nutch web search result.png" src="http://www.dbanotes.net/archives/images/nutch%20web%20search%20result.png" width="650" /&gt; &lt;/p&gt;&lt;p&gt;可以看到，Nutch 亦提供快照功能。下面进行中文搜索测试:&lt;/p&gt;&lt;p&gt;&lt;img height="380" alt="nutch web Chinese search result.png" src="http://www.dbanotes.net/archives/images/nutch%20web%20Chinese%20search%20result.png" width="654" /&gt; &lt;/p&gt;&lt;p&gt;注意结果中的那个“评分详解”，是个很有意思的功能(Nutch 具有一个链接分析模块)，通过这些数据可以进一步理解该算法。&lt;/p&gt;&lt;p&gt;考虑到带宽的限制，暂时不对整个Web爬行的方式进行了测试了。值得一提的是，在测试的过程中，nutch 的爬行速度还是不错的(相对我的糟糕带宽)。&lt;/p&gt;&lt;p&gt;Nutch 目前还不支持 &lt;strong&gt;&lt;acronym title="Portable Document Format"&gt;PDF&lt;/acronym&gt;&lt;/strong&gt;(开发中，不够完善) 与 &lt;strong&gt;图片&lt;/strong&gt; 等对象的搜索。&lt;strong&gt;中文分词技术&lt;/strong&gt;还不够好，通过“评分详解”可看出，对中文，比如“数据库管理员”，是分成单独的字进行处理的。但作为一个开源搜索引擎软件，功能是可圈可点的。毕竟，主要开发者 &lt;a href="&lt;a"&gt;&lt; color="#0000cc"&gt;Doug Cutting&lt;/span&gt;&lt;/a&gt; 就是开发 &lt;a href="&lt;a"&gt;http://hedong.3322.org/archives/000247.html"&gt;&lt;color="#0000cc"&gt;试用Nutch&lt;/span&gt;&lt;/a&gt; &lt;/li&gt;&lt;li&gt;车东的 &lt;a href="http://www2.blogger.com/&lt;a%20title="&gt;http://www.chedong.com/tech/lucene.html"&gt;&lt;color="#0000cc"&gt;Lucene：基于Java的全文检索引擎简介&lt;/span&gt;&lt;/a&gt;&lt;br /&gt;&lt;/li&gt;&lt;ul&gt;&lt;/ul&gt;&lt;/div&gt;&lt;/div&gt;&lt;br /&gt;&lt;br /&gt;&lt;div class="post" id="csdn_zhaig_ad_yahoo"&gt;&lt;/div&gt;&lt;span id="Anthem_Comments.ascx_ltlComments__"&gt;&lt;span id="Comments.ascx_ltlComments"&gt;&lt;br /&gt;&lt;br /&gt;&lt;/span&gt;&lt;/span&gt;&lt;/rdf:descriptionrdf:about="http:&gt;&lt;span id="Anthem_Comments.ascx_ltlComments__"&gt;&lt;span id="Comments.ascx_ltlComments"&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/rdf:descriptionrdf:about="http:&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5349536066535334815-6469816597730638141?l=yourmagicsearchengine.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://yourmagicsearchengine.blogspot.com/feeds/6469816597730638141/comments/default' title='帖子评论'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=5349536066535334815&amp;postID=6469816597730638141' title='0 条评论'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5349536066535334815/posts/default/6469816597730638141'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5349536066535334815/posts/default/6469816597730638141'/><link rel='alternate' type='text/html' href='http://yourmagicsearchengine.blogspot.com/2007/03/nutchzz.html' title='Nutch初体验(ZZ)'/><author><name>Yichuan Cai's home page</name><uri>http://www.blogger.com/profile/10921376371265973337</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5349536066535334815.post-4666477197558316666</id><published>2007-02-26T05:23:00.000-08:00</published><updated>2007-02-26T05:27:15.850-08:00</updated><title type='text'>Eclipse 开发环境的调试功能总结（ZZ）</title><content type='html'>转载自&lt;a href="http://www.javaeye.com/article/799"&gt;http://www.javaeye.com/article/799&lt;/a&gt;&lt;br /&gt;本文概述了怎样使用 Eclipse 平台的内置调试功能来调试您的软件项目。调试是程序员无法回避的工作。调试方法有许多种，但归根结底，就是找到引发错误的代码。举例来说，在 Linux 应用程序中，分段故障被认为是最常见的错误之一。当程序尝试访问未分配给它的内存并因为分段违例而终止时，将产生这种错误。要修正这种错误，您需要找到引发该行为的那行代码。一旦找到有问题的代码行，这对于知道引发错误的上下文及其相关的值、变量和方法也是有所帮助的。使用调试器将使查找这些信息变得相当简单。Eclipse 调试器及 Debug 视图Eclipse 平台的特色在于内置了 Java 调试器，该调试器提供所有标准调试功能，包括进行单步执行、设置断点和值、检查变量和值以及暂挂和恢复线程的能力。此外，您还可以调试在远程机器上运行的应用程序。Eclipse 平台主要是一个 Java 开发环境，但其体系结构同时也向其它编程语言开放。如以下您将看到的，同一个 Eclipse 的 Debug 视图也可用于 C 和 C++ 编程语言。Eclipse 平台工作台（Eclipse Platform Workbench）及其工具是基于 Java 开发工具（JDT）组件所构建的。这些组件向 Eclipse 提供以下功能：* 项目管理工具* 透视图和视图* 构建器、编辑器、搜索和构建功能* 调试器Eclipse 调试器本身是作为 Eclipse 二进制文件中包含的标准插件而存在的。Eclipse 还有一个特别的 Debug 视图，允许您在工作台中管理程序的调试和运行。它为调试中的每个目标显示其暂挂线程的堆栈帧。程序中的各个线程以作为树的节点出现，而 Debug 视图则显示运行中的各目标的进程。如果暂挂一个线程，则其堆栈帧显示为子元素。在您开始使用 Eclipse 调试器之前，假定您已经安装了适当的 Java SDK/JRE（我推荐您使用 Java VM 1.4）和 Eclipse 平台 SDK 2.0/2.1，且两者都工作正常。一般来说，先使用 Eclipse 样本来测试一下调试选项是一个好主意。如果您要开发和调试 C/C++ 项目，您还需要得到并安装 C/C++ 开发工具（C/C++ Development Tool，CDT）。有关 Java SDK/JRE、Eclipse 平台和样本以及 CDT 的链接，请参阅本文后面的参考资料。图 1 显示了 Debug 用户界面的常规视图。图 1. Eclipse Debug 视图用户界面的常规视图[myimg]upload/debug.png[/myimg]调试 Java在您能调试您的项目之前，需要先完整地编译和运行代码。您首先需要为您的应用程序创建运行配置并确认其正常启动。之后，您需要使用 Run &gt; Debug... 菜单，以同样的方式设置调试配置。您还需要选择作为主 Java 类的由调试器使用的类（也请参阅图 2）。对一个项目，您希望有几种调试配置就可以有几种。当调试器启动后（通过 Run &gt; Debug...），会在一个新窗口打开它，您可以准备开始调试。图 2. 在调试配置中设置项目的主 Java 类[myimg]upload/debug-config.png[/myimg]以下是最常见的 Eclipse 调试操作的示例指示信息：设置断点当您启动应用程序以进行调试时，Eclipse 自动切换到 Debug 透视图。无庸置疑，最常见的调试过程就是设置断点，以允许检查在条件语句和循环中的变量和值。要在 Java 透视图的 Package Explorer 视图中设置断点，双击所选的源代码文件，在编辑器中打开它。遍历全部代码，将光标放置在含有可疑代码的那一行的标记栏上（在编辑器区域的左侧）。双击以设置断点（也请参阅图 3）。图 3. 在编辑器左侧边缘可看到两个断点标记[myimg]upload/breakpoints2.png[/myimg]现在通过 Run &gt; Debug... 菜单启动调试会话。有一点很重要，不要把数条语句放在同一行，因为您不能在同一行的多条语句上单步跳过或设置行断点（也请参阅图 4）。图 4. 视图通过左侧边缘的箭头指出当前正在执行的行[myimg]upload/pointer.png[/myimg]条件断点一旦您找到出错的地方，您会想要了解在崩溃前程序在干些什么。完成该工作的一种方法是单步执行程序中的每条语句，一次一句，直到到达出问题的地方。有时候更好的方法是仅运行某段代码并在出问题的地方终止其执行，这样就可以检查该位置上的数据。要实现这一点，可能要声明每当表达式的值更改时就被触发的条件断点（请参阅图 5）。此外，在输入条件表达式时还可以使用代码辅助。图 5. 设置条件断点触发器[myimg]upload/conditional.png[/myimg]对表达式求值要在 Debug 透视图的编辑器中求表达式的值，选中设置有断点的一整行，并在上下文菜单中选择 Inspect 选项（请参阅图 6）。表达式是在当前堆栈帧的上下文中求值的，其结果显示在 Display 窗口的 Expressions 视图中。图 6. 用 Inspect 选项求表达式的值[myimg]upload/debug-menu1.png[/myimg]查看变量Variables 视图（在 Display 窗口中）显示了选中的堆栈帧中的变量值（请参阅图 7）。要查看所请求的变量，只需展开 Variables 视图中的树直到您看到所请求的元素为止。您也可以在 Debug 视图中单步执行代码的同时，在 Variables 视图中查看变量。图 7. 在 Display 窗口中查看变量[myimg]upload/variables.png[/myimg]当调试器在断点上停止时，您可以通过在 Run &gt; Debug... 菜单上选择 Step Over 选项来继续调试器会话（请参阅图 8）。这将单步跳过突出显示的代码行并执行同一个方法中的下一行（或者它在调用当前方法的方法中继续）。作为最后一步的结果而发生更改的变量用颜色突出显示（缺省值是红色），所用颜色可在“Changed Variable Value Color”首选项（由 Debug Variable Views 指定）中指定。图 8. Run... 菜单中的调试器命令[myimg]upload/debug-menu.png[/myimg]要在 Debug 视图中暂挂线程的执行，选择一个运行中的线程并单击 Debug 视图工具栏中的 Suspend 按钮。会显示该线程的当前调用堆栈，并且在 Debug 透视图的编辑器中突出显示当前执行的行。暂挂线程时，将光标放置到 Java 编辑器中的变量上，该变量的值显示在一个小悬浮窗口中。同样，该线程的顶部堆栈帧被自动选中，该堆栈帧中的可视变量显示在 Variables 视图中。可以通过在 Variables 视图中单击变量名来检查相应的变量。热交换错误修正：实时代码修正如果您运行的是 JVM 1.4（Java 虚拟机，Java Virtual Machine），Eclipse 2.0.2 和 2.1 提供一个叫做热交换错误修正（Hotswap Bug Fixing）的新功能（无法用于 JVM 1.3 或更低版本 － 也请参阅图 9）。它允许在调试器会话过程中更改源代码，这要比“退出应用程序，更改源代码，重新编译，再启动另一个调试会话”的一系列步骤好多了。要使用该功能，只需在编辑器中更改代码并恢复调试。由于 JVM 1.4 与 Java 平台调试器体系结构（Java Platform Debugger Architecture，JPDA）兼容，所以能使用该功能。JPDA 实现了在运行中的应用程序中用经过修改的代码进行替换的能力。当然，当启动您的应用程序或找到出错点需要花费很长时间的时候，该功能极其有用。图 9. 热交换错误修正功能不能在 JVM 1.3 及更低版本中使用[myimg]upload/hot-code.png[/myimg]如果您完成调试时程序还没有被完整地执行过，请在 Debug 视图中的上下文菜单中选择 Terminate 选项。一个常见的错误是您在调试器会话中使用了 Debug 或 Run 而不是 Resume。这样将会启动另一个调试器会话，而不是继续当前的会话。远程调试Eclipse 调试器提供了一个有趣的选项，可用于调试远程应用程序。它可以连接到一个运行 Java 应用程序的远程 VM 上，并将其连接到内部调试器上。处理远程调试会话非常类似于本地调试。不过，远程调试配置要求对 Run &gt; Debug... 窗口进行不同的设置。您首先要选择左侧视图中的 Remote Java Application 项，单击 New 按钮。这样就创建了一个新的远程启动配置，并显示三个选项卡：Connect、Source 和 Common。在 Connect 选项卡的 Project 域中，选择用作启动首选项的项目（用于查找源代码）。在 Connect 选项卡的 Host 域中，输入运行 Java 程序的远程主机的 IP 地址或域名。在 Connect 选项卡的 Port 域中，输入远程 VM 接受连接的端口。一般来说，该端口是在远程 VM 启动时指定的。当您想让调试器确定 Terminate 命令在远程会话中是否可用，可以选择 Allow termination of remote VM 选项。如果您希望能终止所连接的 VM，则选择该选项。现在当您选择 Debug 选项时，调试器将尝试按指定的地址和端口连接远程 VM，并在 Debug 视图中显示结果。如果启动器无法连接至指定位置上的 VM，将显示错误消息。一般来说，远程调试功能的可用性完全取决于远程主机上运行的 Java VM（虚拟机，Virtual Machine）。图 10 显示了远程调试会话的连接属性的设置。图 10. 设置远程调试会话的连接属性[myimg]upload/remote-debug.png[/myimg]调试其它语言Java 是 Eclipse 平台的主语言。然而，Eclipse 平台同时也是一个可支持许多其它语言的可扩展平台，而其中最重要的就是支持 C/C++（因为其流行性）。Eclipse 通过用 C/C++ 开发工具（CDT）支持 C/C++。请参阅参考资料以获取相关链接。CDT 通过调试 C/C++ 代码的功能扩展了标准的 Eclipse Debug 视图，同时 CDT Debug 视图允许您在工作台中管理 C/C++ 项目的调试。CDT 不包含其内部调试器，但它向必须可在本地使用的 GNU GDB 调试器提供了一个前端。下载并安装了 CDT 之后，只需切换到 Debug 视图，您就可以开始调试当前的 C/C++ 项目了（请参阅参考资料，以获取一篇介绍如何安装 CDT 的文章的链接）。这样您可以设置（并在执行过程中任何时候更改）代码中的断点，并且跟踪变量和寄存器。Eclipse 调试器显示您调试中各个目标的暂挂线程的堆栈帧。程序中的各个线程作为树的节点出现。它显示了运行中各目标的进程。请记住当 GNU GDB 调试一个带有调试符号链接的程序时，它最有效。这是在编译过程中由命令行参数 -g 来实现的。需要更多的信息请使用 -ggdb 开关，该参数包含有特定于 GNU GDB 的调试符号。如果您要调试 servlet，使用 Sysdeo Eclipse Tomcat Launcher。该插件使您能够管理 Tomcat 4.x/3.3 servlet 容器（通过创建和导入一个 Tomcat WAR 项目）。它同时在一个内部 Java Eclipse 调试器中注册一个 Tomcat 进程，这样您就能方便地调试 Tomcat 应用程序了。还有其它几个 Eclipse 插件，使我们能够对 servlet 使用内部 Eclipse 调试器，比如 Cactus 的 Eclipse 插件，Resin 插件和 X-Parrots ServletExec 插件。在下面的参考资料中可获得这些插件的链接。结束语Eclipse 平台提供了内置的 Java 调试器，该调试器具有标准调试功能，包括进行单步执行、设置断点和值、检查变量和值以及暂挂和恢复线程的能力。它还可以用于调试在远程机器上运行的应用程序。Eclipse 平台主要是一个 Java 开发环境，但是同一个 Eclipse 的 Debug 视图也可用于 C 和 C++ 编程语言。参考资料* 在 eclipse.org 加入 Eclipse 平台社区并下载该平台。Eclipse 平台源代码是通过普通公共许可证（Common Public License）进行许可的。您还能在 eclipse.org 找到 Eclipse 项目的术语词汇表及描述，以及技术文章和新闻组。Eclipse 平台白皮书详细描述了 Eclipse 的主要组件和功能。* 在 eclipse.org 下载 C/C++ 开发工具。* 下载 Sysdeo Eclipse Tomcat Launcher。* 以下是另外一些用于 servlet 的 Eclipse 插件的链接：o Cactus 的 Eclipse 插件o Resin 插件o X-Parrots ServletExec 插件* 查看 Eclipse 插件的分类注册表。* 以下文章可以帮助您开始学习用 Eclipse 进行调试：o 请参阅由 Greg Adams 和 Marc Erickson 撰写的 developerWorks 文章“Working the Eclipse Platform”，介绍了 Eclipse 平台及其工作方式。o 要着手使用 Eclipse 平台开发应用程序，请参阅由 David Gallardo 撰写的 developerWorks 文章“Getting started with the Eclipse Platform”。o 要学习 Eclipse 中的启动框架（Launching Framework），请参阅 eclipse.org 文章“We Have Lift-off: The Launching Framework in Eclipse”。o 要获取关于产品构建器和特性的更多信息，请参阅 eclipse.org 文章“Project Builders and Natures”。* 如果您是一名开发人员，且希望参与和实现不同 Eclipse 组件相关的讨论，请访问为该项目中各组件所创建的开发人员邮件列表。* 在 developerWorks 查找更多为 Eclipse 用户准备的参考资料。关于作者Paul Leszek，是 Studio B 的一名作者，同时也是专长于 Linux/Win/Mac OS 系统体系结构和管理的独立软件顾问。在许多操作系统、编程语言和网络协议，尤其是 Lotus Domino 和 DB2 方面，他都有经验。Paul 同时还是“LinuxWorld”系列文章的作者，以及波兰语版“PC World”的 Linux 专栏作家。&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5349536066535334815-4666477197558316666?l=yourmagicsearchengine.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://yourmagicsearchengine.blogspot.com/feeds/4666477197558316666/comments/default' title='帖子评论'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=5349536066535334815&amp;postID=4666477197558316666' title='0 条评论'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5349536066535334815/posts/default/4666477197558316666'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5349536066535334815/posts/default/4666477197558316666'/><link rel='alternate' type='text/html' href='http://yourmagicsearchengine.blogspot.com/2007/02/eclipse-zz.html' title='Eclipse 开发环境的调试功能总结（ZZ）'/><author><name>Yichuan Cai's home page</name><uri>http://www.blogger.com/profile/10921376371265973337</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5349536066535334815.post-6948371132839252170</id><published>2007-02-25T07:49:00.000-08:00</published><updated>2007-02-25T08:00:21.460-08:00</updated><title type='text'>Nutch=lucene+Crawler(转载)</title><content type='html'>&lt;a href="http://lucene.apache.org/nutch/index.html"&gt;&lt;a class="keyword" href="http://www.kehui.net/index.php?op=article&amp;action=keyword&amp;amp;keyword=Nutch"&gt;Nutch&lt;/a&gt;&lt;/a&gt; 是一个开源Java 实现的搜索引擎。它提供了我们运行自己的搜索引擎所需的全部工具。可以为什么我们需要建立自己的搜索引擎呢？毕竟我们已经有google可以使用。这里我列出3点原因：&lt;br /&gt;透明度：&lt;a class="keyword" href="http://www.kehui.net/index.php?op=article&amp;action=keyword&amp;amp;keyword=Nutch"&gt;Nutch&lt;/a&gt;是开放源代码的，因此任何人都可以查看他的排序算法是如何工作的。商业的搜索引擎排序算法都是保密的，我们无法知道为什么搜索出来的排序结果是如何算出来的。更进一步，一些搜索引擎允许竞价排名，比如百度，这样的索引结果并不是和站点内容相关的。因此 &lt;a class="keyword" href="http://www.kehui.net/index.php?op=article&amp;action=keyword&amp;amp;keyword=Nutch"&gt;Nutch&lt;/a&gt; 对学术搜索和政府类站点的搜索来说，是个好选择。因为一个公平的排序结果是非常重要的。&lt;br /&gt;对搜索引擎的理解：我们并没有google的源代码，因此学习搜索引擎&lt;a class="keyword" href="http://www.kehui.net/index.php?op=article&amp;action=keyword&amp;amp;keyword=Nutch"&gt;Nutch&lt;/a&gt;是个不错的选择。了解一个大型分布式的搜索引擎如何工作是一件让人很受益的事情。在写&lt;a class="keyword" href="http://www.kehui.net/index.php?op=article&amp;action=keyword&amp;amp;keyword=Nutch"&gt;Nutch&lt;/a&gt;的过程中，从学院派和工业派借鉴了很多知识：比如：&lt;a class="keyword" href="http://www.kehui.net/index.php?op=article&amp;action=keyword&amp;amp;keyword=Nutch"&gt;Nutch&lt;/a&gt;的核心部分目前已经被重新用 &lt;a href="http://labs.google.com/papers/mapreduce.html"&gt;Map Reduce&lt;/a&gt; 实现了。看过开复演讲的人都知道 &lt;a href="http://labs.google.com/papers/mapreduce.html"&gt;Map Reduce&lt;/a&gt; 的一点知识吧。&lt;a href="http://labs.google.com/papers/mapreduce.html"&gt;Map Reduce&lt;/a&gt; 是一个分布式的处理模型，最先是从 Google 实验室提出来的。你也可以从下面获得更多的消息。 &lt;a href="http://www.domolo.com/bbs/list.asp?boardid=29"&gt;http://www.domolo.com/bbs/list.asp?boardid=29&lt;/a&gt;&lt;a href="http://domolo.oicp.net/bbs/list.asp?boardid=29"&gt;http://domolo.oicp.net/bbs/list.asp?boardid=29&lt;/a&gt;并且 &lt;a class="keyword" href="http://www.kehui.net/index.php?op=article&amp;action=keyword&amp;amp;keyword=Nutch"&gt;Nutch&lt;/a&gt; 也吸引了很多研究者，他们非常乐于尝试新的搜索算法，因为对&lt;a class="keyword" href="http://www.kehui.net/index.php?op=article&amp;action=keyword&amp;amp;keyword=Nutch"&gt;Nutch&lt;/a&gt; 来说，这是非常容易实现扩展的。&lt;br /&gt;扩展性：你是不是不喜欢其他的搜索引擎展现结果的方式呢？那就用 &lt;a class="keyword" href="http://www.kehui.net/index.php?op=article&amp;action=keyword&amp;amp;keyword=Nutch"&gt;Nutch&lt;/a&gt; 写你自己的搜索引擎吧。 &lt;a class="keyword" href="http://www.kehui.net/index.php?op=article&amp;action=keyword&amp;amp;keyword=Nutch"&gt;Nutch&lt;/a&gt; 是非常灵活的：他可以被很好的客户订制并集成到你的应用程序中：使用&lt;a class="keyword" href="http://www.kehui.net/index.php?op=article&amp;action=keyword&amp;amp;keyword=Nutch"&gt;Nutch&lt;/a&gt; 的插件机制，&lt;a class="keyword" href="http://www.kehui.net/index.php?op=article&amp;action=keyword&amp;amp;keyword=Nutch"&gt;Nutch&lt;/a&gt; 可以作为一个搜索不同信息载体的搜索平台。当然，最简单的就是集成&lt;a class="keyword" href="http://www.kehui.net/index.php?op=article&amp;action=keyword&amp;amp;keyword=Nutch"&gt;Nutch&lt;/a&gt;到你的站点，为你的用户提供搜索服务。&lt;br /&gt;&lt;a class="keyword" href="http://www.kehui.net/index.php?op=article&amp;action=keyword&amp;amp;keyword=Nutch"&gt;Nutch&lt;/a&gt; 的安装分为3个层次：基于本地文件系统，基于局域网，或者基于 internet 。不同的安装方式具有不同的特色。比如：索引一个本地文件系统相对于其他两个来说肯定是要稳定多了，因为没有 网络错误也不同缓存文件的拷贝。基于Internet 的搜索又是另一个极端：抓取数以千计的网页有很多技术问题需要解决：我们从哪些页面开始抓取？我们如何分配抓取工作？何时需要重新抓取？我们如何解决失效的链接，没有响应的站点和重复的内容？还有如何解决对大型数据的上百个并发访问？搭建这样一个搜索引擎是一笔不小的投资呀！在 "&lt;a href="http://www.acmqueue.com/modules.php?name=Content&amp;pa=showpage&amp;amp;pid=144"&gt; Building &lt;a class="keyword" href="http://www.kehui.net/index.php?op=article&amp;action=keyword&amp;amp;keyword=Nutch"&gt;Nutch&lt;/a&gt;: Open Source Search&lt;/a&gt;," 的作者 Mike Cafarella 和 Doug Cutting 总结如下：:&lt;br /&gt;... 一个具有完全功能的搜索系统：1亿页面索引量，每秒2个并发索引，需要每月800美元。10亿页面索引量，每秒50个页面请求，大概需要每月30000美元。&lt;br /&gt;这篇文章将为你演示如何在中等级别的网站上搭建&lt;a class="keyword" href="http://www.kehui.net/index.php?op=article&amp;action=keyword&amp;amp;keyword=Nutch"&gt;Nutch&lt;/a&gt;。第一部分集中在抓取上。&lt;a class="keyword" href="http://www.kehui.net/index.php?op=article&amp;action=keyword&amp;amp;keyword=Nutch"&gt;Nutch&lt;/a&gt;的抓取架构，如何运行一个抓取程序，理解这个抓取过程产生了什么。第二部分关注搜索。演示如何运行&lt;a class="keyword" href="http://www.kehui.net/index.php?op=article&amp;action=keyword&amp;amp;keyword=Nutch"&gt;Nutch&lt;/a&gt;搜索程序。以及如何订制&lt;a class="keyword" href="http://www.kehui.net/index.php?op=article&amp;action=keyword&amp;amp;keyword=Nutch"&gt;Nutch&lt;/a&gt; 。&lt;br /&gt;&lt;a class="keyword" href="http://www.kehui.net/index.php?op=article&amp;action=keyword&amp;amp;keyword=Nutch"&gt;Nutch&lt;/a&gt; Vs. Lucene&lt;br /&gt;&lt;a name="architecture"&gt;&lt;a class="keyword" href="http://www.kehui.net/index.php?op=article&amp;action=keyword&amp;amp;keyword=Nutch"&gt;Nutch&lt;/a&gt; 是基于 Lucene的。Lucene为 &lt;a class="keyword" href="http://www.kehui.net/index.php?op=article&amp;action=keyword&amp;amp;keyword=Nutch"&gt;Nutch&lt;/a&gt; 提供了文本索引和搜索的API。一个常见的问题是；我应该使用Lucene还是&lt;a class="keyword" href="http://www.kehui.net/index.php?op=article&amp;action=keyword&amp;amp;keyword=Nutch"&gt;Nutch&lt;/a&gt;？最简单的回答是：如果你不需要抓取数据的话，应该使用Lucene。常见的应用场合是：你有数据源，需要为这些数据提供一个搜索页面。在这种情况下，最好的方式是直接从数据库中取出数据并用Lucene API建立索引。中文用户，可以参考 WebLucene 或者 车东 的一些列文章。如果需要中文分词帮助还可以联系作者。 &lt;/a&gt;&lt;a href="http://domolo.oicp.net/bbs/list.asp?boardid=24"&gt;http://domolo.oicp.net/bbs/list.asp?boardid=24&lt;/a&gt; Erik Hatcher 和 Otis Gospodneti?'s 的 &lt;a href="http://www.manning.com/books/hatcher2"&gt;Lucene in Action&lt;/a&gt; 中详细讲述了这个过程。&lt;a class="keyword" href="http://www.kehui.net/index.php?op=article&amp;action=keyword&amp;amp;keyword=Nutch"&gt;Nutch&lt;/a&gt; 适用于你无法直接获取数据库中的网站，或者比较分散的数据源的情况下使用。&lt;br /&gt;架构&lt;br /&gt;总体上&lt;a class="keyword" href="http://www.kehui.net/index.php?op=article&amp;action=keyword&amp;amp;keyword=Nutch"&gt;Nutch&lt;/a&gt;可以分为2个部分：抓取部分和搜索部分。抓取程序抓取页面并把抓取回来的数据做成反向索引，搜索程序则对反向索引搜索回答用户的请求。抓取程序和搜索程序的接口是索引。两者都使用索引中的字段。（）&lt;br /&gt;实际上搜索程序和抓取程序可以分别位于不同的机器上。（）&lt;br /&gt;这里我们先看看&lt;a class="keyword" href="http://www.kehui.net/index.php?op=article&amp;action=keyword&amp;amp;keyword=Nutch"&gt;Nutch&lt;/a&gt;的抓取部分。&lt;br /&gt;抓取程序：&lt;br /&gt;抓取程序是被&lt;a class="keyword" href="http://www.kehui.net/index.php?op=article&amp;action=keyword&amp;amp;keyword=Nutch"&gt;Nutch&lt;/a&gt;的抓取工具驱动的。这是一组工具，用来建立和维护几个不同的数据结构： web database, a set of segments, and the index。下面我们逐个解释上面提到的3个不同的数据结构。&lt;br /&gt;The web database, 或者WebDB, 是一个特殊存储数据结构，用来映像被抓取网站数据的结构和属性的集合。WebDB 用来存储从抓取开始（包括重新抓取）的所有网站结构数据和属性。WebDB 只是被 抓取程序使用，搜索程序并不使用它。WebDB 存储2种实体：页面 和 链接。页面 表示 网络上的一个网页，这个网页的Url作为标示被索引，同时建立一个对网页内容的MD5 哈希签名。跟网页相关的其它内容也被存储，包括：页面中的链接数量（外链接），页面抓取信息（在页面被重复抓取的情况下），还有表示页面级别的分数 score 。链接 表示从一个网页的链接到其它网页的链接。因此 WebDB 可以说是一个网络图，节点是页面，链接是边。&lt;br /&gt;Segment 是 网页 的集合，并且它被索引。 Segment 的 Fetchlist 是抓取程序使用的 url 列表 ， 它是从 WebDB中生成的。Fetcher 的输出数据是从 fetchlist 中抓取的网页。Fetcher 的输出数据先被反向索引，然后索引后的结果被存储在segment 中。 Segment 的生命周期是有限制的，当下一轮抓取开始后它就没有用了。默认的 重新抓取间隔是30天。因此删除超过这个时间期限的segment是可以的。而且也可以节省不少磁盘空间。Segment 的命名是 日期加时间 ，因此很直观的可以看出他们的存活周期。&lt;br /&gt;索引库 是 反向索引所有系统中被抓取的页面，他并不直接从页面反向索引产生，它是合并很多小的 segment 的索引中产生的。&lt;a class="keyword" href="http://www.kehui.net/index.php?op=article&amp;action=keyword&amp;amp;keyword=Nutch"&gt;Nutch&lt;/a&gt; 使用 Lucene 来建立索引，因此所有 Lucene 相关的工具 API 都用来建立索引库。需要说明的是 Lucene 的 segment 的概念 和 &lt;a class="keyword" href="http://www.kehui.net/index.php?op=article&amp;action=keyword&amp;amp;keyword=Nutch"&gt;Nutch&lt;/a&gt; 的 segment 概念是完全不同的，不要混淆哦。 可以参考 车东 的相关文章。 &lt;a href="http://www.chedong.com/"&gt;http://www.chedong.com/&lt;/a&gt; 简单来说 Lucene 的 segment 是 Lucene 索引库的一部分，而 &lt;a class="keyword" href="http://www.kehui.net/index.php?op=article&amp;action=keyword&amp;amp;keyword=Nutch"&gt;Nutch&lt;/a&gt; 的 Segment 是 WebDB 中 被 抓取和索引的一部分。&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5349536066535334815-6948371132839252170?l=yourmagicsearchengine.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://yourmagicsearchengine.blogspot.com/feeds/6948371132839252170/comments/default' title='帖子评论'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=5349536066535334815&amp;postID=6948371132839252170' title='0 条评论'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5349536066535334815/posts/default/6948371132839252170'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5349536066535334815/posts/default/6948371132839252170'/><link rel='alternate' type='text/html' href='http://yourmagicsearchengine.blogspot.com/2007/02/nutchlucenecrawler.html' title='Nutch=lucene+Crawler(转载)'/><author><name>Yichuan Cai's home page</name><uri>http://www.blogger.com/profile/10921376371265973337</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5349536066535334815.post-1434948874353592633</id><published>2007-02-25T05:33:00.000-08:00</published><updated>2007-02-25T07:58:42.832-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='java 技术'/><title type='text'>Java I/O 学习笔记4(利用Socket进行Java网络编程) ZZ</title><content type='html'>出自：天极网 郗旻&lt;br /&gt;Socket是网络上运行的两个程序间双向通讯的一端，它既可以接受请求，也可以发送请求，利用它可以较为方便的编写网络上数据的传递。在Java中，有专门的Socket类来处理用户的请求和响应。利用Socket类的方法，就可以实现两台计算机之间的通讯。这里就介绍一下在Java中如何利用Socket进行网络编程。 　　&lt;br /&gt;在Java中Socket可以理解为客户端或者服务器端的一个特殊的对象，这个对象有两个关键的方法，一个是getInputStream方法，另一个是getOutputStream方法。getInputStream方法可以得到一个输入流，客户端的Socket对象上的getInputStream方法得到的输入流其实就是从服务器端发回的数据流。GetOutputStream方法得到一个输出流，客户端Socket对象上的getOutputStream方法返回的输出流就是将要发送到服务器端的数据流，（其实是一个缓冲区，暂时存储将要发送过去的数据）。　　&lt;br /&gt;程序可以对这些数据流根据需要进行进一步的封装。本文的例子就对这些数据流进行了一定的封装（关于封装可以参考Java中流的实现部分）。　　&lt;br /&gt;为了更好的说明问题，这里举了一个网上对话的例子，客户端启动以后，服务器会启动一个线程来与客户进行文字交流。　　&lt;br /&gt;要完成这个工作，需要完成三个部分的工作，以下依次说明：　　&lt;br /&gt;一、建立服务器类　　Java中有一个专门用来建立Socket服务器的类，名叫ServerSocket,可以用服务器需要使用的端口号作为参数来创建服务器对象。&lt;br /&gt;ServerSocket server = new ServerSocket(9998) 　　这条语句创建了一个服务器对象，这个服务器使用9998号端口。当一个客户端程序建立一个Socket连接，所连接的端口号为9998时，服务器对象server便响应这个连接，并且server.accept()方法会创建一个Socket对象。服务器端便可以利用这个Socket对象与客户进行通讯。Socket incoming = server.accept() 　　&lt;br /&gt;进而得到输入流和输出流,并进行封装BufferedReader in = new BufferedReader(new InputStreamReader(incoming.getInputStream()));PrintWriter out = new PrintWriter(incoming.getOutputStream(),true); 　　&lt;br /&gt;随后，就可以使用in.readLine()方法得到客户端的输入，也可以使用out.println()方法向客户端发送数据。从而可以根据程序的需要对客户端的不同请求进行回应。　　&lt;br /&gt;在所有通讯结束以后应该关闭这两个数据流，关闭的顺序是先关闭输出流，再关闭输入流，即使用 out.close();in.close();&lt;br /&gt;二、建立客户端代码　　&lt;br /&gt;相比服务器端，客户端要简单一些，客户端只需用服务器所在机器的ip以及服务器的端口作为参数创建一个Socket对象。得到这个对象后，就可以用"建立服务器"部分介绍的方法实现数据的输入和输出。Socket socket = new Socket("168.160.12.42",9998);in = new BufferedReader(new InputStreamReader(socket.getInputStream()));out = new PrintWriter(socket.getOutputStream(),true); 　　以上的程序代码建立了一个Socket对象，这个对象连接到ip地址为168.160.12.42的主机上、端口为9998的服务器对象。并且建立了输入流和输出流，分别对应服务器的输出和客户端的写入。　　&lt;br /&gt;三、建立用户界面　　&lt;br /&gt;读者可以根据自己的喜好建立自己的用户界面，这不是本文的重点。　　经过以上三个步骤，就可以建立一个比较简单的对话程序。但是，为了使这个程序更加完善，应进行以下几个改进：　　一、现在服务器只能服务一个客户，也就是单线程的。可以将它改进为多线程服务器。try{ file://建立服务器　ServerSocket server = new ServerSocket(9998);　int i=1;　for(;;)　{　　Socket incoming = server.accept();　　new ServerThread(incoming,i).start();　　i++;　}}catch (IOException ex){ ex.printStackTrace(); } 　　循环检测是否有客户连接到服务器上，如果有，则创建一个线程来服务这个客户，这个线程的名称是ServerThread，这个类扩展了Thread类，它的编写方法与前述的服务器的写法相同。　&lt;br /&gt;　二、为了可以随时得到对方传送过来的消息，可以在服务器以及客户端各建立一个独立的线程来察看输入流，如果输入流中有输入，则可以即时显示出来。代码如下：new Thread(){　public void run()　{　　try　　{　　　　while(true)　　　{　　　　checkInput();　　　　sleep(1000);//每1000毫秒检测一次　　　}　　}catch (InterruptedException ex)　{　}catch(IOException ex)　{　　}　}}.start();其中的checkInput()方法为private void checkInput() throws IOException{　String line;　if((line=in.readLine())!=null) file://检测输入流中是否有新的数据　　t.setPartner(line); file://将数据流中的消息显示出来}&lt;br /&gt;&lt;br /&gt;附：服务器的实现代码:&lt;br /&gt;import java.net.*;import java.io.*;import java.awt.event.ActionEvent;import java.awt.event.ActionListener;&lt;br /&gt;public class talkServer{ public static void main(String[] args)　{ try　　{ file://建立服务器　　　ServerSocket server = new ServerSocket(9998);　　　int i=1;　　　for(;;)　　　　{ Socket incoming = server.accept();　　　　　new ServerThread(incoming,i).start();　　　　　i++;　　　　}　　　}catch (IOException ex){　　　ex.printStackTrace();　　}　}}&lt;br /&gt;class ServerThread extends Thread implements ActionListener{　private int threadNum;　private Socket socket;　talkServerFrm t;　BufferedReader in;　PrintWriter out;　private boolean talking=true;　public ServerThread(Socket s,int c)　{ threadNum = c;　　socket = s;　}&lt;br /&gt;public void actionPerformed(ActionEvent e){ Object source = e.getSource();　try{　　if(source==t.btnSend)　　　{ out.println(t.getTalk());　　　　t.clearTalk();　　}else　　if(source==t.btnEnd)　　　{ out.println("谈话过程被对方终止");　　　　out.close();　　　　in.close();　　　　talking = false;　　　}　}catch(IOException ex){　}}&lt;br /&gt;public void run(){ try{　　t=new talkServerFrm(new Integer(threadNum).toString(),this);　　t.setSize(500,500);　　t.show();　　in = new BufferedReader(new 　　　　　 InputStreamReader(socket.getInputStream()));　　out = new PrintWriter(socket.getOutputStream(),true);　}catch(Exception e){}　new Thread()　{ public void run()　　{ try{　　　　while(true)　　　　{ checkInput();　　　　　sleep(1000);　　}　}catch (InterruptedException ex){　}catch(IOException ex){　}　}　}.start();　while(talking)　{ }　t.dispose();　}&lt;br /&gt;private void checkInput() throws IOException{ String line;　if((line=in.readLine())!=null)　　t.setPartner(line); file://这是界面类里的方法，　　file://用来将line的内容输出到用户界面　}}&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5349536066535334815-1434948874353592633?l=yourmagicsearchengine.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://yourmagicsearchengine.blogspot.com/feeds/1434948874353592633/comments/default' title='帖子评论'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=5349536066535334815&amp;postID=1434948874353592633' title='0 条评论'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5349536066535334815/posts/default/1434948874353592633'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5349536066535334815/posts/default/1434948874353592633'/><link rel='alternate' type='text/html' href='http://yourmagicsearchengine.blogspot.com/2007/02/java-io-4socketjava-zz.html' title='Java I/O 学习笔记4(利用Socket进行Java网络编程) ZZ'/><author><name>Yichuan Cai's home page</name><uri>http://www.blogger.com/profile/10921376371265973337</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5349536066535334815.post-2825039730068810239</id><published>2007-02-25T05:28:00.000-08:00</published><updated>2007-02-25T05:29:27.749-08:00</updated><title type='text'>java I/O 学习笔记3 转载</title><content type='html'>摘自&lt;a href="http://blog.csdn.net/csusuntao/archive/2005/04/04/336354.aspx"&gt;http://blog.csdn.net/csusuntao/archive/2005/04/04/336354.aspx&lt;/a&gt;&lt;br /&gt;低级流类用于与磁盘文件的通信，高级流类用于组织那些通过低级流移动的信息，高级流类也适用于组织发送到网络或者从网络接受的信息。&lt;br /&gt;抽象上级流类：(都继承于类Object)&lt;br /&gt;            InputStream-------OutputStream.：所有面向字节的输入和输出流的父类(自己本身是抽象类）&lt;br /&gt;            Reader------Writer：所有面向字符的输入和输出流得的父类(自己本身是抽象类）&lt;br /&gt;低级流类：&lt;br /&gt;   ByteArrayInputStream  (extends  InputStream)：   从字节数组或字节数组中的一部分读取输入。&lt;br /&gt;   ByteArrayOutputStream (extends OutputStream)  ：向字节数组写入。&lt;br /&gt;            附：有几种方法可以将字节数组输出流变成可以访问的数据。&lt;br /&gt;                一：  String   toString()  返回迄今为止写入流的所有字节构成的字符串。&lt;br /&gt;                二： byte[] toByteArray()  返回包涵迄今为止写入流的所有字节的数组。这个数组是流内容的备份，可以修改而不会影响原始数据。&lt;br /&gt;高级流类：   高级输入流从其他输入流读取输入，而高级输出流从其他输出流写入输出。&lt;br /&gt;    一： DataInputStream (extends InputStream) ：&lt;br /&gt;          DataInputStream类从另一流读取字节并将其翻译成Java原型，字符数组和字符串。没有面向字符的对应读取器类。&lt;br /&gt;              boolean  readBoolean();   byte  readByte();  char  readChar();    int  readInt();&lt;br /&gt;             long  readLong();    floag  readFloat();    double  readDouble();   String readUTF();&lt;br /&gt;             Static  String  readUTF(DataInput  din)  静态方法。从指定的输入流读取UTF字符串。&lt;br /&gt;     二：DataOutputStream (extends OutputStream) ：&lt;br /&gt;             DataOutputStream类支持将Java的原型数据写入输出流。也可以写入字符串和字节数组。没有相应的面向字符的写入器类。&lt;br /&gt;        void writeBoolean(boolean b); &lt;br /&gt;        void  writeByte(int i)  写入i的低位字节。&lt;br /&gt;        void  writeShort(int i)  写入i的低两位字节。&lt;br /&gt;        void  write int(int i)  写入i的所有字节&lt;br /&gt;       void  writeBytes(String s)   将S写成一系列字节，只写入每个双字节Unicode代码的低位字节。&lt;br /&gt;        void  writeChars(String s);   将S  写成一系列Unicode代码。每个Unicode代码写成两个字节，从高位字节开始。&lt;br /&gt;        void writeUTF(String s)  将S写成UTF字符串。&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5349536066535334815-2825039730068810239?l=yourmagicsearchengine.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://yourmagicsearchengine.blogspot.com/feeds/2825039730068810239/comments/default' title='帖子评论'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=5349536066535334815&amp;postID=2825039730068810239' title='0 条评论'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5349536066535334815/posts/default/2825039730068810239'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5349536066535334815/posts/default/2825039730068810239'/><link rel='alternate' type='text/html' href='http://yourmagicsearchengine.blogspot.com/2007/02/java-io-3.html' title='java I/O 学习笔记3 转载'/><author><name>Yichuan Cai's home page</name><uri>http://www.blogger.com/profile/10921376371265973337</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5349536066535334815.post-2612378476429285633</id><published>2007-02-25T05:23:00.000-08:00</published><updated>2007-02-25T05:24:36.697-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='java 技术'/><title type='text'>java I/O 学习笔记2</title><content type='html'>Java 输入/输出（I/O）机制提供了一套简单的，标准化的API以便从不同的数据源读取和写入字符和字节数据。在“面向对象编程：Java collection更有效管理elements”一文中，我们讨论了Java 集合类架构中的类和功能并介绍了它的排序功能。在本文中，我们将学习Java 平台提供的这些I/O类，接口和操作。让我们先从了解Java 数据流开始。&lt;br /&gt;数据流 Java所有的I/O机制都是基于数据流的，这些数据流表示了字符或者字节数据的流动序列。Java的I/O流提供了读写数据的标准方法。任何Java中表示数据源的对象都会提供以数据流的方式读写它的数据的方法。&lt;br /&gt;Java.io是大多数面向数据流的输入/输出类的主要软件包。这个软件包包含了两个抽象类，InputStream和OutputStream。所有其它面象数据流的输入/输出类都要扩展这两个基类。&lt;br /&gt;java.io软件包提供了一些类和接口，它们在由InputStream和OuputStream类提供的读写操作的顶端定义了一些有用的抽象。例如，ObjectInputStream类提供了让你把输入/输出流中的数据当成对象来读取的方法，而ObjectOutputStream类提供了让你能够把Java对象写入数据流中的方法。&lt;br /&gt;优化读写过程JDK 1.1 增加了一套读写类，它们提供了比现有数据流类更有用的抽象和更好的输入/输出性能。例如，BufferedReader和BufferedWriter 类被用来从基于字符的输入和输出流中读取和写入文本。BufferdReader 类缓存字符以更高效的读取字符串，数组和文本行。BufferedWriter类缓存字符以更高效的写入字符串，数组和文本行。BufferedReader和BufferedWriter 类可以按需求进行设置。&lt;br /&gt;Java输入/输出架构提供的读取器和写入器类包括 LineNumberReader 类，CharArrayReader类，FileReader类，FilterReader类，PushbackReader类，PipedReader类，StringReader类以及其它一些类。这些类是在InputStream和OuputStream类顶部的包裹类因此提供了与InputStream和OuputStream类相似的方法。但是，这些类为读写特定的对象，比方文件，字符数组和字符串等等提供了更高效而有用的抽象。&lt;br /&gt;读取数据当你从一个相应的数据源对象里提取输入流或者是创建一个读取器对象的时候就会自动打开一个输入流。例如，要为一个文件打开输入流，我们只需要以下面的方式把文件名传递给Java.io.FileReader对象的构造函数：&lt;br /&gt;java.io.FileReader fileReader = new java.io.FileReader("/home/me/myfile.txt");&lt;br /&gt;要按顺序读取FileReader底层的输入流中的一个字节数据，只需要使用不带参数的read方法。表A中的代码段从一个文件读取文本数据，一次一个字符，然后把它写入System.out里。&lt;br /&gt;要从输入流读取指定数目的字节数据到char数组里，只需要使用带一个char[]参数的read方法。数组的长度被用来确定应该读取的字符的个数。表B演示了这个技术。&lt;br /&gt;要关闭一个输入流以及这个流使用的所有系统资源，你只需要以下面的方式调用close方法：&lt;br /&gt;fileReader.close();&lt;br /&gt;写入数据象一个输入流一样，输出流通常在你从相应的数据源提取它或者是在你创建一个写入对象的时候被自动的打开。例如，要为一个文件打开输出流，我们把文件的名字传递给java.io.FileWriter对象的构造函数，如下所示：&lt;br /&gt;java.io.FileWriter fileWriter = new java.io.FileWriter("/home/me/out.txt");&lt;br /&gt;要将一个特定的字符写入到输出流中，可以使用带一个int参数的write方法，int参数代表要定入的字符。&lt;br /&gt;int aChar = (int)'X'; fileWriter.write(aChar);&lt;br /&gt;要在输出流给定的偏移地址写入一个char数组中特定数目的字符，你可以使用带一个char[]参数，一个int 偏移量参数和一个int长度参数的write方法，如下面的例子所示：&lt;br /&gt;fileWriter.write(buffer, 0, byteCount);&lt;br /&gt;要关闭一个输出流并释放所有与之相关的系统资源，可以使用close方法，就象这样：&lt;br /&gt;fileWriter.close();&lt;br /&gt;要强迫写出一个输出流中的所有数据，可以使用下面的flush方法：&lt;br /&gt;fileWriter.flush();&lt;br /&gt;把它们全部综合起来我们可以使用我们学习过的这些函数从一个文件中读取数据并同时写到另一个文件中去，如表C所示。&lt;br /&gt;总结Java的输入/输出机制为从不同的数据源读取和写入字符增加了一套简单而标准化的API。你对一种数据源使用Java流的经验能够让你容易的使用其它由Java提供的数据源类型。&lt;br /&gt;在我们下一篇文章中，我们将会开始学习Java平台的联网和远程通讯架构。我们将会把我们对Java流的讨论扩展到这些环境并演示如何打开远程数据源，并象操作本地数据源，比方文件一样，写入数据和读取数据&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5349536066535334815-2612378476429285633?l=yourmagicsearchengine.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://yourmagicsearchengine.blogspot.com/feeds/2612378476429285633/comments/default' title='帖子评论'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=5349536066535334815&amp;postID=2612378476429285633' title='0 条评论'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5349536066535334815/posts/default/2612378476429285633'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5349536066535334815/posts/default/2612378476429285633'/><link rel='alternate' type='text/html' href='http://yourmagicsearchengine.blogspot.com/2007/02/java-io-2.html' title='java I/O 学习笔记2'/><author><name>Yichuan Cai's home page</name><uri>http://www.blogger.com/profile/10921376371265973337</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5349536066535334815.post-227739137117636133</id><published>2007-02-25T05:07:00.000-08:00</published><updated>2007-02-25T05:10:56.078-08:00</updated><title type='text'>Java IO学习笔记（１） 转载</title><content type='html'>最近在看lucene的原代码,里面有好多Java 的I/O操作,现在顺带复习一下,网上找到一个东东很好,&lt;br /&gt;于是转载过来&lt;a href="http://blog.csdn.net/yangxp_82/archive/2005/09/13/479306.aspx"&gt;http://blog.csdn.net/yangxp_82/archive/2005/09/13/479306.aspx&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;java IO最关键的四个父类：InputStream、OutputStream、Reader、Writer。它们都是public abstract class。&lt;br /&gt;InputSream和OutputStream对于数据的传送是以“byte”为单位的，而Reader和Writer对于数据的传送是以“character”为单位的。所以我们看到java.io包中的类大体上可以分为两大类，一类是以byte处理为主的Stream类，它们都是以XXXStream方式命名的；一类是以character处理为主的Reader/Writer类，它们都是以XXXReader或XXXWriter的方式命名的。&lt;br /&gt;InputStream类方法简介：&lt;br /&gt;InputStream是输入字节数据用的类，所以InputStream类提供了3种重载的read方法（具体方法参见API）。&lt;br /&gt;如果我们要知道这个流中还有多少个byte的数据可以读取时可以调用available方法，注意的是如果InputStream对象调用这个方法的话，它只会返回0，这个方法必须由继承InputStream类的子类对象调用才有用，另外它会抛出IOException异常，必须对此异常进行处理。&lt;br /&gt;如果我们想跳过一些字节来读取的话可以调用skip方法来跳过一些字节来读取。skip方法所需要的参数是long型的，指出要跳过的字节数。&lt;br /&gt;流的处理是单向的，如果想重新读取已经读过的数据，我们必须进行一些处理。有些InputStream是不支持重新读取的（也就是不允许重新定位读取指针），这样我们在进行重新读取前最好先调用markSupported方法来检查一下这个InputStream对象是否支持重定位指针，如果通过，那么我们就可以先使用mark方法来指定指针的位置，然后调用reset方法来让指针指到你要求得位置。（注意的是reset方法可能抛出IOException异常）。&lt;br /&gt;还有就是我们在使用完后，必须对我们打开的流进行关闭（close）。&lt;br /&gt;OutputStream方法的介绍：&lt;br /&gt;OutputStream提供了3个write方法来做数据的输出，这个是和InputStream是相对应的。&lt;br /&gt;还有一个常用的清除缓冲区的方法就是flush。&lt;br /&gt;最好就是close方法&lt;br /&gt;Reader方法的介绍&lt;br /&gt;Reader是输入字符数据用的，它所提供的方法跟InputStream基本一样。不过Reader类没有提供available方法，取而代之的是ready方法。&lt;br /&gt;Writer方法的介绍&lt;br /&gt;Writer类有5个write方法，具体可以查看API。&lt;br /&gt;学习完上面的四个最最基本的四个抽象类之后，我们就来学习Java的节点类，这个对于理解使用IO来说是至关重要的，下面就用一个表格来简单说明，若要详细了解请查看API。&lt;br /&gt;文件（File）&lt;br /&gt;FileInputStream&lt;br /&gt;FileOutputStream&lt;br /&gt;FileReader&lt;br /&gt;FileWriter&lt;br /&gt;&lt;br /&gt;内存（数组）&lt;br /&gt;ByteArrayInputStream&lt;br /&gt;ByteArrayOutputStream&lt;br /&gt;CharArrayReader&lt;br /&gt;CharArrayWriter&lt;br /&gt;&lt;br /&gt;内存（字符串）&lt;br /&gt;&lt;br /&gt;StringReader&lt;br /&gt;StringWriter&lt;br /&gt;&lt;br /&gt;管道（Pipe）&lt;br /&gt;PipeInputStream&lt;br /&gt;PipeOutputStream&lt;br /&gt;PipeReader&lt;br /&gt;PipeWriter&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;下面就是一些必须学习的类（java.io.File）&lt;br /&gt;这个类对文件的操作是至关重要的，对于它的学习必须深入，它不是一仅仅是我们概念中的文件，而且包括目录。详细细节请查看API。&lt;br /&gt;RandomAccessFile对于IO系统来说也是很重要的，它与我们前面介绍的四个抽象类没有关系，它是直接继承自Object类，用于对文件进行随机读取操作。&lt;br /&gt;下面给出一个文件操作的例子：&lt;br /&gt;/**&lt;br /&gt;* 学生类&lt;br /&gt;* @author Sunny&lt;br /&gt;* @version 1.0&lt;br /&gt;*/&lt;br /&gt;import java.io.*;&lt;br /&gt;public class Student implements Serializable{&lt;br /&gt;private String name;&lt;br /&gt;private String id;&lt;br /&gt;private int age;&lt;br /&gt;&lt;br /&gt;/**&lt;br /&gt;* 构造函数&lt;br /&gt;* @param name&lt;br /&gt;* @param id&lt;br /&gt;* @param age&lt;br /&gt;*/&lt;br /&gt;public Student(String name, String id, int age) {&lt;br /&gt;setName(name);&lt;br /&gt;setId(id);&lt;br /&gt;setAge(age);&lt;br /&gt;}&lt;br /&gt;/**&lt;br /&gt;*&lt;br /&gt;* @return int&lt;br /&gt;*/&lt;br /&gt;public int getAge() {&lt;br /&gt;return age;&lt;br /&gt;}&lt;br /&gt;/**&lt;br /&gt;*&lt;br /&gt;* @param age&lt;br /&gt;*/&lt;br /&gt;public void setAge(int age) {&lt;br /&gt;if(age&gt;0 &amp;&amp;amp; age &lt;100)&lt;br /&gt;this.age = age;&lt;br /&gt;else&lt;br /&gt;this.age = 10;&lt;br /&gt;}&lt;br /&gt;/**&lt;br /&gt;*&lt;br /&gt;* @return String&lt;br /&gt;*/&lt;br /&gt;public String getId() {&lt;br /&gt;return id;&lt;br /&gt;}&lt;br /&gt;/**&lt;br /&gt;*&lt;br /&gt;* @param id&lt;br /&gt;*/&lt;br /&gt;public void setId(String id) {&lt;br /&gt;this.id = id;&lt;br /&gt;}&lt;br /&gt;/**&lt;br /&gt;*&lt;br /&gt;* @return String&lt;br /&gt;*/&lt;br /&gt;public String getName() {&lt;br /&gt;return name;&lt;br /&gt;}&lt;br /&gt;/**&lt;br /&gt;*&lt;br /&gt;* @param name&lt;br /&gt;*/&lt;br /&gt;public void setName(String name) {&lt;br /&gt;this.name = name;&lt;br /&gt;}&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;/**&lt;br /&gt;* 随即存取Student类&lt;br /&gt;* @author Sunny&lt;br /&gt;* @version 1.0&lt;br /&gt;*/&lt;br /&gt;import java.io.RandomAccessFile;&lt;br /&gt;import java.io.IOException;&lt;br /&gt;public final class RandomAccessStudent extends Student {&lt;br /&gt;//name域所占的字节数&lt;br /&gt;private static final int NAME_SIZE = 30;&lt;br /&gt;//id域所占的字节数&lt;br /&gt;private static final int ID_SIZE = 30;&lt;br /&gt;//该对象所占的字节数&lt;br /&gt;public static final int SISE = NAME_SIZE + ID_SIZE + 4;&lt;br /&gt;/**&lt;br /&gt;* 构造函数&lt;br /&gt;* @param name&lt;br /&gt;* @param id&lt;br /&gt;* @param age&lt;br /&gt;*/&lt;br /&gt;public RandomAccessStudent(String name, String id, int age) {&lt;br /&gt;super(name, id, age);&lt;br /&gt;}&lt;br /&gt;/**&lt;br /&gt;* 从随即文件读取一个Student类&lt;br /&gt;* @param file&lt;br /&gt;* @throws IOException&lt;br /&gt;*/&lt;br /&gt;public void read(RandomAccessFile file) throws IOException {&lt;br /&gt;setName(readName(file));&lt;br /&gt;setId(readId(file));&lt;br /&gt;setAge(file.readInt());&lt;br /&gt;}&lt;br /&gt;//从随即文件中读取学生姓名&lt;br /&gt;private String readName(RandomAccessFile file) throws IOException {&lt;br /&gt;return readString(file, NAME_SIZE);&lt;br /&gt;}&lt;br /&gt;//从随即文件中读取学生ID&lt;br /&gt;private String readId(RandomAccessFile file) throws IOException {&lt;br /&gt;return readString(file, ID_SIZE);&lt;br /&gt;}&lt;br /&gt;//从随即文件中读取size字节长度的字符串&lt;br /&gt;private String readString(RandomAccessFile file, int size) throws IOException {&lt;br /&gt;char [] temp = new char[15];&lt;br /&gt;for(int i = 0; i &lt;temp.length;&gt;temp[i] = file.readChar();&lt;br /&gt;&lt;br /&gt;return new String(temp).replace('\0', ' ');&lt;br /&gt;}&lt;br /&gt;/**&lt;br /&gt;* 把对象写入随即文件&lt;br /&gt;* @param file&lt;br /&gt;* @throws IOException&lt;br /&gt;*/&lt;br /&gt;public void write(RandomAccessFile file) throws IOException {&lt;br /&gt;writeName(file);&lt;br /&gt;writeId(file);&lt;br /&gt;file.writeInt(getAge());&lt;br /&gt;}&lt;br /&gt;//把name写入随即文件&lt;br /&gt;private void writeName(RandomAccessFile file) throws IOException {&lt;br /&gt;writeString(file, getName());&lt;br /&gt;}&lt;br /&gt;//把id写入随即文件&lt;br /&gt;private void writeId(RandomAccessFile file) throws IOException {&lt;br /&gt;writeString(file, getId());&lt;br /&gt;}&lt;br /&gt;//把给定的字符串写入随即文件&lt;br /&gt;private void writeString(RandomAccessFile file, String content) throws IOException {&lt;br /&gt;StringBuffer buffer = null;&lt;br /&gt;&lt;br /&gt;if(content != null)&lt;br /&gt;buffer = new StringBuffer(content);&lt;br /&gt;else&lt;br /&gt;buffer = new StringBuffer(15);&lt;br /&gt;&lt;br /&gt;buffer.setLength(15);&lt;br /&gt;file.writeChars(buffer.toString());&lt;br /&gt;}&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;/**&lt;br /&gt;* 测试随即文件的读取&lt;br /&gt;* @author Sunny&lt;br /&gt;* @version 1.0&lt;br /&gt;*/&lt;br /&gt;import java.io.*;&lt;br /&gt;public class RandomAccessFileTest {&lt;br /&gt;/**&lt;br /&gt;* @param args&lt;br /&gt;*/&lt;br /&gt;public static void main(String[] args) throws IOException, FileNotFoundException {&lt;br /&gt;File file = new File("random.dat");&lt;br /&gt;if(file.exists())&lt;br /&gt;file.createNewFile();&lt;br /&gt;RandomAccessFile randomAccessFile = new RandomAccessFile(file, "rw");&lt;br /&gt;&lt;br /&gt;RandomAccessStudent yang = new RandomAccessStudent("Sunny", "123456", 23);&lt;br /&gt;RandomAccessStudent jin = new RandomAccessStudent("JJJJJ", "12345678", 24);&lt;br /&gt;&lt;br /&gt;yang.write(randomAccessFile);&lt;br /&gt;jin.write(randomAccessFile);&lt;br /&gt;&lt;br /&gt;randomAccessFile.close();&lt;br /&gt;&lt;br /&gt;RandomAccessFile read = new RandomAccessFile(file, "rw");&lt;br /&gt;&lt;br /&gt;jin.read(read);&lt;br /&gt;System.out.println(jin.getAge());&lt;br /&gt;&lt;br /&gt;read.close();&lt;br /&gt;}&lt;br /&gt;}&lt;br /&gt;上面仅仅是基本的IO简介，更多的知识就是去查看API来完善，还有在JDK1.4中又有java.nio.*这个包，它又是一种新的机制，如果有时间将介绍它的基本机制。&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5349536066535334815-227739137117636133?l=yourmagicsearchengine.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://yourmagicsearchengine.blogspot.com/feeds/227739137117636133/comments/default' title='帖子评论'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=5349536066535334815&amp;postID=227739137117636133' title='0 条评论'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5349536066535334815/posts/default/227739137117636133'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5349536066535334815/posts/default/227739137117636133'/><link rel='alternate' type='text/html' href='http://yourmagicsearchengine.blogspot.com/2007/02/java-io.html' title='Java IO学习笔记（１） 转载'/><author><name>Yichuan Cai's home page</name><uri>http://www.blogger.com/profile/10921376371265973337</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5349536066535334815.post-2204879428452399677</id><published>2007-02-19T00:15:00.001-08:00</published><updated>2007-02-21T05:27:27.826-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='技术资源'/><title type='text'>Lucene Resource</title><content type='html'>在应用中加入全文检索功能——基于Java的全文索引引擎Lucene简介: &lt;a href="http://www.chedong.com/tech/lucene.html" target="_blank"&gt;http://www.chedong.com/tech/lucene.html&lt;/a&gt;&lt;br /&gt;Apache Lucene:&lt;a href="http://lucene.apache.org/java/docs/index.html" target="_blank"&gt;http://lucene.apache.org/java/docs/index.html&lt;/a&gt;&lt;br /&gt;Java开源全文检索:&lt;a href="http://www.open-open.com/32.htm" target="_blank"&gt;http://www.open-open.com/32.htm&lt;/a&gt;&lt;br /&gt;水木BBS上搜索引擎的论坛: &lt;a href="http://www.newsmth.org/bbsdoc.php?board=SearchEngineTech"&gt;http://www.newsmth.org/bbsdoc.php?board=SearchEngineTech&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;最近在看一本书叫做Ajax+Lucene构建搜索引擎,好像写的满好的,可以学到不少东东.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5349536066535334815-2204879428452399677?l=yourmagicsearchengine.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://yourmagicsearchengine.blogspot.com/feeds/2204879428452399677/comments/default' title='帖子评论'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=5349536066535334815&amp;postID=2204879428452399677' title='3 条评论'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5349536066535334815/posts/default/2204879428452399677'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5349536066535334815/posts/default/2204879428452399677'/><link rel='alternate' type='text/html' href='http://yourmagicsearchengine.blogspot.com/2007/02/lucence-resource.html' title='Lucene Resource'/><author><name>Yichuan Cai's home page</name><uri>http://www.blogger.com/profile/10921376371265973337</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>3</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5349536066535334815.post-8055197799564315684</id><published>2007-02-15T06:57:00.000-08:00</published><updated>2007-02-20T01:48:06.994-08:00</updated><title type='text'>新开的博客</title><content type='html'>寒假最近无事,于是开始玩玩Blog,下学期实验室也可能有个Blog 搜索的项目.这是我的有关于专注于CS技术的博客,现在的主题是搜索引擎的设计顺带Java技术的讨论,现在刚刚起步.想在这里和大家一起讨论一下Lucene和Java技术^_^现在刚开张的博客,比较简陋,以后会慢慢填料的.之前用过MSN spaces,感觉限制太多,虽然好用但是看不到一点技术细节,这也符合MS的一贯风格.最近开始喜欢玩玩Linux和Javascript,喜欢DIY的感觉.大家多捧捧场拉,多点击广告和下载FireFox,开了2天,好像帐户上显示有10美分拉-_-&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5349536066535334815-8055197799564315684?l=yourmagicsearchengine.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://yourmagicsearchengine.blogspot.com/feeds/8055197799564315684/comments/default' title='帖子评论'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=5349536066535334815&amp;postID=8055197799564315684' title='1 条评论'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5349536066535334815/posts/default/8055197799564315684'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5349536066535334815/posts/default/8055197799564315684'/><link rel='alternate' type='text/html' href='http://yourmagicsearchengine.blogspot.com/2007/02/blog-post.html' title='新开的博客'/><author><name>Yichuan Cai's home page</name><uri>http://www.blogger.com/profile/10921376371265973337</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>1</thr:total></entry></feed>
