Sunday, April 30, 2006

[Reproduced]A Tao of Regular Expressions(En)

What Are Regular Expressions

A regular expression is a formula for matching strings that follow some pattern. Many people are afraid to use them because they can look confusing and complicated. Unfortunately, nothing in this write up can change that. However, I have found that with a bit of practice, it’s pretty easy to write these complicated expressions. Plus, once you get the hang of them, you can reduce hours of laborious and error-prone text editing down to minutes or seconds. Regular expressions are supported by many text editors, class libraries such as Rogue Wave’s Tools.h++, scripting tools such as awk, grep, sed, and increasingly in interactive development environments such as Microsoft’s Visual C++.

Regular expressions usage is explained by examples in the sections that follow. Most examples are presented as vi substitution commands or as grep file search commands, but they are representative examples and the concepts can be applied in the use of tools such as sed, awk, perl and other programs that support regular expressions. Have a look at Regular Expressions In Various Tools for examples of regular expression usage in other tools. A short explanation of vi’s substitution command and syntax is provided at the end of this document.

Regular Expression Basics

Regular expressions are made up of normal characters and metacharacters. Normal characters include upper and lower case letters and digits. The metacharacters have special meanings and are described in detail below.

In the simplest case, a regular expression looks like a standard search string. For example, the regular expression “testing” contains no metacharacters. It will match “testing” and “123testing” but it will not match “Testing”.

To really make good use of regular expressions it is critical to understand metacharacters. The table below lists metacharacters and a short explanation of their meaning.
 

Metacharacter   Description


.
Matches any single character. For example the regular expression r.t would match the strings rat, rut, r t, but not root
$
Matches the end of a line. For example, the regular expression weasel$ would match the end of the string “He’s a weasel” but not the string “They are a bunch of weasels.“ 
^
Matches the beginning of a line. For example, the regular expression ^When in would match the beginning of the string “When in the course of human events” but would not match “What and When in the”
*
Matches zero or more occurences of the character immediately preceding. For example, the regular expression .* means match any number of any characters. 
\
This is the quoting character, use it to treat the following character as an ordinary character. For example, \$ is used to match the dollar sign character ($) rather than the end of a line. Similarly, the expression \. is used to match the period character rather than any single character. 
[ ] 
[c1-c2]
[^c1-c2]
Matches any one of the characters between the brackets. For example, the regular expression r[aou]t matches rat, rot, and rut, but not ret. Ranges of characters can specified by using a hyphen. For example, the regular expression [0-9] means match any digit. Multiple ranges can be specified as well. The regular expression [A-Za-z] means match any upper or lower case letter. To match any character except those in the range, the complement range, use the caret as the first character after the opening bracket. For example, the expression [^269A-Z] will match any characters except 2, 6, 9, and upper case letters. 
\< \>
Matches the beginning (\<) or end (\>) or a word. For example, \<the matches on “the” in the string “for the wise” but does not match “the” in “otherwise“. NOTE: this metacharacter is not supported by all applications.
\( \)
Treat the expression between \( and \) as a group. Also, saves the characters matched by the expression into temporary holding areas. Up to nine pattern matches can be saved in a single regular expression. They can be referenced as \1 through \9.
|
Or two conditions together. For example (him|her) matches the line “it belongs to him” and matches the line “it belongs to her” but does not match the line “it belongs to them.” NOTE: this metacharacter is not supported by all applications.
+
Matches one or more occurences of the character or regular expression immediately preceding. For example, the regular expression 9+ matches 9, 99, 999. NOTE: this metacharacter is not supported by all applications.
?
Matches 0 or 1 occurence of the character or regular expression immediately preceding.NOTE: this metacharacter is not supported by all applications.
\{i\}
\{i,j\}
Match a specific number of instances or instances within a range of the preceding character. For example, the expression A[0-9]\{3\} will match “A” followed by exactly 3 digits. That is, it will match A123 but not A1234. The expression [0-9]\{4,6\} any sequence of 4, 5, or 6 digits. NOTE: this metacharacter is not supported by all applications.

The simplest metacharacter is the dot. It matches any one character (excluding the newline character). Consider a file named test.txt consisting of the following lines:

he is a rat
he is in a rut
the food is Rotten
I like root beer

We can use grep to test our regular expressions. Grep uses the regular expression we supply and tries to match it to every line of the file. It prints all lines where the regular expression matches at least one sequence of characters on a line. The command

grep r.t test.txt

searches for the regular expression r.t in each line of test.txt and prints the matching lines. The regular expression r.t matches an r followed by any character followed by a t. It will match rat and rut. It does not match the Rot in Rotten because regular expressions are case sensitive. To match both the upper and lower the square brackets (character range metacharacters) can be used. The regular expression [Rr] matches either R or r. So, to match an upper or lower case r followed by any character followed by the character t the regular expression [Rr].t will do the trick.

To match characters at the beginning of a line use the circumflex character (sometimes called a caret). For example, to find the lines containing the word “he” at the beginning of each line in the file test.txt you might first think the use the simple expression he. However, this would match the in the third line. The regular expression ^he only matches the h at the beginning of a line.

Sometimes it is easier to indicate something what should not be matched rather than all the cases that should be matched. When the circumflex is the first character between the square brackets it means to match any character which is not in the range. For example, to match he when it is not preceded by t or s, the following regular expression can be used: [^st]he.

Several character ranges can be specified between the square brackets. For example, the regular expression [A-Za-z] matches any letter in the alphabet, upper or lower case. The regular expression [A-Za-z][A-Za-z]* matches a letter followed by zero or more letters. We can use the + metacharacter to do the same thing. That is, the regular expression [A-Za-z]+ means the same thing as [A-Za-z][A-Za-z]*. Note that the + metacharacter is not supported by all programs that have regular expressions. See Regular Expressions Syntax Support for more details.

To specify the number of occurrences matched, use the braces (they must be escaped with a backslash). As an example, to match all instances of 100 and 1000 but not 10 or 10000 use the following: 10\{2,3\}. This regular expression matches a the digit 1 followed by either 2 or 3 0’s. A useful variation is to omit the second number. For example, the regular expression 0\{3,\} will match 3 or more successive 0’s.

Simple Examples

Here are a few representative, simple examples.
 

vi command What it does


:%s/ */ /g Change 1 or more spaces into a single space.
:%s/ *$// Remove all spaces from the end of the line.
:%s/^/ / Insert a space at the beginning of every line.
:%s/^[0-9][0-9]* // Remove all numbers at the beginning of a line.
:%s/b[aeio]g/bug/g Change all occurences of bag, beg, big, and bog, to bug
:%s/t\([aou]\)g/h\1t/g Change all occurences of tag, tog, and tug to hat, hot, and hug respectively. 

Medium Examples (Strange Incantations)

Example 1

Change all instances of foo(a,b,c) to foo(b,a,c). where a, b, and c can be any parameters supplied to foo(). That is, we must be able to make changes like the following:
 

Before   After
foo(10,7,2) foo(7,10,2)
foo(x+13,y-2,10) foo(y-2,x+13,10)
foo( bar(8), x+y+z, 5) foo( x+y+z, bar(8), 5)

The following substitution command will do the trick :

:%s/foo(\([^,]*\),\([^,]*\),\([^)]*\))/foo(\2,\1,\3)/g

Now, let’s break this apart and analyze what’s happening. The idea behind this expression is to identify invocations of foo() with three parameters between the parentheses. The first parameter is identified by the regular expression \([^,]*\), which we can analyze from the inside out.
 

[^,]   means any character which is not a comma
[^,]* means 0 or more characters which are not commas
\([^,]*\) tags the non-comma characters as \1 for use in the replacement part of the command
\([^,]*\), means that we must match 0 or more non-comma characters which are followed by a comma. The non-comma characters are tagged.

This is a good time to point out one of the most common problems people have with regular expressions. Why would we use an expression like [^,]*, instead of something more straightforward like .*, to match the first parameter?  Consider applying the pattern .*, to the string “10,7,2″.  Should it match “10,” or “10,7,” ?  To resolve this ambiguity, regular expressions will always match the longest string possible. In this case “10,7,” which covers two parameters instead of one parameter like we want. So, by using the expression [^,]*, we force the pattern to match all characters up to the first comma.

The expression up to this point is: foo(\([^,]*\), and can be roughly translated as “after you find foo( tag all characters up to the next comma as \1“. We tag the second parameter just like the first and it can be referenced as \2. The tag used on the third parameter is exactly like the others except that we search for all characters up to the right parenthesis. It may be superfluous to search for the last parameter since we don’t have to move it. But this pattern guarantees that we update only those instances of foo() where 3 parameters are specified. In these times of function and method overloading, being explicit often proves to be useful. In the substitution portion of the command, we explicitly enter the invocation of foo() as we want it, referencing the matched patterns in the new order where the first and second parameter have been switched.

Example 2

We have a CSV (comma separated value) file with information we need, but in the wrong format. The columns of data are currently arranged in the following order: Name, Company Name, State, Postal Code. We need to reorganize the data into the following order in order to use it with a particular piece of software: Name, State-Postal Code, Company Name. This means that we must change the order of the columns in addition to merging two columns to form a new column value. The particular piece of software that needs this data will not work if there are any whitespace characters (spaces or tabs) before or after the commas. So we must remove whitespace around the commas.

Here are a few lines from the data we have:

Bill Jones,     HI-TEK Corporation ,  CA, 95011
Sharon Lee Smith,  Design Works Incorporated,  CA, 95012
B. Amos   ,  Hill Street Cafe,  CA, 95013
Alexander Weatherworth,  The Crafts Store,  CA, 95014

We need to transform them to look like this:

Bill Jones,CA 95011,HI-TEK Corporation
Sharon Lee Smith,CA 95012,Design Works Incorporated
B. Amos,CA 95013,Hill Street Cafe
Alexander Weatherworth,CA 95014,The Crafts Store

We’ll look at two regular expressions to solve this problem. The first moves the columns around and merges the data. The second removes the excess spaces.

Here is the first pass at a substitution command that will solve the problem:

:%s/\([^,]*\),\([^,]*\),\([^,]*\),\(.*\)/\1,\3 \4,\2/

The approach is similar to that of Example 1. The Name is matched by the expression \([^,]*\), that is, all characters up to the first comma. The name can then be referenced as \1 in the replacement pattern. The Company Name and State fields are matched just like the Name field and are referenced as \2 and \3 in the replacement pattern. The last field is matched with the expression \(.*\) which can be translated as “match all characters through the end of the line”. The replacement pattern is constructed by calling out each tagged expression in the appropriate order and adding or not adding the delimeter.

The following substitution command will remove the excess spaces:

:%s/[ \t]*,[ \t]*/,/g

To break it down: [ \t] matches a space or tab character; [ \t]* matches 0 or more spaces or tabs; [ \t]*, matches 0 or more spaces or tabs followed by a comma; and finally [ \t]*,[ \t]* matches 0 or more spaces or tabs followed by a comma followed by 0 or more spaces or tabs. In the replacement pattern, we simply replace whatever we matched with a single comma. The optional g parameter is added to the end of the substitution command to apply the substitution to all commas in the line.

Example 3

Suppose you have a multi-character sequence that repeats.  For example, consider the following:

Billy tried really hard
Sally tried really really hard
Timmy tried really really really hard
Johnny tried really really really really hard

Now suppose you want to change “really”, “really really”,  and any number of consecutive “really” strings to a single word: “very”.  The command

:%s/\(really \)\(really \)*/very /

changes the text above to:

Billy tried very hard
Sally tried very hard
Timmy tried very hard
Johnny tried very hard

The expression \(really \)* matches 0 or more sequences of “really “. The sequence \(really \)\(really \)* matches one or more instances of the sequence “really “.

Hard Examples (Magical Hieroglyphics)

coming soon.


Regular Expressions In Various Tools

OK, you’d like to use regular expressions, but you can’t bring yourself to use vi. Here, then, are a few examples of how to use regular expressions in other tools. Also, I have attempted to summarize the differences in regular expressions you will find between different programs.

You can use regular expressions in the Visual C++ editor. Select Edit->Replace, then be sure to check the checkbox labled “Regular expression”. For vi expressions of the form :%s/pat1/pat2/g set the Find What field to pat1 and the Replace with field to pat2. To simulate the range (% in this case) and the g option you will have to use the Replace All button or appropriate combinations of Find Next and Replace

sed

Sed is a Stream EDitor which can be used to make changes to files or pipes. For complete details, see the man page sed(1).

Here are a few interesting sed scripts. Assume that we’re processing a file called price.txt. Note that the edits don’t actually happen to the input file, sed simply processes each line of the file with the command you supply and echos the result to its standard out.
 

sed script   Description


sed ’s/^$/d’ price.txt removes all empty lines
sed ’s/^[ \t]*$/d’ price.txt removes all lines containing only whitespace
sed ’s/”//g’ price.txt remove all quotation marks

awk

Awk is a programming language which can be used to perform sophisticated analysis and manipulation of text data. For complete details, see the man page awk(1). Its peculiar name is an acronym made up of the first character of its authors last names (Aho, Weinberger, and Kernighan).

There are many good awk examples in the book The AWK Programming Language (written by Aho, Weinberger, and Kernighan). Please don’t form any broad opinions about awk’s capabilities based on the following trivial sample scripts. For purposes of these examples, assume that we’re working with a file called price.txt. As with sed, awk simply echos its output to its standard out.
 

awk script   Description


awk ‘$0 !~ /^$/’ price.txt removes all empty lines
awk ‘NF > 0′ price.txt a better way to remove all lines in awk
awk ‘$2 ~ /^[JT]/ {print $3}’ price.txt print the third field of all lines whose second field begins with ‘J’ or ‘T’
awk ‘$2 !~ /[Mm]isc/ {print $3 + $4}’ price.txt for all lines whose second field does not contain ‘Misc’ or ‘misc’ print the sum of columns 3 and 4 (assumed to be numbers).
awk ‘$3 !~ /^[0-9]+\.[0-9]*$/ {print $0}’ price.txt print all lines where field 3 is not a number. The number must be of the form: d.d or d. where d is any number of digits from 0 to 9.
awk ‘$2 ~ /John|Fred/ {print $0}’ price.txt print the entire line if the second field contains ‘John’ or ‘Fred’

grep

grep is a program used to match regular expressions in one or more specified files or in an input stream. Its name programming language which can be used to perform data manipulation on files or pipes. For complete details, see the man page grep(1). Its peculiar name stems from its roots as a command in vi, g/re/p meaning global regular expression print.

For the examples below, assume we have the text below in a file named phone.txt. Its format is last name followed by a comma, first name followed by a tab, then a phone number.

Francis, John           5-3871
Wong, Fred              4-4123
Jones, Thomas           1-4122
Salazar, Richard        5-2522
grep command   Description


grep ‘\t5-…1′ phone.txt print all the lines in phone.txt where the phone number begins with 5 and ends with 1. Note that the tab character is represented by \t.
grep ‘^S[^ ]* R’ phone.txt print lines where the last name begins with S and first name begins with R.
grep ‘^[JW]‘ phone.txt print lines where the last name begins with J or W
grep ‘, ….\t’ phone.txt print lines where the first name is 4 characters. The tab character is represented by \t.
grep -v ‘^[JW]‘ phone.txt print lines that do not begin with J or W
grep ‘^[M-Z]‘ phone.txt print lines where the last name begins with any letter from M to Z.
grep ‘^[M-Z].*[12]‘ phone.txt print lines where the last name begins with a letter from M to Z and where the phone number ends with a 1 or 2.

egrep

egrep is an extended version of grep. It supports a few more metacharacters in its regular expressions. For the examples below, assume we have the text below in a file named phone.txt. Its format is last name followed by a comma, first name followed by a tab, then a phone number.

Francis, John           5-3871
Wong, Fred              4-4123
Jones, Thomas           1-4122
Salazar, Richard        5-2522
egrep command   Description


egrep ‘(John|Fred)’ phone.txt print all lines that contain the name John or Fred.
egrep ‘John|22$|^W’ phone.txt print lines that contain John or that end with 22 or that begin with W.
egrep ‘net(work)?s’ report.txt print lines in report.txt contain networks or nets.

Regular Expressions Syntax Support

Command or
Environment
. [ ] ^ $ \( \) \{ \} ? + | ( )
vi  X   X   X   X   X 
Visual C++  X   X   X   X   X 
awk  X   X   X   X   X   X   X   X 
sed  X   X   X   X   X   X 
Tcl  X   X   X   X   X   X   X   X   X 
ex  X   X   X   X   X   X 
grep  X   X   X   X   X   X 
egrep  X   X  X   X   X   X   X   X   X 
fgrep  X   X   X   X   X 
perl X X X X X X X X X

The vi Substitution Command

Vi’s substitution command has the form

:ranges/pat1/pat2/g

where

: begins an ex (command line editor) command which is applied to the file currently being edited.
range is the line range specifier. Use the percent sign (%) to indicate all lines. Use the dot (.) to indicate the current line. Use the dollar sign to indicate the last line. You can also use specific line numbers. Examples: 10,20 means lines 10 through 20; .,$ means from the current line to the last line; .+2,$-5 means from two lines after the current through the fifth line up from the end of the file.

s is the substitution command.

pat1 is the regular expression to be searched for. This paper is full of examples.

pat2 is the replacement pattern. This paper is full of examples.

g is optional. When present the substitution is made to all matches on the line. When it is not present, the substitution is applied only to the first match on the line.

There are many online manuals for vi that provide more complete detail. This page has a number of good vi links and information.

Posted by Wyulnnhtg at 17:16:22 | Permalink | No Comments »

[转]正则表达式之道(中)

    译者按:原文因为年代久远,文中很多链接早已过期(主要是关于vi、sed等工具的介绍和手册),本译文中已将此类链接删除,如需检查这些链接可以查看上面链接的原文。除此之外基本照原文直译,括号中有“译者按”的部分是译者补充的说明。如有内容方面的问题请直接和Steve Mansor联系,当然,如果你只写中文,也可以和我联系。

什么是正则表达式

一个正则表达式,就是用某种模式去匹配一类字符串的一个公式。很多人因为它们看上去比较古怪而且复杂所以不敢去使用——很不幸,这篇文章也不能够改变这一点,不过,经过一点点练习之后我就开始觉得这些复杂的表达式其实写起来还是相当简单的,而且,一旦你弄懂它们,你就能把数小时辛苦而且易错的文本处理工作压缩在几分钟(甚至几秒钟)内完成。正则表达式被各种文本编辑软件、类库(例如Rogue Wave的tools.h++)、脚本工具(像awk/grep/sed)广泛的支持,而且像Microsoft的Visual C++这种交互式IDE也开始支持它了。

我们将在如下的章节中利用一些例子来解释正则表达式的用法,绝大部分的例子是基于vi中的文本替换命令和grep文件搜索命令来书写的,不过它们都是比较典型的例子,其中的概念可以在sed、awk、perl和其他支持正则表达式的编程语言中使用。你可以看看不同工具中的正则表达式这一节,其中有一些在别的工具中使用正则表达式的例子。还有一个关于vi中文本替换命令(s)的简单说明附在文后供参考。

正则表达式基础

正则表达式由一些普通字符和一些元字符(metacharacters)组成。普通字符包括大小写的字母和数字,而元字符则具有特殊的含义,我们下面会给予解释。

在最简单的情况下,一个正则表达式看上去就是一个普通的查找串。例如,正则表达式”testing”中没有包含任何元字符,,它可以匹配”testing”和”123testing”等字符串,但是不能匹配”Testing”。

要想真正的用好正则表达式,正确的理解元字符是最重要的事情。下表列出了所有的元字符和对它们的一个简短的描述。

元字符   描述


.
匹配任何单个字符。例如正则表达式r.t匹配这些字符串:ratrutr t,但是不匹配root 
$
匹配行结束符。例如正则表达式weasel$ 能够匹配字符串”He’s a weasel“的末尾,但是不能匹配字符串”They are a bunch of weasels.“。 
^
匹配一行的开始。例如正则表达式^When in能够匹配字符串”When in the course of human events“的开始,但是不能匹配”What and When in the”。
*
匹配0或多个正好在它之前的那个字符。例如正则表达式.*意味着能够匹配任意数量的任何字符。
这是引用府,用来将这里列出的这些元字符当作普通的字符来进行匹配。例如正则表达式$被用来匹配美元符号,而不是行尾,类似的,正则表达式.用来匹配点字符,而不是任何字符的通配符。
[ ] 
[c1-c2]
[^c1-c2]
匹配括号中的任何一个字符。例如正则表达式r[aou]t匹配ratrotrut,但是不匹配ret。可以在括号中使用连字符-来指定字符的区间,例如正则表达式[0-9]可以匹配任何数字字符;还可以制定多个区间,例如正则表达式[A-Za-z]可以匹配任何大小写字母。另一个重要的用法是“排除”,要想匹配除了指定区间之外的字符——也就是所谓的补集——在左边的括号和第一个字符之间使用^字符,例如正则表达式[^269A-Z] 将匹配除了2、6、9和所有大写字母之外的任何字符。
<>
匹配词(word)的开始(<)和结束(>)。例如正则表达式能够匹配字符串”for the wise“中的”the”,但是不能匹配字符串”otherwise“中的”the”。注意:这个元字符不是所有的软件都支持的。
( )
将 ( 和 ) 之间的表达式定义为“组”(group),并且将匹配这个表达式的字符保存到一个临时区域(一个正则表达式中最多可以保存9个),它们可以用 19 的符号来引用。
|
将两个匹配条件进行逻辑“或”(Or)运算。例如正则表达式(him|her) 匹配”it belongs to him“和”it belongs to her“,但是不能匹配”it belongs to them.“。注意:这个元字符不是所有的软件都支持的。
+
匹配1或多个正好在它之前的那个字符。例如正则表达式9+匹配9、99、999等。注意:这个元字符不是所有的软件都支持的。
?
匹配0或1个正好在它之前的那个字符。注意:这个元字符不是所有的软件都支持的。
{i}
{i,j}
匹配指定数目的字符,这些字符是在它之前的表达式定义的。例如正则表达式A[0-9]{3} 能够匹配字符”A”后面跟着正好3个数字字符的串,例如A123、A348等,但是不匹配A1234。而正则表达式[0-9]{4,6} 匹配连续的任意4个、5个或者6个数字字符。注意:这个元字符不是所有的软件都支持的。

 


最简单的元字符是点,它能够匹配任何单个字符(注意包括新行符)。假定有个文件test.txt包含以下几行内容:

he is a rat
he is in a rut
the food is Rotten
I like root beer

我们可以使用grep命令来测试我们的正则表达式,grep命令使用正则表达式去尝试匹配指定文件的每一行,并将至少有一处匹配表达式的所有行显示出来。命令

grep r.t test.txt

在test.txt文件中的每一行中搜索正则表达式r.t,并打印输出匹配的行。正则表达式r.t匹配一个r接着任何一个字符再接着一个t。所以它将匹配文件中的ratrut,而不能匹配Rotten中的Rot,因为正则表达式是大小写敏感的。要想同时匹配大写和小写字母,应该使用字符区间元字符(方括号)。正则表达式[Rr]能够同时匹配Rr。所以,要想匹配一个大写或者小写的r接着任何一个字符再接着一个t就要使用这个表达式:[Rr].t

要想匹配行首的字符要使用抑扬字符(^)——又是也被叫做插入符。例如,想找到text.txt中行首”he”打头的行,你可能会先用简单表达式he,但是这会匹配第三行的the,所以要使用正则表达式^he,它只匹配在行首出现的h

有时候指定“除了×××都匹配”会比较容易达到目的,当抑扬字符(^)出现在方括号中是,它表示“排除”,例如要匹配he ,但是排除前面是t or s的情性(也就是theshe),可以使用:[^st]he

可以使用方括号来指定多个字符区间。例如正则表达式[A-Za-z]匹配任何字母,包括大写和小写的;正则表达式[A-Za-z][A-Za-z]* 匹配一个字母后面接着0或者多个字母(大写或者小写)。当然我们也可以用元字符+做到同样的事情,也就是:[A-Za-z]+ ,和[A-Za-z][A-Za-z]*完全等价。但是要注意元字符+ 并不是所有支持正则表达式的程序都支持的。关于这一点可以参考后面的正则表达式语法支持情况

要指定特定数量的匹配,要使用大括号(注意必须使用反斜杠来转义)。想匹配所有1001000的实例而排除1010000,可以使用:10{2,3},这个正则表达式匹配数字1后面跟着2或者3个0的模式。在这个元字符的使用中一个有用的变化是忽略第二个数字,例如正则表达式0{3,} 将匹配至少3个连续的0。

简单的例子

这里有一些有代表性的、比较简单的例子。

vi 命令 作用


:%s/ */ /g 把一个或者多个空格替换为一个空格。
:%s/ *$// 去掉行尾的所有空格。
:%s/^/ / 在每一行头上加入一个空格。
:%s/^[0-9][0-9]* // 去掉行首的所有数字字符。
:%s/b[aeio]g/bug/g 将所有的bagbegbigbog改为bug。 
:%s/t([aou])g/h1t/g 将所有tagtogtug分别改为hathothug(注意用group的用法和使用1引用前面被匹配的字符)。

中级的例子(神奇的咒语)

例1

将所有方法foo(a,b,c)的实例改为foo(b,a,c)。这里a、b和c可以是任何提供给方法foo()的参数。也就是说我们要实现这样的转换:

之前   之后
foo(10,7,2) foo(7,10,2)
foo(x+13,y-2,10) foo(y-2,x+13,10)
foo( bar(8), x+y+z, 5) foo( x+y+z, bar(8), 5)

下面这条替换命令能够实现这一魔法:

:%s/foo(([^,]*),([^,]*),([^)]*))/foo(2,1,3)/g

现在让我们把它打散来加以分析。写出这个表达式的基本思路是找出foo()和它的括号中的三个参数的位置。第一个参数是用这个表达式来识别的::([^,]*),我们可以从里向外来分析它: 

[^,]   除了逗号之外的任何字符
[^,]* 0或者多个非逗号字符
([^,]*) 将这些非逗号字符标记为1,这样可以在之后的替换模式表达式中引用它
([^,]*), 我们必须找到0或者多个非逗号字符后面跟着一个逗号,并且非逗号字符那部分要标记出来以备后用。

现在正是指出一个使用正则表达式常见错误的最佳时机。为什么我们要使用[^,]*这样的一个表达式,而不是更加简单直接的写法,例如:.*,来匹配第一个参数呢?设想我们使用模式.*来匹配字符串”10,7,2″,它应该匹配”10,”还是”10,7,”?为了解决这个两义性(ambiguity),正则表达式规定一律按照最长的串来,在上面的例子中就是”10,7,”,显然这样就找出了两个参数而不是我们期望的一个。所以,我们要使用[^,]*来强制取出第一个逗号之前的部分。

这个表达式我们已经分析到了:foo(([^,]*),这一段可以简单的翻译为“当你找到foo(就把其后直到第一个逗号之前的部分标记为1”。然后我们使用同样的办法标记第二个参数为2。对第三个参数的标记方法也是一样,只是我们要搜索所有的字符直到右括号。我们并没有必要去搜索第三个参数,因为我们不需要调整它的位置,但是这样的模式能够保证我们只去替换那些有三个参数的foo()方法调用,在foo()是一个重载(overoading)方法时这种明确的模式往往是比较保险的。然后,在替换部分,我们找到foo()的对应实例,然后利用标记好的部分进行替换,是的第一和第二个参数交换位置。

例2

假设有一个CSV(comma separated value)文件,里面有一些我们需要的信息,但是格式却有问题,目前数据的列顺序是:姓名,公司名,州名缩写,邮政编码,现在我们希望讲这些数据重新组织,以便在我们的某个软件中使用,需要的格式为:姓名,州名缩写-邮政编码,公司名。也就是说,我们要调整列顺序,还要合并两个列来构成一个新列。另外,我们的软件不能接受逗号前后面有任何空格(包括空格和制表符)所以我们还必须要去掉逗号前后的所有空格。

这里有几行我们现在的数据:

Bill Jones,     HI-TEK Corporation ,  CA, 95011
Sharon Lee Smith,  Design Works Incorporated,  CA, 95012
B. Amos   ,  Hill Street Cafe,  CA, 95013
Alexander Weatherworth,  The Crafts Store,  CA, 95014

我们希望把它变成这个样子:

Bill Jones,CA 95011,HI-TEK Corporation
Sharon Lee Smith,CA 95012,Design Works Incorporated
B. Amos,CA 95013,Hill Street Cafe
Alexander Weatherworth,CA 95014,The Crafts Store

我们将用两个正则表达式来解决这个问题。第一个移动列和合并列,第二个用来去掉空格。

下面就是第一个替换命令:

:%s/([^,]*),([^,]*),([^,]*),(.*)/1,3 4,2/

这里的方法跟例1基本一样,第一个列(姓名)用这个表达式来匹配:([^,]*),即第一个逗号之前的所有字符,而姓名内容被用1标记下来。公司名和州名缩写字段用同样的方法标记为23,而最后一个字段用(.*)来匹配(”匹配所有字符直到行末”)。替换部分则引用上面标记的那些内容来进行构造。

下面这个替换命令则用来去除空格:

:%s/[ t]*,[ t]*/,/g

我们还是分解来看:[ t]匹配空格/制表符,[ t]* 匹配0或多个空格/制表符,[ t]*,匹配0或多个空格/制表符后面再加一个逗号,最后,[ t]*,[ t]*匹配0或多个空格/制表符接着一个逗号再接着0或多个空格/制表符。在替换部分,我们简单的我们找到的所有东西替换成一个逗号。这里我们使用了结尾的可选的g参数,这表示在每行中对所有匹配的串执行替换(而不是缺省的只替换第一个匹配串)。

例3

假设有一个多字符的片断重复出现,例如:

Billy tried really hard
Sally tried really really hard
Timmy tried really really really hard
Johnny tried really really really really hard

而你想把”really”、”really really”,以及任意数量连续出现的”really”字符串换成一个简单的”very”(simple is good!),那么以下命令:

:%s/(really )(really )*/very /

就会把上述的文本变成:

Billy tried very hard
Sally tried very hard
Timmy tried very hard
Johnny tried very hard

表达式(really )*匹配0或多个连续的”really “(注意结尾有个空格),而(really )(really )* 匹配1个或多个连续的”really “实例。

困难的例子(不可思议的象形文字)

Coming soon.

 


不同工具中的正则表达式

OK,你已经准备使用RE(regular expressions,正则表达式),但是你并准备使用vi。所以,在这里我们给出一些在其他工具中使用RE的例子。另外,我还会总结一下你在不同程序之间使用RE可能发现的区别。

当然,你也可以在Visual C++编辑器中使用RE。选择Edit->Replace,然后选择”Regular expression”选择框,Find What输入框对应上面介绍的vi命令:%s/pat1/pat2/g中的pat1部分,而Replace输入框对应pat2部分。但是,为了得到vi的执行范围和g选项,你要使用Replace All或者适当的手工Find Next and Replace(译者按:知道为啥有人骂微软弱智了吧,虽然VC中可以选中一个范围的文本,然后在其中执行替换,但是总之不够vi那么灵活和典雅)。

sed

Sed是Stream EDitor的缩写,是Unix下常用的基于文件和管道的编辑工具,可以在手册中得到关于sed的详细信息。

这里是一些有趣的sed脚本,假定我们正在处理一个叫做price.txt的文件。注意这些编辑并不会改变源文件,sed只是处理源文件的每一行并把结果显示在标准输出中(当然很容易使用重定向来定制):

sed脚本   描述


sed ’s/^$/d’ price.txt 删除所有空行
sed ’s/^[ t]*$/d’ price.txt 删除所有只包含空格或者制表符的行
sed ’s/”//g’ price.txt 删除所有引号

awk

awk是一种编程语言,可以用来对文本数据进行复杂的分析和处理。可以在手册中得到关于awk的详细信息。这个古怪的名字是它作者们的姓的缩写(Aho,Weinberger和Kernighan)。

在Aho,Weinberger和Kernighan的书The AWK Programming Language中有很多很好的awk的例子,请不要让下面这些微不足道的脚本例子限制你对awk强大能力的理解。我们同样假定我们针对price.txt文件进行处理,跟sed一样,awk也只是把结果显示在终端上。 

awk脚本   描述


awk ‘$0 !~ /^$/’ price.txt 删除所有空行
awk ‘NF > 0′ price.txt awk中一个更好的删除所有行的办法
awk ‘$2 ~ /^[JT]/ {print $3}’ price.txt 打印所有第二个字段是’J'或者’T'打头的行中的第三个字段
awk ‘$2 !~ /[Mm]isc/ {print $3 + $4}’ price.txt 针对所有第二个字段不包含’Misc’或者’misc’的行,打印第3和第4列的和(假定为数字)
awk ‘$3 !~ /^[0-9]+.[0-9]*$/ {print $0}’ price.txt 打印所有第三个字段不是数字的行,这里数字是指d.d或者d这样的形式,其中d是0到9的任何数字
awk ‘$2 ~ /John|Fred/ {print $0}’ price.txt 如果第二个字段包含’John’或者’Fred’则打印整行

grep

grep是一个用来在一个或者多个文件或者输入流中使用RE进行查找的程序。它的name编程语言可以用来针对文件和管道进行处理。可以在手册中得到关于grep的完整信息。这个同样古怪的名字来源于vi的一个命令,g/re/p,意思是global regular expression print。

下面的例子中我们假定在文件phone.txt中包含以下的文本,——其格式是姓加一个逗号,然后是名,然后是一个制表符,然后是电话号码:

Francis, John           5-3871
Wong, Fred              4-4123
Jones, Thomas           1-4122
Salazar, Richard        5-2522

grep命令   描述


grep ‘t5-…1′ phone.txt 把所有电话号码以5开头以1结束的行打印出来,注意制表符是用t表示的
grep ‘^S[^ ]* R’ phone.txt 打印所有姓以S打头和名以R打头的行
grep ‘^[JW]‘ phone.txt 打印所有姓开头是J或者W的行
grep ‘, ….t’ phone.txt 打印所有姓是4个字符的行,注意制表符是用t表示的
grep -v ‘^[JW]‘ phone.txt 打印所有不以J或者W开头的行
grep ‘^[M-Z]‘ phone.txt 打印所有姓的开头是M到Z之间任一字符的行
grep ‘^[M-Z].*[12]‘ phone.txt 打印所有姓的开头是M到Z之间任一字符,并且点号号码结尾是1或者2的行

egrep

egrep是grep的一个扩展版本,它在它的正则表达式中支持更多的元字符。下面的例子中我们假定在文件phone.txt中包含以下的文本,——其格式是姓加一个逗号,然后是名,然后是一个制表符,然后是电话号码:

Francis, John           5-3871
Wong, Fred              4-4123
Jones, Thomas           1-4122
Salazar, Richard        5-2522
egrep command   Description


egrep ‘(John|Fred)’ phone.txt 打印所有包含名字John或者Fred的行
egrep ‘John|22$|^W’ phone.txt 打印所有包含John 或者以22结束或者以W的行
egrep ‘net(work)?s’ report.txt 从report.txt中找到所有包含networks或者nets的行

正则表达式语法支持情况

命令或环境 . [ ] ^ $ ( ) { } ? + | ( )
vi  X   X   X   X   X           
Visual C++  X   X   X   X   X           
awk  X   X   X   X       X   X   X   X 
sed  X   X   X   X   X   X         
Tcl  X   X   X   X   X     X   X   X   X 
ex  X   X   X   X   X   X         
grep  X   X   X   X   X   X         
egrep  X   X  X   X   X     X   X   X   X 
fgrep  X   X   X   X   X           
perl  X  X  X  X  X    X  X  X  X

 


vi替换命令简介

Vi的替换命令:

:ranges/pat1/pat2/g

其中

: 这是Vi的命令执行界面。
range 是命令执行范围的指定,可以使用百分号(%)表示所有行,使用点(.)表示当前行,使用美元符号($)表示最后一行。你还可以使用行号,例如10,20表示第10到20行,.,$表示当前行到最后一行,.+2,$-5表示当前行后两行直到全文的倒数第五行,等等。

s 表示其后是一个替换命令。

pat1 这是要查找的一个正则表达式,这篇文章中有一大堆例子。

pat2 这是希望把匹配串变成的模式的正则表达式,这篇文章中有一大堆例子。

g 可选标志,带这个标志表示替换将针对行中每个匹配的串进行,否则则只替换行中第一个匹配串。

网上有很多vi的在线手册,你可以访问他们以获得更加完整的信息。

 

Posted by Wyulnnhtg at 17:11:39 | Permalink | No Comments »

一只毛笔,两个螺丝刀;给电脑洗澡!

五一长假,也过个有意义的劳动节。。

电脑已经为我服役两年多了,还一次澡没洗!

这个劳动节,也让它舒服一点。。。

很奇怪的是打开机箱在里面竟然发现了几片羽毛。。

我从来没养过带有羽毛的动物。。(仅一只狗而已)

动手,机箱全部分解(CPU的散热片没搞定,被硅胶粘住了,都怪我力气小)

用毛笔对每个部件一一打扫。。没想到这东西虽然长得不大。

竟然用了我整个下午的时间。。

Posted by Wyulnnhtg at 15:30:21 | Permalink | No Comments »

来看看这个搜索引擎。。。

其实没什么技术含量。。。

但想法蛮新颖的。。

是通过框架将Google和Baidu两个搜索巨头集成在一起

使之提交一次关键字,同时在两个搜索引擎中搜索。。

地址:http://www.baigoogledu.com/

Posted by Wyulnnhtg at 14:48:33 | Permalink | No Comments »

Saturday, April 29, 2006

真是搞笑的“超级女生”。。。。

今天看电视,选台的时候不小进了湖南台。。。

当时正播“超级女生”海选赛。。。

要是平时,我肯定手指一晃就跳过了。。。

不过,今年的超级女生可真是有味道。。。

我看了三个选手的海选赛,有两位是年过七旬的大妈。。。

哼哼吖吖的唱了一痛。三位评委也很给两位大妈面子。都忍到她们唱完。。。

我看“超级女生”不如就此改名“超级大妈”算了。。。

Posted by Wyulnnhtg at 08:32:10 | Permalink | No Comments »

Thursday, April 27, 2006

MSSQL的高危存储过程及其依存DLL的安全处理

先来列出危险的内置存储过程:

xp_cmdshell
xp_regaddmultistring
xp_regdeletekey
xp_regdeletevalue
xp_regenumkeys
xp_regenumvalues
xp_regread
xp_regremovemultistring
xp_regwrite

ActiveX自动脚本:sp_OACreate
sp_OADestroy
sp_OAMethod
sp_OAGetProperty
sp_OASetProperty
sp_OAGetErrorInfo
sp_OAStop

以上各项全在我们封杀之列,例如xp_cmdshell屏蔽的方法为:

sp_dropextendedproc ‘xp_cmdshell’
,如果需要的话,再用

sp_addextendedproc ‘xp_cmdshell’, ‘xpsql70.dll’
进行恢复。如果你不知道xp_cmdshell使用的是哪个.dll文件的话,可以使用

sp_helpextendedproc xp_cmdshell
来查看xp_cmdshell使用的是哪个动态联接库。另外,将xp_cmdshell屏蔽后,我们还需要做的步骤是将xpsql70.dll文件进行改名,以防止获得SA的攻击者将它进行恢复。

 

删除MSSQL危险存储过程的代码
drop PROCEDURE sp_makewebtask
exec master..sp_dropextendedproc xp_cmdshell
exec master..sp_dropextendedproc xp_dirtree
exec master..sp_dropextendedproc xp_fileexist
exec master..sp_dropextendedproc xp_terminate_process
exec master..sp_dropextendedproc sp_oamethod
exec master..sp_dropextendedproc sp_oacreate
exec master..sp_dropextendedproc xp_regaddmultistring
exec master..sp_dropextendedproc xp_regdeletekey
exec master..sp_dropextendedproc xp_regdeletevalue
exec master..sp_dropextendedproc xp_regenumkeys
exec master..sp_dropextendedproc xp_regenumvalues
exec master..sp_dropextendedproc sp_add_job
exec master..sp_dropextendedproc sp_addtask
exec master..sp_dropextendedproc xp_regread
exec master..sp_dropextendedproc xp_regwrite
exec master..sp_dropextendedproc xp_readwebtask
exec master..sp_dropextendedproc xp_makewebtask
exec master..sp_dropextendedproc xp_regremovemultistring

将以上代码复制到查询分析器里执行就可以了。

Posted by Wyulnnhtg at 14:21:58 | Permalink | No Comments »

Serv-U安全设置攻略

作为一款精典的FTP服务器软件,SERV-U一直被大部分管理员所使用,它简单的安装和配置以及强大的管理功能的人性化也一直被管理员们称颂。但是随着使用者越来越多,该软件的安全问题也逐渐显露出来。

  首先是SERV-U的SITE CHMOD漏洞和Serv-U MDTM漏洞,即利用一个账号可以轻易的得到SYSTEM权限。其次是Serv-u的本地溢出漏洞,即Serv-U有一个默认的管理用户(用户名:localadministrator,密码:#@$ak#.k;0@p),任何人只要通过一个能访问本地端口43958的账号就可以随意增删账号和执行任意内部和外部命令。


  此时,人们才开始重视起SERV-U的安全来,并采取了一些相关措施,如修改SERV-U的管理端口、账号和密码等。但是,修改后的内容还是保留在ServUDaemon.exe文件里,因此下载后用如UltraEdit之类的16进制编辑软件就可以很轻易的获取到修改后的端口、账号和密码。

  从SERV-U6.0.0.2开始,该软件有了登录密码功能,这样如果加了管理密码,并且设置比较妥善的话,SERV-U将会比原来安全的多。现在我们就开始SERV-U的设置之旅,采用版本是SERV-U 6.0.0.2。

  古语有云,千尺之台始于垒土,设置SERV-U的安全就从安装开始。这篇文章主要是写SERV-U的安全设置,所以不会花费太多的功夫来介绍安装,只说一下要点。

  SERV-U默认是安装在C:\Program Files\Serv-U目录下的,我们最好做一下变动。例如改为:D:\u89327850mx8utu432X$UY32×211936890co7v23×1t3(图1)这样的路径,如果安装盘符WEB用户不能浏览的话,他便很难猜到安装的路径。当然,安装后会在桌面和开始菜单上生成快捷方式,建议删除,因为一般不会使用到它。可能你要问了,那应该怎样进入SERV-U的设置界面呢?其实很简单,双击下右角任务栏里的Tray Monitor小图标来启动SERV-U的管理界面。

  

  图1:修改安装的目录

  安装的时候只选前2项就可以了,后面的2个是说明和在线帮助文件。(见图2)

  

  图2:安装时候只需要选择前2项

下图是生成的开始菜单组里的文件夹的名字,建议更改成比较不像SERV-U的名字,或者是删除该文件夹。(见图3)

  

  图3:更改安装后生成开始菜单组里文件夹的名字

  安装完成后会出现一个向导让你建立一个域和账号。在这里点Cancel取消向导。用向导生成的账号会带来一些问题,所以下面采用手工方式建立域和账号。(见图4)

  

  图4:点Cancel取消向导

  然后点选Start automatically(system service)前面的选项,接着点下边的Start Server按钮把SERV-U加入系统服务,这样就可以随系统启动了,不用每次都手工启动。(见图5)

  

  图5:把SERV-U加入服务

  接下来就会出现如图6的界面。通过点击Set/Change Password设置一个密码。

  

  图6:点击Set/Change Password设置密码

  然后会出现如图7的界面。因为是第一次使用,所以是没有密码的,也就是说原来的密码为空。不用在old password里输入字符,直接在下面的New password和Repeat new password里输入同样的密码再点OK就可以了。这里建议设置一个足够复杂的密码,以防止别人暴力破解。自己记不得也没有关系,只要把ServUDaemon.ini里的LocalSetupPassword=这一行清除并保存,再次运行ServUAdmin.exe就不会提示你输入密码登录了。

  

  图7:设置和更改密码界面

下面就到了该对SERV-U进行安全设置的时候了。首先建立一个WINDOWS账号SSERVU,密码也需要足够的复杂。密码要记住,如果记不住就暂时保存在一个文件里,一会儿还要用到。(见图8)

  

  图8:建立一个WINDOWS账号

  建好账号以后,双击建好的用户编辑用户属性,从“隶属于”里删除USERS组。

  

  图9:从隶属于里删除USERS组

  从“终端服务配置文件”选项里取消“允许登录到终端服务器(W)”的选择,然后点击确定继续我们的设置。(见图10)

  

  图10:取消“允许登录到终端服务器”

  这里我们已经建好了账号,该设置服务里的账号了。现在就要用到刚才建立的这个账号,密码还没有忘记吧,马上就要用到了。

  在开始菜单的管理工具里找到“服务”点击打开。在“Serv-U FTP Server服务”上点右键,选择属性继续。

  然后点击“登录”进入登录账号选择界面。选择刚才建立的系统账号名,并在下面重复输入2次该账号的密码(就是刚才让你记住的那个),然后点“应用”,再次点确定,完成服务的设置。(见图11)

  

  图11:更改启动和登录SRV-U的账号密码

  接下来要先使用FTP管理工具建立一个域,再建立一个账号,建好后选择保存在注册表。(见图12)

  

  图12:FTP用户密码保存到注册表里

打开注册表来测试相应的权限,否则SERV-U是没办法启动的。在开始->运行里输入regedt32点“确定”继续。

  找到[HKEY_LOCAL_MACHINE\SOFTWARE\Cat Soft]分支。在上面点右键,选择权限,然后点高级,取消允许父项的继承权限传播到该对象和所有子对象,包括那些在此明确定义的项目,点击“应用”继续,接着删除所有的账号。再次点击“确定”按钮继续。这时会弹出对话框显示“您拒绝了所有用户访问Cat Soft。没有人能访问 Cat Soft,而且只有所有者才能更改权限。您要继续吗?”,点击“是”继续。接着点击添加按钮增加我们建立的SSERVU账号到该子键的权限列表里,并给予完全控制权限。到这里注册表已经设置完了。但还不能重新启动SERV-U,因为安装目录还没设置。

  现在就来设置一下,只保留你的管理账号和SSERVU账号,并给予除了完全控制外的所有权限。(见图13)

  

  图13:SERV-U安装目录权限设置

  现在,在服务里重启Serv-U FTP Server服务就可以正常启动了。当然,到这里还没有完全设置完,你的FTP用户因为没有权限还是登录不了的,所以还要设置一下目录的权限。

  假设你有一个WEB目录,路径是d:\web。那么在这个目录的“安全设定”里除了管理员和IIS用户都删除掉,再加入SSERVU账号,切记SYSTEM账号也删除掉。为什么要这样设置呢?因为现在已经是用SSERVU账号启动的SERV-U,而不是用SYSTEM权限启动的了,所以访问目录不再是用SYSTEM而是用SSERVU,此时SYSTEM已经没有用了,这样就算真的溢出也不可能得到SYSTEM权限。另外,WEB目录所在盘的根目录还要设置允许SSERV-U账号的浏览和读取权限,并确认在高级里设置只有该文件夹。(见图14)

  

  图14:WEB目录所在盘的权限设置

  至此,设置全部结束。现在的SERV-U设置是配合IIS设置的,因为和IIS使用不同的账号,WEB用户就不可能访问SERV-U的目录,并且WEB目录没有给予SYSTEM权限,所以SYSTEM账号也同样访问不了WEB目录,也就是说,即使使用MSSQL得到备份的权限也不能备份SHELL到你的WEB目录。你可以安全的使用SERV-U了。

Posted by Wyulnnhtg at 13:44:04 | Permalink | No Comments »

Thursday, April 20, 2006

Tracing the roots of computer languages through the ages.

Posted by Wyulnnhtg at 16:23:53 | Permalink | No Comments »

程序语言选学方案

程序语言可分为5类

  1、web page script languages

    就是网页代码,比如Html、javascript、Css、Asp、Php、Xml都应该属于此类(照蔡的意思,Html不应该属于anguages的,可能他觉得太简单了吧,不过我觉得Html也应该算的,大家爱听谁的就听谁的好了,这个不重要)

  2、Interpreted Languages(解释型语言)

    包括Perl、Python、REBOL、Ruby等(后面三个听豆没听过),也常被称作Script语言,通常被用于和底下的操作系统沟通。照蔡的意思,每个人至少应该学会一套这类的语言。这类语言的缺点是效率差、源代码外露——所以不适合用来开发软件产品,一般用于网页服务器。

  3、Hybrid Laguages(混合型语言)

    代表是JAVA和C#。介于解释型和编译型之间。蔡认为C#是.NET最重要的语言,值得期待其后续发展;至于JAVA,蔡叫看他的《Sleepless in Java》——我倒!

  4、COMPILING Languages(编译型语言)

    C/C++,JAVA都是编译型语言。蔡说C++很复杂,要成为高手没三五年是不行的(就凭这句话,我决定向JAVA投降),虽然如此,但真正要成为高手的都应该懂C/C++。

    关于Delphi,蔡说如果想要跨Linux和Windows平台的语言,则Delphi似乎是最好的选择。

  5、Assembly Languages(汇编语言)

    汇编语言是最接近于硬件的语言,不过现在几乎没多少人用了。

  程序语言学习顺序建议

    如果完全没有程序经验,可照这个顺序:javascript——解释型语言——混合型语言——编译型语言——汇编(如果需要的话)

    用业界流行一句话做结尾“真正的程序员用C++,聪明的程序员用Delphi”,那么,什么样的程序员用JAVA呢?

  虚怀若谷题外话

    看在我这么晚了还这么辛苦的敲字的面子上,看到这篇帖子的朋友给点掌声吧,虽然没什么自己的思想。

    另外,本文完全据蔡学镛先生同名文章精简,所以如果这篇文章给你的程序员生涯有什么不好的作用,那我不介意你去扁他。当然如果这篇文章对你有什么积极影响,我很高兴你能请我吃大餐。

如果您想学习编程,却又不知从何入手,那么您不妨看看下面的几种学习方案,可能会给您一些启示吧!

    方案一 Basic语言 & Visual Basic

    优点

    (1)Basic 简单易学,很容易上手。

    (2)Visual Basic 提供了强大的可视化编程能力,可以让你轻松地做出漂亮的程序。

    (3)众多的控件让编程变得象垒积木一样简单。

    (4)Visual Basic 的全部汉化让我们这些见了English就头大的人喜不自禁。

    缺点

    (1)Visual Basic 不是真正的面向对象的开发文具。

    (2)Visual Basic 的数据类型太少,而且不支持指针,这使得它的表达能力很有限。

    (3)Visual Basic 不是真正的编译型语言,它产生的最终代码不是可执行的,是一种伪代码。它需要一个动态链接库去解释执行,这使得Visual Basic 的编译速度大大变慢。

    综述:方案一适合初涉编程的朋友,它对学习者的要求不高,几乎每个人都可以在一个比较短的时间里学会vB编程,并用VB 做出自己的作品。对于那些把编程当做游戏的朋友来说,VB 是您最佳的选择。

    方案二 Pascal语言 & Delphi

    优点

    (1)Pascal语言结构严谨,可以很好地培养一个人的编程思想。

    (2)Delphi是一门真正的面向对象的开发工具,并且是完全的可视化。

    (3)Delphi使用了真编译,可以让你的代码编译成为可执行的文件,而且编译速度非常快。

    (4)Delphi具有强大的数据库开发能力,可以让你轻松地开发数据库。

    缺点

    Delphi几乎可以说是完美的,只是Pascal语言的过于严谨让人感觉有点烦。

    综述: 方案二比较适合那些具有一定编程基础并且学过Pascal语言的朋友。

    方案三 C语言 & Visual C++

    优点

    (1)C语言灵活性好,效率高,可以接触到软件开发比较底层的东西。

    (2)微软的MFC库博大精深,学会它可以让随心所欲地进行编程。

    (3)VC是微软制作的产品,与操作系统的结合更加紧密。

    缺点

    对使用者的要求比较高,既要具备丰富的C语言编程经验,又要具有一定的WINDOWS编程基础,它的过于专业使得一般的编程爱好者学习起来会有不小的困难。

    综述: VC是程序员用的东西。如果你是一个永不满足的人,而且可以在编程上投入很大的精力和时间,那么学习VC你一定不会后悔的。

    方案四 C++语言 & C++ Builder

    优点

    (1)C++语言的优点全部得以继承。

    (2)完全的可是化。

    (3)极强的兼容性,支持OWL、VCL和MFC三大类库。

    (4)编译速度非常快。

    缺点

    由于推出的时间太短,关于它的各种资料还不太多。

     综述:我认为C++ Builder 是最好的编程工具。它既保持了C++语言编程的优点,又做到了完全的可视化。

    方案五 SQL语言 & Power Builder

    对于一些传统的数据开发人员来说,Foxpro系列也许让他们感到更加熟悉。但是对于初学者来说,PowerBuilder也许是最好的数据库开发工具。各种各样的控件,功能强大的PowerBuilder语言都会帮助你开发出自己的数据库应用程序。

补充:关于网页后台语言和其他语言的关系,一般认为:

C语言学的好,那么学习PHP会有事半功倍的效果

VC学的好,那么学习ASP会有事半功倍的效果
JAVA学的好,那么学习JSP、JAVASCRIPT会有事半功倍的效果

关于网页后台语言和数据库,通常来说:
PHP与MYSQL是黄金搭档
ASP小型网站用ACCESS,大型用SQL
JSP我就不知道了

关于电子商务流行的语言,目前
当当、卓越、6688、igo5、雅宝使用的是asp语言
易趣、淘宝使用的是php语言
贝塔斯曼使用的是jsp语言

 

Posted by Wyulnnhtg at 13:24:31 | Permalink | No Comments »

Tuesday, April 18, 2006

制作网站同步镜像

现在的网站随着访问量的增加,单一服务器无法承担巨大的访问量,有没有什么方便快捷的方式解决这个问题呢,答案是”有”!
比如建立服务器群,进行均衡负载.
但是如果要解决像电信网通这样的互访问题(中国网民的悲哀..),这个解决办法就无能为力了!
要解决这个问题最方便快捷的方式就是建立镜像网站!由访问者自己选择适合自己网络的速度最快的网站!这样即可以解决线路问题,又可以解决访问量问题!
1.介绍

现在的网站随着访问量的增加,单一服务器无法承担巨大的访问量,有没有什么方便快捷的方式解决这个问题呢,答案是”有”!
比如建立服务器群,进行均衡负载.
但是如果要解决像电信网通这样的互访问题(中国网民的悲哀..),这个解决办法就无能为力了!
要解决这个问题最方便快捷的方式就是建立镜像网站!由访问者自己选择适合自己网络的速度最快的网站!这样即可以解决线路问题,又可以解决访问量问题!

2.网站同步的数据分类

网站数据基本分为两类:
一类是文件,比如HTML,ASP,PHP等网页文件,或者RAR,ZIP,RM,AVI等可下载文件!
要实现他们的同步很简单,用FTP同步软件就可以了!至于哪几个我会在后面做详细介绍.

一类是数据库数据文件,比如MySQL,SQL Server等等!
数据库同步的方法也很多,最简单的办法只是将数据库目录同步一下就OK了!
在后面我也会做详细讲解!

3.网站文件的同步

在这里用到的主要工具就是FTP,网站文件同步分两种情况,一种是本地到远程,一种是远程到远程(FXP)!第一种不用说了,第二种远程到远程即FXP,支持它的软件也很多,但是真正适合多网站同步镜像的却不多!
下面我介绍几个我认为不错的软件!

1.首先我要推荐的是国产的FTP软件”网络传神”,功能非常强大,特别是在网站的同步镜像方面,可惜的是,这款非常经典的软件已经不再更新了,最后更新时间是2003年3月,最后一个版本是3.12!虽然如此还是非常好用的!下面是一段官方的简介:

网络传神完全吸收了Cuteftp和UpdataNow的全部功能,并且增加了其他软件没有的多项功能:支持网站互传;支持网站同步(UPDATA NOW);支持后台上传(多线程上传多个文件);可同时打开多个站点;多站点计划上传功能,支持镜像站点;支持宏操作支持计划操作;支持文件高级比较上传;支持目录隐藏过滤(为用ForntPage作主页的朋友带来福音);服务器自动识别功能;资源管理器 浏览方式;可以自定义命令;支持RFC959标准具有更好的稳定性;完备的信息返回机制及错误监控机制完整的中文帮助。

2.第二款是由ReGet同一开发公司制作的专用于网站同步的软件”WebSynchronizer”,用这款软件,你才会体验到网站同步的方便快捷,简单容易.最新版本是1.3.62, 网上能找到XX的最后版本是1.1版!下面是一段简介:

档案同步化工具 - WebSynchronizer,由知名续传软件 ReGet 之软件出版公司所推出,是网站同步化、档案镜像、档案备份的绝佳工具,可以执行下列主要工作:1) 本机资料夹及远程资料夹的同步化;2) 两台远程计算机中的资料夹同步化;3) 两个本机资料之同步化。

3.其他还有一些软件如同步快梭(AutoSyncFTP),也能实现简单的网站同步,不过,这款软件非常不稳定,而且2001年就已经停止开发.所以,不用考虑了!还有上次有朋友提到的SiteMirro,由于网上找不到可以用的版本,所以没有办法测试 !

网络传神网站同步镜像使用傻瓜指南

上次在网络技术版已经发过一个简单的使用方法介绍,如果嫌我罗嗦(o(>_<)o),大家可以参考那篇文章:
http://bbs.et8.net/bbs/showthread.php?t=650129

第一次运行网络传神的时候,它会让你选择”完全模式”和”启动模式”,要使用网站同步必须选择”完全模式”.

进入主界面:你会发现这个软件非常像FlashGet,要使用网站同步功能,点击软件左下角的”FTP客户端”.

现在你要做的就是添加你的镜像母站和需要镜像的网站:文件->站点管理,在里面添加你需要同步的几个的FTP服务器,下面我将介绍具体设置(主要是镜像母站的设置)

母站设置最重要的地方就是”比较目录(网站同步)”:

1.拖动传送
就是手动拖动传送时候的传送方式,非拖动方式不受影响,因为母站的更新基本上是从本地目录进行的,所以建议这里选择第二项”只上传不同的文件”,镜像站点这里可以忽略
2.磁盘比较设置
这个功能非常有用,可以通过文件长度和文件日期进行文件差异对比.

你还可以设置本地目录过滤和服务器目录过滤,非常灵活!
好了,添加好母站和镜像站之后,我们来设置同步:

如果是添加镜像站,则会跳出下面的母站选择窗口:

设置好多个镜像站点之后,返回主界面就可以进行网站同步了:

注意事项:
一般不要改动镜像站点前面的“对钩”标志,因为镜像站点同步的原理是“根据主站点的同步信息更新镜像站点”,如果景象站点没有更新那么必须将该镜像站点单独同步。
注意一般应该“选项窗口”-〉“FTP客户端”-〉“计划”中的“最大连接个数”和“最大线程个数”一般不要改动保持为“1”。
如果同步时某个站点出错同步没有完全完成只需按“计划”窗口中的 按钮单独执行某个站点的传送即可。

再介绍一下网站镜像同步中必要的功能,定时同步,这样你就只要做好母站的更新,其他的让网络传神帮你完成:
点击设置:

进入网络传神的选项设置窗口,选中FTP客户端中的定时同步

定时下载:在指定的时段内下载文件。
定时上传:在指定的时段内用网站同步的方法进行上传。
开始时间:如果要使用定时方式必须设置此项,在设置的时间中会自动开始所有设置为计划的站点。
停止时间:如果设置此项则当系统时间大于停止时间时自动停止所有设置为计划的站点。

注意:定时上传种没有“停止时间”选项,网站同步完成后会自动停止,同时网站同步同一时间只能运行一次。

网络传神还有很多为便于同步镜像设置的功能,大家实际使用中去体会吧!

Web Synchronizer 网站同步镜像使用傻瓜指南

Web Synchronizer功能非常强大,不单单是网站同步,还可以进行本地目录同步,本地到远程同步,功能方面远胜过传神,不过,国内好像没有汉化版,对于一些对英文感冒的朋友用起来会不太舒服,希望CCF里的汉化高手能将他中文化!

开始运行程序,老外的软件就是智能,一打开这个软件就会有一个同步向导跳出来!

在这里,你也看出来了吧,这个软件只支持两个目录间的同步.不过,我们可以创建多个项目来解决这个问题!具体我在后面介绍!这里,我们来看看如何建立两个远程目录的同步!
选择第二项,进入下一个选择框:

网站同步当然是选择第一项了!再进入下一步,要你选择同步的第一个服务器,建议在这里servers list,先编辑好需要同步的几个FTP

同步方式设置说明:
第一项的意思是:上传下载所有改变的文件
第二项的意思是:不改变文件夹内容,只下载新的文件

点击下一步进行第二个服务器设置,方法和第一个一样!
设置完成后,你还可以修改任务名称!
我觉得这点就是这个软件非常优秀的地方,以任务的方式进行管理.非常方便!

OK,现在我们就可以看到他的主界面了!

还有一点要提,他的计划任务更能也很强大,在这里我就不再多说了!用过才知道他的强大!

有朋友会说,我要进行两个以上的网站同步怎么办?
很简单,再多建几个任务就可以了,要镜像多少网站都没有问题!
这个软件的网站同步镜像可以非常灵活,比如可以本地-远程,然后再远程-远程!或者本地-远程1,本地-远程2,远程1-远程2,任何情形下的同步都可以完成!

数据库同步操作指南

说完了文件同步,我们再来探讨一下数据库同步!
这里我主要教大家两种最常用的数据库的同步方法!

MySQL

MySQL数据同步主要有三种方式:
1.利用MySQL自身的数据库同步功能
2.利用MySQL数据库的特性(数据库存在固顶目录,并且以文件形式存储),进行数据库目录同步以达到数据同步目的
3.利用专用的MySQL数据库同步软件

1. 利用MySQL自身的数据库同步功能(下面参考自网上的文章,写的非常详细了)
MySQL从3.23.15版本以后提供数据库复制功能。利用该功能可以实现两个数据库同步,主从模式,互相备份模式的功能.

数据库同步复制功能的设置都在mysql的设置文件中体现。mysql的配置文件(一般是my.cnf),在unix环境下在/etc/mysql/my.cnf 或者在mysql用户的home目录下的my.cnf。

windows环境中,如果c:根目录下有my.cnf文件则取该配置文件。当运行mysql的winmysqladmin.exe工具时候,该工具会把c:根目录下的my.cnf 命名为mycnf.bak。并在winnt目录下创建my.ini。mysql服务器启动时候会读该配置文件。所以可以把my.cnf中的内容拷贝到my.ini文件中,用my.ini文件作为mysql服务器的配置文件。

  设置方法:
  设置范例环境:
  操作系统:window2000 professional
  mysql:4.0.4-beta-max-nt-log
  A ip:10.10.10.22
  B ip:10.10.10.53
  A:设置
  1.增加一个用户最为同步的用户帐号:
  GRANT FILE ON *.* TO backup@’10.10.10.53′ IDENTIFIED BY ‘1234’
  2.增加一个数据库作为同步数据库:
  create database backup
  B:设置
  1.增加一个用户最为同步的用户帐号:
  GRANT FILE ON *.* TO backup@’10.10.10.22′ IDENTIFIED BY ‘1234’
  2.增加一个数据库作为同步数据库:
  create database backup
  主从模式:A->B
  A为master
  修改A mysql的my.ini文件。在mysqld配置项中加入下面配置:
  server-id=1
  log-bin
  #设置需要记录log 可以设置log-bin=c:mysqlbakmysqllog 设置日志文件的目录,
  #其中mysqllog是日志文件的名称,mysql将建立不同扩展名,文件名为mysqllog的几个日志文件。
  binlog-do-db=backup #指定需要日志的数据库
  重起数据库服务。
  用show master status 命令看日志情况。
  B为slave
  修改B mysql的my.ini文件。在mysqld配置项中加入下面配置:
  server-id=2
  master-host=10.10.10.22
  master-user=backup #同步用户帐号
  master-password=1234
  master-port=3306
  master-connect-retry=60 预设重试间隔60秒
  replicate-do-db=backup 告诉slave只做backup数据库的更新
  重起数据库
  用show slave status看同步配置情况。
  注意:由于设置了slave的配置信息,mysql在数据库目录下生成master.info
  所以如有要修改相关slave的配置要先删除该文件。否则修改的配置不能生效。
  双机互备模式。
  如果在A加入slave设置,在B加入master设置,则可以做B->A的同步。
  在A的配置文件中 mysqld 配置项加入以下设置:
  master-host=10.10.10.53
  master-user=backup
  master-password=1234
  replicate-do-db=backup
  master-connect-retry=10
  在B的配置文件中 mysqld 配置项加入以下设置:
  log-bin=c:mysqllogmysqllog
  binlog-do-db=backup
  注意:当有错误产生时*.err日志文件。同步的线程退出,当纠正错误后要让同步机制进行工作,运行slave start
  重起AB机器,则可以实现双向的热备。
  测试:
  向B批量插入大数据量表AA(1872000)条
  A数据库每秒钟可以更新2500条数据。

2.数据库目录同步,方法和文件同步一样,设置好需要同步的两个数据库目录就可以了!
缺点很明显,数据同步只能单向进行,可以作为备份方案

3.用专用的MySQL同步软件进行同步
这方面的软件有SQLBalance 和MyReplicator ,优点是方便直观,还有很多争强功能!
缺点和2一样,只能单项同步!
当然你也可以修改镜像网站的程序为提交数据到母数据库,读取则在当前镜像下的数据,不过,修改起来麻烦!普通用户修改也非常难!呵呵,大家了解一下就可以!给大家一个思路!有能力的朋友可以试试阿!

4.关于MySQL论坛的数据同步
由于数据来源的不可控制(不好表达),论坛数据是实时的,而且还要考虑来自镜像论坛的数据,如何实现镜像论坛与母论坛数据同步呢?
用1中介绍的MySQL自带的数据库同步功能互相备份模式就可以实现的!
不过,具体的应用我没有测试!稳定性不敢保证!
有能力的朋友推荐用下面这种思路来同步,相对来说减少点效率,但能减少发生的错误!
比如镜像论坛数据同步:
1.母论坛和镜像论坛的数据全写在母论坛数据库里,主从模式,读取只在本地读取,这个需要修改程序!

2.每次写数据,都同时提交到两个数据库中,安全,但是效率很差,也得修改程序!

MSSQL

MSSQL数据同步利用数据库复制技术实现数据同步更新(来自网络,也是非常完美的教程)
复制的概念
复制是将一组数据从一个数据源拷贝到多个数据源的技术,是将一份数据发布到多个存储站点上的有效方式。使用复制技术,用户可以将一份数据发布到多台服务器上,从而使不同的服务器用户都可以在权限的许可的范围内共享这份数据。复制技术可以确保分布在不同地点 的数据自动同步更新,从而保证数据的一致性。
SQL复制的基本元素包括
出版服务器、订阅服务器、分发服务器、出版物、文章
SQL复制的工作原理
SQL SERVER 主要采用出版物、订阅的方式来处理复制。源数据所在的服务器是出版服务器,负责发表数据。出版服务器把要发表的数据的所有改变情况的拷贝复制到分发服务器,分发服务器包含有一个分发数据库,可接收数据的所有改变,并保存这些改变,再把这些改变分发给订阅服 务器
SQL SERVER复制技术类型
SQL SERVER提供了三种复制技术,分别是:
1、快照复制(呆会我们就使用这个)
2、事务复制
3、合并复制
只要把上面这些概念弄清楚了那么对复制也就有了一定的理解。接下来我们就一步一步来实现复制的步骤。
第一先来配置出版服务器
(1)选中指定[服务器]节点
(2)从[工具]下拉菜单的[复制]子菜单中选择[发布、订阅服务器和分发]命令
(3)系统弹出一个对话框点[下一步]然后看着提示一直操作到完成。
(4)当完成了出版服务器的设置以后系统会为该服务器的树形结构中添加一个复制监视器。同时也生成一个分发数据库(distribution)
第二创建出版物
(1)选中指定的服务器
(2)从[工具]菜单的[复制]子菜单中选择[创建和管理发布]命令。此时系统会弹出一个对话框
(3)选择要创建出版物的数据库,然后单击[创建发布]
(4)在[创建发布向导]的提示对话框中单击[下一步]系统就会弹出一个对话框。对话框上的内容是复制的三个类型。我们现在选第一个也就是默认的快照发布(其他两个大家可以去看看帮助)
(5)单击[下一步]系统要求指定可以订阅该发布的数据库服务器类型,SQLSERVER允许在不同的数据库如 ORACLE或ACCESS之间进行数据复制。但是在这里我们选择运行”SQL SERVER 2000″的数据库服务器
(6)单击[下一步]系统就弹出一个定义文章的对话框也就是选择要出版的表
(7)然后[下一步]直到操作完成。当完成出版物的创建后创建出版物的数据库也就变成了一个共享数据库。
第三设计订阅
(1)选中指定的订阅服务器
(2)从[工具]下拉菜单中选择[复制]子菜单的[请求订阅]
(3)按照单击[下一步]操作直到系统会提示检查SQL SERVER代理服务的运行状态,执行复制操作的前提条件是SQL SERVER代理服务必须已经启动。
(4)单击[完成]。完成订阅操作。
完成上面的步骤其实复制也就是成功了。但是如何来知道复制是否成功了呢?这里可以通过这种方法来快速看是否成功。展开出版服务器下面的复制——发布内容——右键发布内容——属性——击活——状态然后点立即运行代理程序接着点代理程序属性击活调度把调度设置 为每一天发生,每一分钟,在0:00:00和23:59:59之间。接下来就是判断复制是否成功了打开C:/Program Files/Microsoft SQL Server/MSSQL/REPLDATA/unc/XIAOWANGZI_database_database下面看是不是有一些以时间做为文件名的文件夹差不多一分中就产生一个。要是你还不信的话就打开你的数据库看在订阅的服务器的指定订阅数据库下 看是不是看到了你刚才所发布的表—
一个手工同步的方案
–定时同步服务器上的数据
–例子:
–测试环境,SQL Server2000,远程服务器名:xz,用户名为:sa,无密码,测试数据库:test
–服务器上的表(查询分析器连接到服务器上创建)
create table [user](id int primary key,number varchar(4),name varchar(10))
go
–以下在局域网(本机操作)
–本机的表,state说明:null 表示新增记录,1 表示修改过的记录,0 表示无变化的记录
if exists (select * from dbo.sysobjects where id = object_id(N’[user]‘) and OBJECTPROPERTY(id, N’IsUserTable’) = 1)
drop table [user]
GO
create table [user](id int identity(1,1),number varchar(4),name varchar(10),state bit)
go
–创建触发器,维护state字段的值
create trigger t_state on [user]
after update
as
update [user] set state=1
from [user] a join inserted b on a.id=b.id
where a.state is not null
go
–为了方便同步处理,创建链接服务器到要同步的服务器
–这里的远程服务器名为:xz,用户名为:sa,无密码
if exists(select 1 from master..sysservers where srvname=’srv_lnk’)
exec sp_dropserver ’srv_lnk’,'droplogins’
go
exec sp_addlinkedserver ’srv_lnk’,”,’SQLOLEDB’,'xz’
exec sp_addlinkedsrvlogin ’srv_lnk’,'false’,null,’sa’
go
–创建同步处理的存储过程
if exists (select * from dbo.sysobjects where id = object_id(N’[dbo].[p_synchro]‘) and OBJECTPROPERTY(id, N’IsProcedure’) = 1)
drop procedure [dbo].[p_synchro]
GO
create proc p_synchro
as
–set XACT_ABORT on
–启动远程服务器的MSDTC服务
–exec master..xp_cmdshell ‘isql /S”xz” /U”sa” /P”" /q”exec master..xp_cmdshell ”net start msdtc”,no_output”‘,no_output
–启动本机的MSDTC服务
–exec master..xp_cmdshell ‘net start msdtc’,no_output
–进行分布事务处理,如果表用标识列做主键,用下面的方法
–BEGIN DISTRIBUTED TRANSACTION
–同步删除的数据
delete from srv_lnk.test.dbo.[user]
where id not in(select id from [user])
–同步新增的数据
insert into srv_lnk.test.dbo.[user]
select id,number,name from [user] where state is null
–同步修改的数据
update srv_lnk.test.dbo.[user] set
number=b.number,name=b.name
from srv_lnk.test.dbo.[user] a
join [user] b on a.id=b.id
where b.state=1
–同步后更新本机的标志
update [user] set state=0 where isnull(state,1)=1
–COMMIT TRAN
go
–创建作业,定时执行数据同步的存储过程
if exists(SELECT 1 from msdb..sysjobs where name=’数据处理’)
EXECUTE msdb.dbo.sp_delete_job @job_name=’数据处理’
exec msdb..sp_add_job @job_name=’数据处理’
–创建作业步骤
declare @sql varchar(800),@dbname varchar(250)
select @sql=’exec p_synchro’ –数据处理的命令
,@dbname=db_name() –执行数据处理的数据库名
exec msdb..sp_add_jobstep @job_name=’数据处理’,
@step_name = ‘数据同步’,
@subsystem = ‘TSQL’,
@database_name=@dbname,
@command = @sql,
@retry_attempts = 5, –重试次数
@retry_interval = 5 –重试间隔
–创建调度
EXEC msdb..sp_add_jobschedule @job_name = ‘数据处理’,
@name = ‘时间安排’,
@freq_type = 4, –每天
@freq_interval = 1, –每天执行一次
@active_start_time = 00000 –0点执行
go

Posted by Wyulnnhtg at 07:33:17 | Permalink | No Comments »