printf的格式控制的完整格式:
% - 0 m.n l或h 格式字符下面对组成格式说明的各项加以说明: ①%:表示格式说明的起始符号,不可缺少。
②-:有-表示左对齐输出,如省略表示右对齐输出。
③0:有0表示指定空位填0,如省略表示指定空位不填。
④m.n:m指域宽,即对应的输出项在输出设备上所占的字符数。N指精度。用于说明输出的实型数的小数位数。为指定n时,隐含的精度为n=6位。
⑤l或h:l对整型指long型,对实型指double型。h用于将整型的格式字符修正为short型。
---------------------------------------
格式字符格式字符用以指定输出项的数据类型和输出格式。
①d格式:用来输出十进制整数。有以下几种用法: %d:按整型数据的实际长度输出。 %md:m为指定的输出字段的宽度。如果数据的位数小于m,则左端补以空格,若大于m,则按实际位数输出。 %ld:输出长整型数据。
②o格式:以无符号八进制形式输出整数。对长整型可以用"%lo"格式输出。同样也可以指定字段宽度用“%mo”格式输出。例: main() { int a = -1; printf("%d, %o", a, a); } 运行结果:-1,177777 程序解析:-1在内存单元中(以补码形式存放)为(1111111111111111)2,转换为八进制数为(177777)8。
③x格式:以无符号十六进制形式输出整数。对长整型可以用"%lx"格式输出。同样也可以指定字段宽度用"%mx"格式输出。
④u格式:以无符号十进制形式输出整数。对长整型可以用"%lu"格式输出。同样也可以指定字段宽度用“%mu”格式输出。
⑤c格式:输出一个字符。
⑥s格式:用来输出一个串。有几中用法 %s:例如:printf("%s", "CHINA")输出"CHINA"字符串(不包括双引号)。 %ms:输出的字符串占m列,如字符串本身长度大于m,则突破获m的限制,将字符串全部输出。若串长小于m,则左补空格。 %-ms:如果串长小于m,则在m列范围内,字符串向左靠,右补空格。 %m.ns:输出占m列,但只取字符串中左端n个字符。这n个字符输出在m列的右侧,左补空格。 %-m.ns:其中m、n含义同上,n个字符输出在m列范围的左侧,右补空格。如果n>m,则自动取n值,即保证n个字符正常输出。
⑦f格式:用来输出实数(包括单、双精度),以小数形式输出。有以下几种用法: %f:不指定宽度,整数部分全部输出并输出6位小数。 %m.nf:输出共占m列,其中有n位小数,如数值宽度小于m左端补空格。 %-m.nf:输出共占n列,其中有n位小数,如数值宽度小于m右端补空格。
⑧e格式:以指数形式输出实数。可用以下形式: %e:数字部分(又称尾数)输出6位小数,指数部分占5位或4位。 %m.ne和%-m.ne:m、n和”-”字符含义与前相同。此处n指数据的数字部分的小数位数,m表示整个输出数据所占的宽度。
⑨g格式:自动选f格式或e格式中较短的一种输出,且不输出无意义的零。
---------------------------------------
关于printf函数的进一步说明:如果想输出字符"%",则应该在“格式控制”字符串中用连续两个%表示,如: printf("%f%%", 1.0/3); 输出0.333333%。
---------------------------------------
对于单精度数,使用%f格式符输出时,仅前7位是有效数字,小数6位.对于双精度数,使用%lf格式符输出时,前16位是有效数字,小数6位.
对于m.n的格式还可以用如下方法表示(例)
char ch[20]; printf("%*.*s\n",m,n,ch);
前边的*定义的是总的宽度,后边的定义的是输出的个数。分别对应外面的参数m和n 。我想这种方法的好处是可以在语句之外对参数m和n赋值,从而控制输出格式。
1.空格作为scanf的参数
在程序员后面的皇后问题源程序中有这样一段代码
要注意我加上去的注释部分
if(k==n)/*找到一个解*/
{
printf("列\t行);
for(i=1;j<=n;j++)
printf("%3d\t%d\n",j,col[j]);
printf("enter a character(Q/q for exit)!\n");
scanf(" %c",&awn);/*注意这里scanf中的空格*/
if(awn==`Q`||awn==`q`)exit(0);
}
.
想必大家都能知道这段代码的功能,也就是说如果用户没有输入退出的条件(q或者Q)那么程序会将下一个皇后解都打出来,直到出现Q/q或者全部解输出完为止,问题就出在这里,scanf里面为什么要有一个空格呢?如果没有运行会是:当输入非退出条件会连续输出两个解,这是为什么?原因就在scanf。
空格在scanf函数中是一个参数,我们都知道c语言的输入输出流有三种(stdio,stdin,stderr),scanf函数是在键盘缓冲区取数据,当我们输入的数据在屏幕上显示的时候,实际上数据还在键盘缓冲区,当用户输入回车键的时候计算机会把缓冲区的数据(包括回车)输入到内存供程序的执行。而参 数%c是接受字符的,当他遇到回车scanf也能正常接收。
假设现在scanf没有空格
分析:现在我们再来看看我们的程序:假设 现在输入y继续执行程序函数scanf正确接收到了y,循环继续,但是下一个字符应该是回车,scanf函数循环接收到的回车!=q/Q,这样循环又继续 一次此时没有数据,那么计算机就等待用户输入数据。就这样用户输入一个数据就会出现两次,这是我们程序员不希望看到的。
解决:解决就是在%c的前面加上一个空格,空格是一个参数,它可以在scanf接收数据之前屏蔽回车(\n),这样就能达到我们的预想效果。
2.输入特定字符集
scanf("%[]",str)可以实现只读入中括号[]中的字符,遇到非指定的字符就停止输入。
例如,scanf("%[0-9]",str);只读入0-9的数字,遇到其它字符返回。scanf("%[^\n]",str),读入回车符外的所有字符,用此语句可以实现输入带空格的字符串。
1.交换两个字符位置
xp
其实就是删除光标当前字符(x),然后再将缓存的字符贴出(p)
2.上下两行调换
ddp
实际就是(dd)删除当前行,(p)后即将缓存的行贴出
3.把文件内容反转
:g/^/m0/ (未通过)
这个貌似没写全啊
4.上下两行合并
J
实用性:高,shift+j
5.删除所有行
dG
错!(d)为删除,(shift+g)到文件尾,因此这命令效用为从光标处删除至文件尾
那么删除到文首呢?对了,如果知道gg可以将光标移至文首,那么dgg便是删除到文首了
类似的还有:
dw -- 删到词尾
db -- 删到词头
daw -- 删除光标所在词(较有用)
如果你用 "c" 代替 "d",这会变成修改命令;而改用 "y",则变成拷贝命令,等等等等。
6.从当前位置删除到行尾
d$
(d)删除,$ (即 shift+4)为行尾,^( 即 shift+6)为行头,依此类推,d^删除至行头
更为简洁的命令为D,对了,就是shift+d,就这么简单
7.从当前位置复制到行尾
y$ 如果要粘贴到其他地方 p 就可以了
同上
由于vi 是建立在 EX 上的 所以 当键入 : 时就来到了 EX 命令状态
8.
:ab string strings
例如 ":ab usa United States of America" ,
当你在文件里插入 usa 时
United States of America 就蹦出来了
其实执行的是":abbreviate"命令,用":unabbreviate"命令解除缩写,要删除全部缩写用":abclear"
9.
:map keys new_seq
定义你当前 键盘命令
键映射,我前面的tips有用到
10.
:set [all]
vi or ex 的编辑状态
如 显示每行 :set nu
set,就是set,不显示行号":se nonu"
11.
在命令状态下,nyy表示拷贝从光标行起的下n行内容,p表示paste,可刚复制的内容粘贴在光标处的
下面。
讲的太不清楚了,n用数字代替,即将后面的指令重复执行n次,yy为复制,nyy复制n行
12.
单个字符替换用r,覆盖多个字符用R,用多个字符替换一个字符用s,整行替换用S
实用性:高,S等于我常用的cc或C
13.
:%s/old_word/new_word/g
这个指令是于在整个文件中替换特定字符串
替换,用法是这样:":起始行,结束行s/搜索串/替换串/gc" 从起始行到结束行,把所有的搜索串替换为替换串,最后那个"c"为开启交互式替换,%为全文
14.光标控制
k:上移 nk 上移n行
j:下移 nj 下移n行
光标移动,还有h:左移 l:右移,不过我更喜欢用上、下、左、右,嘿嘿
将光标移到第n行,按下 mk
将光标移到第m行,按下 "ay'k
即将第n到m的行存到a寄存器,以此类推,b,c........寄存器等
这样就可以将你常用的需要复用的内容粘贴到不同的寄存器中以备用
想粘贴到某处,直接将光标移到某地,按下 'ap 即可,以此类推,b,c........寄存器等
在当前屏幕中
H 跳到第一行
M 跳到中间一行
L 跳到最后一行
当前屏幕的光标控制技巧,助记方法为:Head、Middle、Last
15.
表8-2 删除命令
删除命令操作
d l 删除当前字符(与x命令功能相同)
d 0 删除到某一行的开始位置
d ^ 删除到某一行的第一个字符位置(不包括空格或TA B字符)
d w 删除到某个单词的结尾位置
d 3 w 删除到第三个单词的结尾位置
d b 删除到某个单词的开始位置
d W 删除到某个以空格作为分隔符的单词的结尾位置
d B 删除到某个以空格作为分隔符的单词的开始位置
d 7 B 删除到前面7个以空格作为分隔符的单词的开始位置
d) 删除到某个语句的结尾位置
d 4) 删除到第四个语句的结尾位置
d( 删除到某个语句的开始位置
d } 删除到某个段落的结尾位置
d { 删除到某个段落的开始位置
d 7 { 删除到当前段落起始位置之前的第7个段落位置
d d 删除当前行
d /t e x t 删除从文本中出现" t e x t"中所指定字样的位置,一直向前直到下一个该字样所出现的
位置(但不包括该字样)之间的内容
d fc 删除从文本中出现字符"c"的位置,一直向前直到下一个该字符所出现的位置(包括
该字符)之间的内容
d tc 删除当前行直到下一个字符" c"所出现位置之间的内容
D 删除到某一行的结尾
d $ 删除到某一行的结尾
5 d d 删除从当前行所开始的5行内容
d L 删除直到屏幕上最后一行的内容
d H 删除直到屏幕上第一行的内容
d G 删除直到工作缓存区结尾的内容
d 1 G 删除直到工作缓存区开始的内容
修改命令操作
c l 更改当前字符
c w 修改到某个单词的结尾位置
c 3 w 修改到第三个单词的结尾位置
c b 修改到某个单词的开始位置
c W 修改到某个以空格作为分隔符的单词的结尾位置
c B 修改到某个以空格作为分隔符的单词的开始位置
c 7 B 修改到前面7个以空格作为分隔符的单词的开始位置
c 0 修改到某行的结尾位置
c) 修改到某个语句的结尾位置
c 4) 修改到第四个语句的结尾位置
c( 修改到某个语句的开始位置
c } 修改到某个段落的结尾位置
c { 修改到某个段落的开始位置
c 7 { 修改到当前段落起始位置之前的第7个段落位置
c tc 修改当前行直到下一个字符c所出现位置之间的内容
C 修改到某一行的结尾
c c 修改当前行
5 c c 修改从当前行所开始的5行内容
.重复上一次修改!
表8-4 替换命令
替换命令操作
s 将当前字符替换为一个或多个字符
S 将当前行替换为一个或多个字符
5 s 将从当前字符开始的5个字符替换为一个或多个字符
vi替换使用规则:
:g/s1/s/s2/s3/g
第一个g表示对每一个包括s1的行都进行替换,第二个g表示对每一行包括s1的行所有的s2都用s3替换
s表示替换,s2是要被替换的字符串,他可以和s1相同(如果相同的话用//代替),s3是替换字符串
16.
fx
往右移动到 x 字符上
Fx
往左移动到 x 字符上
tx
往右移动到 x 字符前
Tx
往左移动到 x 字符后
(注意:以上四个命令中,其中x是键入的字符)
;
分号,配合 f 和 t 使用,重复一次
,
逗号,配合 f 和 t 使用,反方向重复一次
如果不结合;与,很不好用,nfx为移到第n个x字符上
17. vi 环境选项 Solaris ksh
noautoindent nomodelines noshowmode
autoprint nonumber noslowopen
noautowrite nonovice tabstop=8
nobeautify nooptimize taglength=0
directory=/var/tmp paragraphs=IPLPPPQPP LIpplpipnpbtags=tags /usr/lib/tags
noedcompatible prompt tagstack
noerrorbells noreadonly term=vt100
noexrc redraw noterse
flash remap timeout
hardtabs=8 report=5 ttytype=vt100
noignorecase scroll=11 warn
nolisp sections=NHSHH HUuhsh+c window=23
nolist shell=/bin/ksh wrapscan
magic shiftwidth=8 wrapmargin=0
mesg noshowmatch nowriteany
For C-Shell:
setenv EXINIT "set nu"
For Bourne or Korn Shell:
EXINIT="set nu"; export EXINIT
For Korn Shell Only (alternate method):
typeset -x EXINIT="set nu"
在 .profile 里设置 vi 的环境选项 , 以上均测试过
以上的查阅vim帮助文档,太多了不一一解释了
18.标记文本
mchar 用字母char标记当前光标的位置
`char 移至char所标记处
'char 移至char标记所在行的开头处
" 移至当前行上一次所在位置(在光标移动之后)――一个双引号
'' 移至当前行上第一次所在位置的行的开头处(在光标移动之后)――两个单引号0.
头两个和最后一个就已经挺好用的了
19.
同时vi多个文件时,CTRL-SHIFT-6回到上一个文件,在本次vi的文件和上次vi的文件之间切换。
但是我发现一个BUG:在用CTRL-SHIFT-6切换到上一个文件后,用:args查看多文件vi状态时,
屏幕底部仍然显示目前vi的是刚才的文件。
(在HP-UX,Solaris,AIX上通过)
也可以使用:
:e#
进行切换
ctrl+shift+6=:e#回到上一个文件,哇哈哈,终于找到了,对应的是:next简写为:n到下个文件,用files貌似更能看清当前打开的文件列表
20.
sco 下VI 要在文本前同样的字符加用
%s/^/要加的内容/g 要在文本后同样的字符加
%s/$/要加的内容/g
汗,这样利用查找替换
21.
如何去掉文本中的 ^M 硬回车?不必用binary传回去再ascii传回来的方式,用shell或者unix语句实现。
cat filename |tr -d '\015' >newfile
不同的unix系统还存在一些其他不同的命令,如:doscp
sed 也可以实现这个功能.
dos2unix filename filename2
反之
unix2dos filename filename2
在vi 中用:$s/^M//g
^是crtl-V crtl-M
我常用的是dos2unix
22.如何在"unix命令行"下将一个文件的某字符串用另一个串换掉
sed 's/string1/string2/gp' file1 > file2
23.将/etc/hosts下所有的地址都ping 2次
1 #/usr/bin/sh
2 #grad /etc/hosts and ping each address
3 cat /etc/hosts|grep -v '^#' | while read LINE
4 do
5 ADDR=`awk '{print $1}'`
6 for MACHINE in $ADDR
7 do
8 ping $MACHINE -n 2
9 done
10 done
24.
到前一个函数[[ ,到下一个函数]] ,括号配对% ,交叉参考Ctrl_] (事先用ctags做索引),回来用e# ` 编辑一个函数:vi -t 函数名 ,编辑加密文本vi -X
%括号匹配我介绍过,:e#回来可以用ctrl+o替代,更优雅
25.
在插入模式下ctrl+p,自动补齐剩余单词,以赖规则:tags,以有的单词等等
还ctrl_n呢
例一、两个常用的指令序列
xp 左右交换光标处两字符的位置。
ddp 上下交换光标处两行的位置。
例二、重复输入同一字符
有时,我们可能想多次输入同一字符,VIM的插入功能可以很好的完成这项工作
命令 80i=^ESC 一次可以输入80个字符= ,当然,80a=^ESC 也可以完成上述功能。
请注意:此处的^ESC表示键盘左上方上的ESC键。
例三、将两个文本数据文件按行逐条合并,并给出标尺
数据文件1内容如下:
1-----
2-----
3-----
数据文件2内容如下:
1=====
2=====
3=====
要求的结果如下:
|--------1---------2---------3---------4---------5
1-----
1=====
|--------1---------2---------3---------4---------5
2-----
2=====
|--------1---------2---------3---------4---------5
3-----
3=====
也许您会说,这还不简单,无非是反复拷贝、粘贴,任何一款文本编辑器都能完成上述功能。可是,如果这两个文件都很大,每个文件都成千上万行,恐怕简单的拷贝、粘贴就难以胜任了。因此,我们所关心的,是找到一种行之有效的方法,把枯燥乏味的工作留给计算机,我们只需发布指令。为达到此目的,请按以下步骤执行:
㈠、将两文件合并,结果如下
1-----
2-----
3-----
1=====
2=====
3=====
㈡、在两文件头尾相接的地方插入标志行,用以区分两个文件,本文采用的是一整行!字符
1-----
2-----
3-----
!!!!!!!!!!!!!!!!!!!!!!!!
1=====
2=====
3=====
㈢、在标志行的下方输入标尺
1-----
2-----
3-----
!!!!!!!!!!!!!!!!!!!!!!!!
|--------1---------2---------3---------4---------5
1=====
2=====
3=====
㈣、执行宏命令脚本merge_2r.vim,即在VIM编辑器中按如下键 :so merge_2r.vim 回车
㈤、按下键盘上的=键,执行的结果如下
|--------1---------2---------3---------4---------5
1-----
1=====
|--------1---------2---------3---------4---------5
2-----
2=====
|--------1---------2---------3---------4---------5
3-----
3=====
|--------1---------2---------3---------4---------5
!!!!!!!!!!!!!!!!!!!!!!!!
|--------1---------2---------3---------4---------5
㈥、将最后三行删除,即可得到我们需要的结果
|--------1---------2---------3---------4---------5
1-----
1=====
|--------1---------2---------3---------4---------5
2-----
2=====
|--------1---------2---------3---------4---------5
3-----
3=====
怎么样,简单吗?请大家自己实际尝试一下。下面,我来详细讲解宏命令脚本merge_2r.vim 。
该脚本内容如下:
"--------------------------------------------------------------------
"Macro Function : Merge File1 And File2,Have Ruler in every record
" Date : 2001/12/01
" Author : Yan Shi
"--------------------------------------------------------------------
"1-----
"2----- } Sample File1
"3-----
"!!!!!!!!!!!!!!!!!!!!!!!! Flag Row
"|--------1---------2---------3---------4---------5 Ruler
"1=====
"2===== } Sample File2
"3=====
"--------------------------------------------------------------------
:1
:map = ma/!!!!!^M+:.co 'a-1^M/!!!!!^M2+:.m'a^M+=
前14行每行都以"开始,表明该行是注释行,实际并不执行,只是方便读者阅读,只有最后两行才是真正的代码行。请注意:本例中的^M表示键盘上的回车键,并非^和M两个字符。为了讲述清楚,我把命令行分解开,逐一说明。
首先将第一行置为当前行,然后执行map命令,将一大串VIM指令映像给字符=。这一大串VIM指令共分9步执行:
ma 将数据文件一的第一行标记为a
1-----
2-----
3-----
!!!!!!!!!!!!!!!!!!!!!!!!
|--------1---------2---------3---------4---------5
1=====
2=====
3=====
/!!!!!^M 找到标志行,置为当前行
1-----
2-----
3-----
!!!!!!!!!!!!!!!!!!!!!!!!
|--------1---------2---------3---------4---------5
1=====
2=====
3=====
+ 光标下移一行,即把标尺行置为当前行
1-----
2-----
3-----
!!!!!!!!!!!!!!!!!!!!!!!!
|--------1---------2---------3---------4---------5
1=====
2=====
3=====
:.co 'a-1^M 把标尺行复制到标记行(数据文件一的第一行)的上方
|--------1---------2---------3---------4---------5
1-----
2-----
3-----
!!!!!!!!!!!!!!!!!!!!!!!!
|--------1---------2---------3---------4---------5
1=====
2=====
3=====
/!!!!!^M 再次找到标志行,置为当前行
|--------1---------2---------3---------4---------5
1-----
2-----
3-----
!!!!!!!!!!!!!!!!!!!!!!!!
|--------1---------2---------3---------4---------5
1=====
2=====
3=====
2+ 光标下移2行,即数据文件二的第一行置为当前行
|--------1---------2---------3---------4---------5
1-----
2-----
3-----
!!!!!!!!!!!!!!!!!!!!!!!!
|--------1---------2---------3---------4---------5
1=====
2=====
3=====
:.m'a^M 把数据文件二的第一行移至标记行的下方
|--------1---------2---------3---------4---------5
1-----
1=====
2-----
3-----
!!!!!!!!!!!!!!!!!!!!!!!!
|--------1---------2---------3---------4---------5
2=====
3=====
+ 光标下移一行,即数据文件一的第二行置为当前行
|--------1---------2---------3---------4---------5
1-----
1=====
2-----
3-----
!!!!!!!!!!!!!!!!!!!!!!!!
|--------1---------2---------3---------4---------5
2=====
3=====
= 这一步很关键,是典型的递归调用,重复完成以上步骤
例四、在文件中置入行号
工作中,我们有时希望把行号置入文件中,而VIM提供的功能 :set nu 只能显示行号,不能编辑或将其置入文件当中,下面的宏命令脚本row_num.vim可以完成此项功能。
"------------------------------------------
"Macro Function : Source File Add Row_Num
" Date : 2001/12/01
" Author : Yan Shi
"------------------------------------------
:%s/^/^I/
:$
:let end=line(".")
:1
"------------------------------------------
:let num=1
:while num<=end
:let line=getline(".")
:let temp=substitute(line,$,num,"")
:call setline(".",temp)
:+
:let num=num+1
:endwhile
"------------------------------------------
请注意:本例中的^I表示键盘上的TAB键,并非^和I两个字符。下面,我针对该宏命令脚本逐一讲解。
:%s/^/^I/ 每一行的行首添加一个TAB字符
:$ 到文件的末行
:let end=line(".") 末行的行号 ==〉变量 END,函数line的功能是取得指定行的行号,此处参数"."表示当前行
:1 到文件的首行
"------------------------------------------
:let num=1 1 ==〉计数器
:while num<=end
:let line=getline(".") 取当前行的内容 ==〉变量 LINE
:let line=substitute(line,$,num,"") 在变量 LINE 的前面置入行号
:call setline(".",line) 将变量 LINE 的内容写回当前行
:+ 下移一行
:let num=num+1 计数器加一
:endwhile 循环执行,直到文件结束
"------------------------------------------
真正的高级技巧,不过貌似太高了,一般用不着……
有关正则表达式的使用
UNIX/LINUX 下的很多工具之所以强大、灵活,关键是因为有了正则文法和元字符,这也是VIM乃至UNIX/LINUX系统的精华所在。正因为使用灵活,因此掌握起来比较吃力,如果不是真正理解,实际运用中会出现千奇百怪的错误。因此,有必要对这部分知识多花些气力。下面结合具体实例讲解。
例五、有一文件,包含某外企的中国员工的资料,首先是姓名,然后是两个空格,其次是15位身份证号码。
zhang.fei 430759701022003
diao.chan 651302801225012
guan.yu 342869680413001
xi.shi 120638780214006
liu.bei 210324650708001
现在,有以下问题需要解决:
按照外国人的习惯,应该是名在前,姓在后。因此,文件中的姓名字段需要修改。
姓与名的首字母应该大写。
根据身份证号码,还可以判断出生年月日,将其作为一个新字段添加。
根据身份证号码,可以判断出性别。若为男性,添加male,若为女性,添加female 。
将男女员工分开,男员工在前,女员工在后。
将各字段数据左对齐
最终结果如下:
Fei.Zhang 430759701022003 1970/10/22 male
Yu.Guan 342869680413001 1968/04/13 male
Bei.Liu 210324650708001 1965/07/08 male
-----------------------------------------------
Chan.Diao 651302801225012 1980/12/25 female
Shi.Xi 120638780214006 1978/02/14 female
为了完成上述功能,只需执行脚本employee.vim ,使用方法为 :so employee.vim 回车即可。
脚本内容如下:
:%s/ / /
:%s/\(............\)\( *\)/\1/
:%s/\([A-Za-z][A-Za-z]*\)\(\.\)\([A-Za-z][A-Za-z]*\)/\u\3\2\u\1/
:%s/$/ xxxxxx/
:%s/\([0-9]\{6}\)\([0-9]\{6}\)\([0-9]\{3}\) \(xxxxxx\)/\1\2\3 \2/
:%s/\(..\)\(..\)\(..\)$/19\1\/\2\/\3
:%s/$/ xxxxxx/
:%s/\([0-9]\{14}[13579]\)\(.*\)\(xxxxxx\)/\1\2male /
:%s/\([0-9]\{14}[02468]\)\(.*\)\(xxxxxx\)/\1\2female/
:$
:s/.*/&^M-----------------------------------------------
:g/female/.m$
在这个脚本中,使用了大量的正则表达式,这里仅对涉及到的正则表达式做一简要介绍。有关正则表达式的内容相当多,本文不可能占用大量篇幅叙述,请大家谅解。
% 地址范围符号,代表文件中的所有行,作用等同于地址范围 1,$
. 与任意单字符(换行符除外)匹配,例如 y.s 可以匹配 yas y.s 或 y s 等等。
* 与前一字符的0次或多次出现匹配,例如 y*s 可以匹配 yys yyyyys 或 s 等等。
$ 与行尾匹配。
& 代表模式匹配中出现的字符串,例如 s/abc/&def 是把当前行的abc替换成abcdef 。
[] 匹配[]中出现的字符,例如[abc]匹配字符 a,b 或 c ,[a-zA-Z]匹配所有的英文字符。
\( \) \(和\)之间出现的内容可以由\num来替代。
\1\2\3 替代\(和\)之间出现的内容。
\u 将后续字符串的首字母大写。
\{num} 与前一字符的num次出现匹配。
现在,我们对脚本逐条讲解,希望能帮助大家理解正则文法。
⑴:%s/ / /
将文件中每行出现的2个空格替换为10个空格。
zhang.fei 430759701022003
diao.chan 651302801225012
guan.yu 342869680413001
xi.shi 120638780214006
liu.bei 210324650708001
⑵:%s/\(............\)\( *\)/\1/
保留行首的12个字符,将其余的空格删除,这样,前两个字段就对齐了。
zhang.fei 430759701022003
diao.chan 651302801225012
guan.yu 342869680413001
xi.shi 120638780214006
liu.bei 210324650708001
⑶:%s/\([A-Za-z][A-Za-z]*\)\(\.\)\([A-Za-z][A-Za-z]*\)/\u\3\2\u\1/
将文件中每行出现的雇员姓名互换,并将首字母大写。
Fei.Zhang 430759701022003
Chan.Diao 651302801225012
Yu.Guan 342869680413001
Shi.Xi 120638780214006
Bei.Liu 210324650708001
⑷:%s/$/ xxxxxx/
在每一行的行尾添加2个空格和6个x
Fei.Zhang 430759701022003 xxxxxx
Chan.Diao 651302801225012 xxxxxx
Yu.Guan 342869680413001 xxxxxx
Shi.Xi 120638780214006 xxxxxx
Bei.Liu 210324650708001 xxxxxx
⑸:%s/\([0-9]\{6}\)\([0-9]\{6}\)\([0-9]\{3}\) \(xxxxxx\)/\1\2\3 \2/
将xxxxxx替换成出生年月日。
Fei.Zhang 430759701022003 701022
Chan.Diao 651302801225012 801225
Yu.Guan 342869680413001 680413
Shi.Xi 120638780214006 780214
Bei.Liu 210324650708001 650708
⑹:%s/\(..\)\(..\)\(..\)$/19\1\/\2\/\3
将年月日用/字符分隔,并在年前添加19。
Fei.Zhang 430759701022003 1970/10/22
Chan.Diao 651302801225012 1980/12/25
Yu.Guan 342869680413001 1968/04/13
Shi.Xi 120638780214006 1978/02/14
Bei.Liu 210324650708001 1965/07/08
⑺:%s/$/ xxxxxx/
在每一行的行尾添加2个空格和6个x
Fei.Zhang 430759701022003 1970/10/22 xxxxxx
Chan.Diao 651302801225012 1980/12/25 xxxxxx
Yu.Guan 342869680413001 1968/04/13 xxxxxx
Shi.Xi 120638780214006 1978/02/14 xxxxxx
Bei.Liu 210324650708001 1965/07/08 xxxxxx
⑻:%s/\([0-9]\{14}[13579]\)\(.*\)\(xxxxxx\)/\1\2male /
身份证号码末位是奇数的,将xxxxxx替换成male
Fei.Zhang 430759701022003 1970/10/22 male
Chan.Diao 651302801225012 1980/12/25 xxxxxx
Yu.Guan 342869680413001 1968/04/13 male
Shi.Xi 120638780214006 1978/02/14 xxxxxx
Bei.Liu 210324650708001 1965/07/08 male
⑼:%s/\([0-9]\{14}[02468]\)\(.*\)\(xxxxxx\)/\1\2female/
身份证号码末位是偶数的,将xxxxxx替换成female
Fei.Zhang 430759701022003 1970/10/22 male
Chan.Diao 651302801225012 1980/12/25 female
Yu.Guan 342869680413001 1968/04/13 male
Shi.Xi 120638780214006 1978/02/14 female
Bei.Liu 210324650708001 1965/07/08 male
⑽:$ 到文件的最后一行
⑾:s/.*/&^M-----------------------------------------------
在文件的最末行插入一行 "-" 字符。
Fei.Zhang 430759701022003 1970/10/22 male
Chan.Diao 651302801225012 1980/12/25 female
Yu.Guan 342869680413001 1968/04/13 male
Shi.Xi 120638780214006 1978/02/14 female
Bei.Liu 210324650708001 1965/07/08 male
-----------------------------------------------
⑿:g/female/.m$
将所有的女员工记录移至文件尾。
Fei.Zhang 430759701022003 1970/10/22 male
Yu.Guan 342869680413001 1968/04/13 male
Bei.Liu 210324650708001 1965/07/08 male
-----------------------------------------------
Chan.Diao 651302801225012 1980/12/25 female
Shi.Xi 120638780214006 1978/02/14 female
笔者目前正在为某外资公司从事大型机(IBM S/390)的软件开发,一切工作都在TSO环境中进行。为了对编写的程序进行测试,必须准备测试数据。有过大型机开发经验的人会知道,通过TSO,输入字符型数据还可以,如果要输入16进制数据,操作起来很麻烦。因为16进制数是纵向排列的,输入时既不方便,又很容易错位。怎么解决呢?我尝试了几种办法,实际证明,用VIM最方便。
例六、下列数据 1234567890ABCDEF ,将其变成 13579ACE 24680BDF 的形式,这样,数据就可以很方便的粘贴到TSO环境中了。
下面给出宏命令脚本change_d.vim
"----------------------------------------------------
"Macro Function : Convert Char Arrange Direction
"
" Sample : 40 50 60 ==> 4 5 6
" 0 0 0
" Date : 2001/12/01
" Author : Yan Shi
"----------------------------------------------------
:s/.*/&^M/
:1
:map = malx+$p-`al=
说明如下:
⑴ :s/.*/&^M/ 在数据行下方添加一空行。
⑵ :1 回到文件的首行的首字符。
⑶ :map = malx+$p-`al= 将一大串VIM命令映像给字符=
① ma 将首字符标记为a
② l 光标右移一个字符
③ x 删除光标处字符
④ + 移至下一行
⑤ $ 到行尾
⑥ p 将删除的字符粘贴
⑦ - 回至上一行
⑧ `a 返回到标记字符处
⑨ l 光标右移一个字符
⑩ = 递归调用,重复以上步骤,直到将该行所有的数据处理完。
xp
其实就是删除光标当前字符(x),然后再将缓存的字符贴出(p)
2.上下两行调换
ddp
实际就是(dd)删除当前行,(p)后即将缓存的行贴出
3.把文件内容反转
:g/^/m0/ (未通过)
这个貌似没写全啊
4.上下两行合并
J
实用性:高,shift+j
5.删除所有行
dG
错!(d)为删除,(shift+g)到文件尾,因此这命令效用为从光标处删除至文件尾
那么删除到文首呢?对了,如果知道gg可以将光标移至文首,那么dgg便是删除到文首了
类似的还有:
dw -- 删到词尾
db -- 删到词头
daw -- 删除光标所在词(较有用)
如果你用 "c" 代替 "d",这会变成修改命令;而改用 "y",则变成拷贝命令,等等等等。
6.从当前位置删除到行尾
d$
(d)删除,$ (即 shift+4)为行尾,^( 即 shift+6)为行头,依此类推,d^删除至行头
更为简洁的命令为D,对了,就是shift+d,就这么简单
7.从当前位置复制到行尾
y$ 如果要粘贴到其他地方 p 就可以了
同上
由于vi 是建立在 EX 上的 所以 当键入 : 时就来到了 EX 命令状态
8.
:ab string strings
例如 ":ab usa United States of America" ,
当你在文件里插入 usa 时
United States of America 就蹦出来了
其实执行的是":abbreviate"命令,用":unabbreviate"命令解除缩写,要删除全部缩写用":abclear"
9.
:map keys new_seq
定义你当前 键盘命令
键映射,我前面的tips有用到
10.
:set [all]
vi or ex 的编辑状态
如 显示每行 :set nu
set,就是set,不显示行号":se nonu"
11.
在命令状态下,nyy表示拷贝从光标行起的下n行内容,p表示paste,可刚复制的内容粘贴在光标处的
下面。
讲的太不清楚了,n用数字代替,即将后面的指令重复执行n次,yy为复制,nyy复制n行
12.
单个字符替换用r,覆盖多个字符用R,用多个字符替换一个字符用s,整行替换用S
实用性:高,S等于我常用的cc或C
13.
:%s/old_word/new_word/g
这个指令是于在整个文件中替换特定字符串
替换,用法是这样:":起始行,结束行s/搜索串/替换串/gc" 从起始行到结束行,把所有的搜索串替换为替换串,最后那个"c"为开启交互式替换,%为全文
14.光标控制
k:上移 nk 上移n行
j:下移 nj 下移n行
光标移动,还有h:左移 l:右移,不过我更喜欢用上、下、左、右,嘿嘿
将光标移到第n行,按下 mk
将光标移到第m行,按下 "ay'k
即将第n到m的行存到a寄存器,以此类推,b,c........寄存器等
这样就可以将你常用的需要复用的内容粘贴到不同的寄存器中以备用
想粘贴到某处,直接将光标移到某地,按下 'ap 即可,以此类推,b,c........寄存器等
在当前屏幕中
H 跳到第一行
M 跳到中间一行
L 跳到最后一行
当前屏幕的光标控制技巧,助记方法为:Head、Middle、Last
15.
表8-2 删除命令
删除命令操作
d l 删除当前字符(与x命令功能相同)
d 0 删除到某一行的开始位置
d ^ 删除到某一行的第一个字符位置(不包括空格或TA B字符)
d w 删除到某个单词的结尾位置
d 3 w 删除到第三个单词的结尾位置
d b 删除到某个单词的开始位置
d W 删除到某个以空格作为分隔符的单词的结尾位置
d B 删除到某个以空格作为分隔符的单词的开始位置
d 7 B 删除到前面7个以空格作为分隔符的单词的开始位置
d) 删除到某个语句的结尾位置
d 4) 删除到第四个语句的结尾位置
d( 删除到某个语句的开始位置
d } 删除到某个段落的结尾位置
d { 删除到某个段落的开始位置
d 7 { 删除到当前段落起始位置之前的第7个段落位置
d d 删除当前行
d /t e x t 删除从文本中出现" t e x t"中所指定字样的位置,一直向前直到下一个该字样所出现的
位置(但不包括该字样)之间的内容
d fc 删除从文本中出现字符"c"的位置,一直向前直到下一个该字符所出现的位置(包括
该字符)之间的内容
d tc 删除当前行直到下一个字符" c"所出现位置之间的内容
D 删除到某一行的结尾
d $ 删除到某一行的结尾
5 d d 删除从当前行所开始的5行内容
d L 删除直到屏幕上最后一行的内容
d H 删除直到屏幕上第一行的内容
d G 删除直到工作缓存区结尾的内容
d 1 G 删除直到工作缓存区开始的内容
修改命令操作
c l 更改当前字符
c w 修改到某个单词的结尾位置
c 3 w 修改到第三个单词的结尾位置
c b 修改到某个单词的开始位置
c W 修改到某个以空格作为分隔符的单词的结尾位置
c B 修改到某个以空格作为分隔符的单词的开始位置
c 7 B 修改到前面7个以空格作为分隔符的单词的开始位置
c 0 修改到某行的结尾位置
c) 修改到某个语句的结尾位置
c 4) 修改到第四个语句的结尾位置
c( 修改到某个语句的开始位置
c } 修改到某个段落的结尾位置
c { 修改到某个段落的开始位置
c 7 { 修改到当前段落起始位置之前的第7个段落位置
c tc 修改当前行直到下一个字符c所出现位置之间的内容
C 修改到某一行的结尾
c c 修改当前行
5 c c 修改从当前行所开始的5行内容
.重复上一次修改!
表8-4 替换命令
替换命令操作
s 将当前字符替换为一个或多个字符
S 将当前行替换为一个或多个字符
5 s 将从当前字符开始的5个字符替换为一个或多个字符
vi替换使用规则:
:g/s1/s/s2/s3/g
第一个g表示对每一个包括s1的行都进行替换,第二个g表示对每一行包括s1的行所有的s2都用s3替换
s表示替换,s2是要被替换的字符串,他可以和s1相同(如果相同的话用//代替),s3是替换字符串
16.
fx
往右移动到 x 字符上
Fx
往左移动到 x 字符上
tx
往右移动到 x 字符前
Tx
往左移动到 x 字符后
(注意:以上四个命令中,其中x是键入的字符)
;
分号,配合 f 和 t 使用,重复一次
,
逗号,配合 f 和 t 使用,反方向重复一次
如果不结合;与,很不好用,nfx为移到第n个x字符上
17. vi 环境选项 Solaris ksh
noautoindent nomodelines noshowmode
autoprint nonumber noslowopen
noautowrite nonovice tabstop=8
nobeautify nooptimize taglength=0
directory=/var/tmp paragraphs=IPLPPPQPP LIpplpipnpbtags=tags /usr/lib/tags
noedcompatible prompt tagstack
noerrorbells noreadonly term=vt100
noexrc redraw noterse
flash remap timeout
hardtabs=8 report=5 ttytype=vt100
noignorecase scroll=11 warn
nolisp sections=NHSHH HUuhsh+c window=23
nolist shell=/bin/ksh wrapscan
magic shiftwidth=8 wrapmargin=0
mesg noshowmatch nowriteany
For C-Shell:
setenv EXINIT "set nu"
For Bourne or Korn Shell:
EXINIT="set nu"; export EXINIT
For Korn Shell Only (alternate method):
typeset -x EXINIT="set nu"
在 .profile 里设置 vi 的环境选项 , 以上均测试过
以上的查阅vim帮助文档,太多了不一一解释了
18.标记文本
mchar 用字母char标记当前光标的位置
`char 移至char所标记处
'char 移至char标记所在行的开头处
" 移至当前行上一次所在位置(在光标移动之后)――一个双引号
'' 移至当前行上第一次所在位置的行的开头处(在光标移动之后)――两个单引号0.
头两个和最后一个就已经挺好用的了
19.
同时vi多个文件时,CTRL-SHIFT-6回到上一个文件,在本次vi的文件和上次vi的文件之间切换。
但是我发现一个BUG:在用CTRL-SHIFT-6切换到上一个文件后,用:args查看多文件vi状态时,
屏幕底部仍然显示目前vi的是刚才的文件。
(在HP-UX,Solaris,AIX上通过)
也可以使用:
:e#
进行切换
ctrl+shift+6=:e#回到上一个文件,哇哈哈,终于找到了,对应的是:next简写为:n到下个文件,用files貌似更能看清当前打开的文件列表
20.
sco 下VI 要在文本前同样的字符加用
%s/^/要加的内容/g 要在文本后同样的字符加
%s/$/要加的内容/g
汗,这样利用查找替换
21.
如何去掉文本中的 ^M 硬回车?不必用binary传回去再ascii传回来的方式,用shell或者unix语句实现。
cat filename |tr -d '\015' >newfile
不同的unix系统还存在一些其他不同的命令,如:doscp
sed 也可以实现这个功能.
dos2unix filename filename2
反之
unix2dos filename filename2
在vi 中用:$s/^M//g
^是crtl-V crtl-M
我常用的是dos2unix
22.如何在"unix命令行"下将一个文件的某字符串用另一个串换掉
sed 's/string1/string2/gp' file1 > file2
23.将/etc/hosts下所有的地址都ping 2次
1 #/usr/bin/sh
2 #grad /etc/hosts and ping each address
3 cat /etc/hosts|grep -v '^#' | while read LINE
4 do
5 ADDR=`awk '{print $1}'`
6 for MACHINE in $ADDR
7 do
8 ping $MACHINE -n 2
9 done
10 done
24.
到前一个函数[[ ,到下一个函数]] ,括号配对% ,交叉参考Ctrl_] (事先用ctags做索引),回来用e# ` 编辑一个函数:vi -t 函数名 ,编辑加密文本vi -X
%括号匹配我介绍过,:e#回来可以用ctrl+o替代,更优雅
25.
在插入模式下ctrl+p,自动补齐剩余单词,以赖规则:tags,以有的单词等等
还ctrl_n呢
例一、两个常用的指令序列
xp 左右交换光标处两字符的位置。
ddp 上下交换光标处两行的位置。
例二、重复输入同一字符
有时,我们可能想多次输入同一字符,VIM的插入功能可以很好的完成这项工作
命令 80i=^ESC 一次可以输入80个字符= ,当然,80a=^ESC 也可以完成上述功能。
请注意:此处的^ESC表示键盘左上方上的ESC键。
例三、将两个文本数据文件按行逐条合并,并给出标尺
数据文件1内容如下:
1-----
2-----
3-----
数据文件2内容如下:
1=====
2=====
3=====
要求的结果如下:
|--------1---------2---------3---------4---------5
1-----
1=====
|--------1---------2---------3---------4---------5
2-----
2=====
|--------1---------2---------3---------4---------5
3-----
3=====
也许您会说,这还不简单,无非是反复拷贝、粘贴,任何一款文本编辑器都能完成上述功能。可是,如果这两个文件都很大,每个文件都成千上万行,恐怕简单的拷贝、粘贴就难以胜任了。因此,我们所关心的,是找到一种行之有效的方法,把枯燥乏味的工作留给计算机,我们只需发布指令。为达到此目的,请按以下步骤执行:
㈠、将两文件合并,结果如下
1-----
2-----
3-----
1=====
2=====
3=====
㈡、在两文件头尾相接的地方插入标志行,用以区分两个文件,本文采用的是一整行!字符
1-----
2-----
3-----
!!!!!!!!!!!!!!!!!!!!!!!!
1=====
2=====
3=====
㈢、在标志行的下方输入标尺
1-----
2-----
3-----
!!!!!!!!!!!!!!!!!!!!!!!!
|--------1---------2---------3---------4---------5
1=====
2=====
3=====
㈣、执行宏命令脚本merge_2r.vim,即在VIM编辑器中按如下键 :so merge_2r.vim 回车
㈤、按下键盘上的=键,执行的结果如下
|--------1---------2---------3---------4---------5
1-----
1=====
|--------1---------2---------3---------4---------5
2-----
2=====
|--------1---------2---------3---------4---------5
3-----
3=====
|--------1---------2---------3---------4---------5
!!!!!!!!!!!!!!!!!!!!!!!!
|--------1---------2---------3---------4---------5
㈥、将最后三行删除,即可得到我们需要的结果
|--------1---------2---------3---------4---------5
1-----
1=====
|--------1---------2---------3---------4---------5
2-----
2=====
|--------1---------2---------3---------4---------5
3-----
3=====
怎么样,简单吗?请大家自己实际尝试一下。下面,我来详细讲解宏命令脚本merge_2r.vim 。
该脚本内容如下:
"--------------------------------------------------------------------
"Macro Function : Merge File1 And File2,Have Ruler in every record
" Date : 2001/12/01
" Author : Yan Shi
"--------------------------------------------------------------------
"1-----
"2----- } Sample File1
"3-----
"!!!!!!!!!!!!!!!!!!!!!!!! Flag Row
"|--------1---------2---------3---------4---------5 Ruler
"1=====
"2===== } Sample File2
"3=====
"--------------------------------------------------------------------
:1
:map = ma/!!!!!^M+:.co 'a-1^M/!!!!!^M2+:.m'a^M+=
前14行每行都以"开始,表明该行是注释行,实际并不执行,只是方便读者阅读,只有最后两行才是真正的代码行。请注意:本例中的^M表示键盘上的回车键,并非^和M两个字符。为了讲述清楚,我把命令行分解开,逐一说明。
首先将第一行置为当前行,然后执行map命令,将一大串VIM指令映像给字符=。这一大串VIM指令共分9步执行:
ma 将数据文件一的第一行标记为a
1-----
2-----
3-----
!!!!!!!!!!!!!!!!!!!!!!!!
|--------1---------2---------3---------4---------5
1=====
2=====
3=====
/!!!!!^M 找到标志行,置为当前行
1-----
2-----
3-----
!!!!!!!!!!!!!!!!!!!!!!!!
|--------1---------2---------3---------4---------5
1=====
2=====
3=====
+ 光标下移一行,即把标尺行置为当前行
1-----
2-----
3-----
!!!!!!!!!!!!!!!!!!!!!!!!
|--------1---------2---------3---------4---------5
1=====
2=====
3=====
:.co 'a-1^M 把标尺行复制到标记行(数据文件一的第一行)的上方
|--------1---------2---------3---------4---------5
1-----
2-----
3-----
!!!!!!!!!!!!!!!!!!!!!!!!
|--------1---------2---------3---------4---------5
1=====
2=====
3=====
/!!!!!^M 再次找到标志行,置为当前行
|--------1---------2---------3---------4---------5
1-----
2-----
3-----
!!!!!!!!!!!!!!!!!!!!!!!!
|--------1---------2---------3---------4---------5
1=====
2=====
3=====
2+ 光标下移2行,即数据文件二的第一行置为当前行
|--------1---------2---------3---------4---------5
1-----
2-----
3-----
!!!!!!!!!!!!!!!!!!!!!!!!
|--------1---------2---------3---------4---------5
1=====
2=====
3=====
:.m'a^M 把数据文件二的第一行移至标记行的下方
|--------1---------2---------3---------4---------5
1-----
1=====
2-----
3-----
!!!!!!!!!!!!!!!!!!!!!!!!
|--------1---------2---------3---------4---------5
2=====
3=====
+ 光标下移一行,即数据文件一的第二行置为当前行
|--------1---------2---------3---------4---------5
1-----
1=====
2-----
3-----
!!!!!!!!!!!!!!!!!!!!!!!!
|--------1---------2---------3---------4---------5
2=====
3=====
= 这一步很关键,是典型的递归调用,重复完成以上步骤
例四、在文件中置入行号
工作中,我们有时希望把行号置入文件中,而VIM提供的功能 :set nu 只能显示行号,不能编辑或将其置入文件当中,下面的宏命令脚本row_num.vim可以完成此项功能。
"------------------------------------------
"Macro Function : Source File Add Row_Num
" Date : 2001/12/01
" Author : Yan Shi
"------------------------------------------
:%s/^/^I/
:$
:let end=line(".")
:1
"------------------------------------------
:let num=1
:while num<=end
:let line=getline(".")
:let temp=substitute(line,$,num,"")
:call setline(".",temp)
:+
:let num=num+1
:endwhile
"------------------------------------------
请注意:本例中的^I表示键盘上的TAB键,并非^和I两个字符。下面,我针对该宏命令脚本逐一讲解。
:%s/^/^I/ 每一行的行首添加一个TAB字符
:$ 到文件的末行
:let end=line(".") 末行的行号 ==〉变量 END,函数line的功能是取得指定行的行号,此处参数"."表示当前行
:1 到文件的首行
"------------------------------------------
:let num=1 1 ==〉计数器
:while num<=end
:let line=getline(".") 取当前行的内容 ==〉变量 LINE
:let line=substitute(line,$,num,"") 在变量 LINE 的前面置入行号
:call setline(".",line) 将变量 LINE 的内容写回当前行
:+ 下移一行
:let num=num+1 计数器加一
:endwhile 循环执行,直到文件结束
"------------------------------------------
真正的高级技巧,不过貌似太高了,一般用不着……
有关正则表达式的使用
UNIX/LINUX 下的很多工具之所以强大、灵活,关键是因为有了正则文法和元字符,这也是VIM乃至UNIX/LINUX系统的精华所在。正因为使用灵活,因此掌握起来比较吃力,如果不是真正理解,实际运用中会出现千奇百怪的错误。因此,有必要对这部分知识多花些气力。下面结合具体实例讲解。
例五、有一文件,包含某外企的中国员工的资料,首先是姓名,然后是两个空格,其次是15位身份证号码。
zhang.fei 430759701022003
diao.chan 651302801225012
guan.yu 342869680413001
xi.shi 120638780214006
liu.bei 210324650708001
现在,有以下问题需要解决:
按照外国人的习惯,应该是名在前,姓在后。因此,文件中的姓名字段需要修改。
姓与名的首字母应该大写。
根据身份证号码,还可以判断出生年月日,将其作为一个新字段添加。
根据身份证号码,可以判断出性别。若为男性,添加male,若为女性,添加female 。
将男女员工分开,男员工在前,女员工在后。
将各字段数据左对齐
最终结果如下:
Fei.Zhang 430759701022003 1970/10/22 male
Yu.Guan 342869680413001 1968/04/13 male
Bei.Liu 210324650708001 1965/07/08 male
-----------------------------------------------
Chan.Diao 651302801225012 1980/12/25 female
Shi.Xi 120638780214006 1978/02/14 female
为了完成上述功能,只需执行脚本employee.vim ,使用方法为 :so employee.vim 回车即可。
脚本内容如下:
:%s/ / /
:%s/\(............\)\( *\)/\1/
:%s/\([A-Za-z][A-Za-z]*\)\(\.\)\([A-Za-z][A-Za-z]*\)/\u\3\2\u\1/
:%s/$/ xxxxxx/
:%s/\([0-9]\{6}\)\([0-9]\{6}\)\([0-9]\{3}\) \(xxxxxx\)/\1\2\3 \2/
:%s/\(..\)\(..\)\(..\)$/19\1\/\2\/\3
:%s/$/ xxxxxx/
:%s/\([0-9]\{14}[13579]\)\(.*\)\(xxxxxx\)/\1\2male /
:%s/\([0-9]\{14}[02468]\)\(.*\)\(xxxxxx\)/\1\2female/
:$
:s/.*/&^M-----------------------------------------------
:g/female/.m$
在这个脚本中,使用了大量的正则表达式,这里仅对涉及到的正则表达式做一简要介绍。有关正则表达式的内容相当多,本文不可能占用大量篇幅叙述,请大家谅解。
% 地址范围符号,代表文件中的所有行,作用等同于地址范围 1,$
. 与任意单字符(换行符除外)匹配,例如 y.s 可以匹配 yas y.s 或 y s 等等。
* 与前一字符的0次或多次出现匹配,例如 y*s 可以匹配 yys yyyyys 或 s 等等。
$ 与行尾匹配。
& 代表模式匹配中出现的字符串,例如 s/abc/&def 是把当前行的abc替换成abcdef 。
[] 匹配[]中出现的字符,例如[abc]匹配字符 a,b 或 c ,[a-zA-Z]匹配所有的英文字符。
\( \) \(和\)之间出现的内容可以由\num来替代。
\1\2\3 替代\(和\)之间出现的内容。
\u 将后续字符串的首字母大写。
\{num} 与前一字符的num次出现匹配。
现在,我们对脚本逐条讲解,希望能帮助大家理解正则文法。
⑴:%s/ / /
将文件中每行出现的2个空格替换为10个空格。
zhang.fei 430759701022003
diao.chan 651302801225012
guan.yu 342869680413001
xi.shi 120638780214006
liu.bei 210324650708001
⑵:%s/\(............\)\( *\)/\1/
保留行首的12个字符,将其余的空格删除,这样,前两个字段就对齐了。
zhang.fei 430759701022003
diao.chan 651302801225012
guan.yu 342869680413001
xi.shi 120638780214006
liu.bei 210324650708001
⑶:%s/\([A-Za-z][A-Za-z]*\)\(\.\)\([A-Za-z][A-Za-z]*\)/\u\3\2\u\1/
将文件中每行出现的雇员姓名互换,并将首字母大写。
Fei.Zhang 430759701022003
Chan.Diao 651302801225012
Yu.Guan 342869680413001
Shi.Xi 120638780214006
Bei.Liu 210324650708001
⑷:%s/$/ xxxxxx/
在每一行的行尾添加2个空格和6个x
Fei.Zhang 430759701022003 xxxxxx
Chan.Diao 651302801225012 xxxxxx
Yu.Guan 342869680413001 xxxxxx
Shi.Xi 120638780214006 xxxxxx
Bei.Liu 210324650708001 xxxxxx
⑸:%s/\([0-9]\{6}\)\([0-9]\{6}\)\([0-9]\{3}\) \(xxxxxx\)/\1\2\3 \2/
将xxxxxx替换成出生年月日。
Fei.Zhang 430759701022003 701022
Chan.Diao 651302801225012 801225
Yu.Guan 342869680413001 680413
Shi.Xi 120638780214006 780214
Bei.Liu 210324650708001 650708
⑹:%s/\(..\)\(..\)\(..\)$/19\1\/\2\/\3
将年月日用/字符分隔,并在年前添加19。
Fei.Zhang 430759701022003 1970/10/22
Chan.Diao 651302801225012 1980/12/25
Yu.Guan 342869680413001 1968/04/13
Shi.Xi 120638780214006 1978/02/14
Bei.Liu 210324650708001 1965/07/08
⑺:%s/$/ xxxxxx/
在每一行的行尾添加2个空格和6个x
Fei.Zhang 430759701022003 1970/10/22 xxxxxx
Chan.Diao 651302801225012 1980/12/25 xxxxxx
Yu.Guan 342869680413001 1968/04/13 xxxxxx
Shi.Xi 120638780214006 1978/02/14 xxxxxx
Bei.Liu 210324650708001 1965/07/08 xxxxxx
⑻:%s/\([0-9]\{14}[13579]\)\(.*\)\(xxxxxx\)/\1\2male /
身份证号码末位是奇数的,将xxxxxx替换成male
Fei.Zhang 430759701022003 1970/10/22 male
Chan.Diao 651302801225012 1980/12/25 xxxxxx
Yu.Guan 342869680413001 1968/04/13 male
Shi.Xi 120638780214006 1978/02/14 xxxxxx
Bei.Liu 210324650708001 1965/07/08 male
⑼:%s/\([0-9]\{14}[02468]\)\(.*\)\(xxxxxx\)/\1\2female/
身份证号码末位是偶数的,将xxxxxx替换成female
Fei.Zhang 430759701022003 1970/10/22 male
Chan.Diao 651302801225012 1980/12/25 female
Yu.Guan 342869680413001 1968/04/13 male
Shi.Xi 120638780214006 1978/02/14 female
Bei.Liu 210324650708001 1965/07/08 male
⑽:$ 到文件的最后一行
⑾:s/.*/&^M-----------------------------------------------
在文件的最末行插入一行 "-" 字符。
Fei.Zhang 430759701022003 1970/10/22 male
Chan.Diao 651302801225012 1980/12/25 female
Yu.Guan 342869680413001 1968/04/13 male
Shi.Xi 120638780214006 1978/02/14 female
Bei.Liu 210324650708001 1965/07/08 male
-----------------------------------------------
⑿:g/female/.m$
将所有的女员工记录移至文件尾。
Fei.Zhang 430759701022003 1970/10/22 male
Yu.Guan 342869680413001 1968/04/13 male
Bei.Liu 210324650708001 1965/07/08 male
-----------------------------------------------
Chan.Diao 651302801225012 1980/12/25 female
Shi.Xi 120638780214006 1978/02/14 female
笔者目前正在为某外资公司从事大型机(IBM S/390)的软件开发,一切工作都在TSO环境中进行。为了对编写的程序进行测试,必须准备测试数据。有过大型机开发经验的人会知道,通过TSO,输入字符型数据还可以,如果要输入16进制数据,操作起来很麻烦。因为16进制数是纵向排列的,输入时既不方便,又很容易错位。怎么解决呢?我尝试了几种办法,实际证明,用VIM最方便。
例六、下列数据 1234567890ABCDEF ,将其变成 13579ACE 24680BDF 的形式,这样,数据就可以很方便的粘贴到TSO环境中了。
下面给出宏命令脚本change_d.vim
"----------------------------------------------------
"Macro Function : Convert Char Arrange Direction
"
" Sample : 40 50 60 ==> 4 5 6
" 0 0 0
" Date : 2001/12/01
" Author : Yan Shi
"----------------------------------------------------
:s/.*/&^M/
:1
:map = malx+$p-`al=
说明如下:
⑴ :s/.*/&^M/ 在数据行下方添加一空行。
⑵ :1 回到文件的首行的首字符。
⑶ :map = malx+$p-`al= 将一大串VIM命令映像给字符=
① ma 将首字符标记为a
② l 光标右移一个字符
③ x 删除光标处字符
④ + 移至下一行
⑤ $ 到行尾
⑥ p 将删除的字符粘贴
⑦ - 回至上一行
⑧ `a 返回到标记字符处
⑨ l 光标右移一个字符
⑩ = 递归调用,重复以上步骤,直到将该行所有的数据处理完。
#include <unistd.h>
int daemon(int nochdir,int noclose)
在创建精灵进程的时候,往往需要将精灵进程的工作目录修改为"/"根目录
并且将标准输入,输出和错误输出重定向到/dev/null
daemon的作用就是当参数nochdir为0时,将根目录修改为工作目录
noclose为0时,做输入,输出以及错误输出重定向到/dev/null
执行成功返回0
错误返回-1
------------------------------------------------------------------------------------------------------------------------------------------------------------------------
转载请注明链接: http://hi.baidu.com/kkernel/blog/item/b1a55c1ea7d37d034134172f.html
守护进程最重要的特性是后台运行;其次,守护进程必须与其运行前的环境隔离开来。这些环境包括未关闭的文件描述符,控制终端,会话和进程组,工作目录以及文件创建掩模等。这些环境通常是守护进程从执行它的父进程(特别是shell)中继承下来的;最后,守护进程的启动方式有其特殊之处------它可以在Linux系统启动时从启动脚本/etc/rc.d中启动,可以由作业规划进程crond启动,还可以由用户终端(通常是shell)执行。
总之,除开这些特殊性以外,守护进程与普通进程基本上没有什么区别,因此,编写守护进程实际上是把一个普通进程按照上述的守护进程的特性改造成为守护进程。
其实,在标准的Linux系统环境下的库函数中已经提供了daemon()函数,如下:
[guowenxue@centos6 mobiled]$ man daemon
DAEMON(3) Linux Programmer’s Manual DAEMON(3)
NAME
daemon - run in the background
SYNOPSIS
#include <unistd.h>
int daemon(int nochdir, int noclose);
下面我们将针对Linux平台和uClinux平台来讲解守护编程的编写细节:
标准Linux下的守护进程的编写:
void daemonize_linux(int nochdir, int noclose)
{
int retval, fd;
int i;
在创建daemon进程时,父进程在子进程退出之前退出,这时子进程会被Init进程(pid=1)领养,所以这里如果进程的parent pid是1的话,说明他已经在后台运行了。
/* already a daemon */
if (1 == getppid())
return;
/* fork error */
retval = fork();
if (retval < 0)
_exit(0);
为避免挂起控制终端将Daemon放入后台执行。方法是在进程中调用fork使父进程终止, 让Daemon在子进程中后台执行。如果这时父进程在子进程前退出,那么init进程将领养子进程。
/* parent process exit */
if (retval > 0)
_exit(0);
这里主要是脱离控制终端,登录会话和进程组 。必要先介绍一下Linux中的进程与控制终端,登录会话和进程组之间的关系:进程属于 一个进程组,进程组号(GID)就是进程组长的进程号(PID)。登录会话可以包含多个进程组。这些进程组共享一个控制终端。这个控制终端通常是创建进程的登录终端。 控制终端,登录会话和进程组通常是从父进程继承下来的。我们的目的就是要摆脱它们 ,使之不受它们的影响。方法是在这里调用setsid()使进程成为会话组长:
/* obtain a new process session group */
setsid();
if (!noclose)
{
进程从创建它的父进程那里继承了打开的文件描述符。如不关闭,将会浪费系统资源, 造成进程所在的文件系统无法卸下以及引起无法预料的错误。按如下方法关闭它们:
/* close all descriptors */
for (i = getdtablesize(); i >= 0; --i)
close(i);
因为子进程在后台运行,他们不需要标准输入,标准输出,标准出错.这时我们可以将他们全部重定向到/dev/null文件下去
fd = open("dev/null", O_RDWR); /* Redirect Standard input [0] to /dev/null */
dup(fd); /* Redirect Standard output [1] to /dev/null */
dup(fd); /* Redirect Standard error [2] to /dev/null */
}
进程从创建它的父进程那里继承了文件创建掩码。它可能修改守护进程所创建的文件的存取位。为防止这一点,将文件创建掩码掩模清除:
umask(0);
进程活动时,其工作目录所在的文件系统不能卸下。一般需要将工作目录
改变到根目录 。对于需要转储核心,写运行日志的进程将工作目录改变到特定目录如/tmp
if (!nochdir)
chdir("/");
return;
}
uCLinux下的守护进程编写:
/*由于uClinux不支持fork(),只支持vfork()的特性,同时在一些库中也没有提供像标准linux系统中能提供的daemon()函数,所以我们必须自己来实现uclinux操作系统下的daemon()函数:
这里我们主要是通过,第一次vfork()让父进程结束,同时让子进程再次运行一次代码来实现。所以这里的argc和argv参数主要使用在子进程中再次以同样的参数来运行自己。
*/
void daemon_ucLinux (int noclose, int argc, char **argv)
{
int iRet = 0;
int fd = -1;
5, 父进程是init进程,说明已在后台运行,这是孙子进程加载运行程序了
if (1 == getppid ()) /* already a daemon*/
{
setsid (); /* set new process group */
if (!noclose)
{
/*Close all the file description.*/
for (iRet = getdtablesize (); 0 <= iRet; --iRet)
{
fd = iRet;
close(fd);
}
fd = open("/dev/null", O_RDWR); /*Redirect Standard input [0] to /dev/null */
dup (fd); /*Redirect Standard output [1] to /dev/null */
dup (fd); /*Redirect Standard error [2] to /dev/null */
}
umask (027); /* set newly created file mode mask */
chdir("/"); // change running directory
return ;
}
else /*1, 第一次启动该程序的时候,系统将运行到这里*/
{
iRet = vfork ();
/*2, 通过vfrok()创建子进程,由于vfork()会导致父进程阻塞直到子进程推出或调用exec()函数,所以这里父进程并没有结束*/
if (0 > iRet) _exit (1); /* fork error */
if (0 < iRet) _exit (0); /* parent exists */
3, 再次调用vfork()产生孙子进程,再让子进程结束,这时上面的父进程就会退出,释放终端,子进程则阻塞在vfork()上,程序跑到后台了。
/*Child process(daemon) continue and fork again.*/
iRet = vfork ();
if (0 > iRet) _exit (1); /* fork error */
if (0 < iRet) _exit (0); /* parent exists */
4, 孙子进程调用execv()函数重新装载程序运行,子进程将不再阻塞在vfork()上,这时子进程也退出了。孙子进程被init进程领养,他的Parent PID将变成1
/* Parent exit, so child process parent pid should be init process(pid is 1) */
execv (argv[0], argv); /*Run the pograme again.*/
_exit (1); /*If execv() returned, then something error happend.*/
}
}
http://fanqiang.chinaunix.net/program/netpro/2001-04-19/1904.shtml
int daemon(int nochdir,int noclose)
在创建精灵进程的时候,往往需要将精灵进程的工作目录修改为"/"根目录
并且将标准输入,输出和错误输出重定向到/dev/null
daemon的作用就是当参数nochdir为0时,将根目录修改为工作目录
noclose为0时,做输入,输出以及错误输出重定向到/dev/null
执行成功返回0
错误返回-1
------------------------------------------------------------------------------------------------------------------------------------------------------------------------
转载请注明链接: http://hi.baidu.com/kkernel/blog/item/b1a55c1ea7d37d034134172f.html
守护进程最重要的特性是后台运行;其次,守护进程必须与其运行前的环境隔离开来。这些环境包括未关闭的文件描述符,控制终端,会话和进程组,工作目录以及文件创建掩模等。这些环境通常是守护进程从执行它的父进程(特别是shell)中继承下来的;最后,守护进程的启动方式有其特殊之处------它可以在Linux系统启动时从启动脚本/etc/rc.d中启动,可以由作业规划进程crond启动,还可以由用户终端(通常是shell)执行。
总之,除开这些特殊性以外,守护进程与普通进程基本上没有什么区别,因此,编写守护进程实际上是把一个普通进程按照上述的守护进程的特性改造成为守护进程。
其实,在标准的Linux系统环境下的库函数中已经提供了daemon()函数,如下:
[guowenxue@centos6 mobiled]$ man daemon
DAEMON(3) Linux Programmer’s Manual DAEMON(3)
NAME
daemon - run in the background
SYNOPSIS
#include <unistd.h>
int daemon(int nochdir, int noclose);
下面我们将针对Linux平台和uClinux平台来讲解守护编程的编写细节:
标准Linux下的守护进程的编写:
void daemonize_linux(int nochdir, int noclose)
{
int retval, fd;
int i;
在创建daemon进程时,父进程在子进程退出之前退出,这时子进程会被Init进程(pid=1)领养,所以这里如果进程的parent pid是1的话,说明他已经在后台运行了。
/* already a daemon */
if (1 == getppid())
return;
/* fork error */
retval = fork();
if (retval < 0)
_exit(0);
为避免挂起控制终端将Daemon放入后台执行。方法是在进程中调用fork使父进程终止, 让Daemon在子进程中后台执行。如果这时父进程在子进程前退出,那么init进程将领养子进程。
/* parent process exit */
if (retval > 0)
_exit(0);
这里主要是脱离控制终端,登录会话和进程组 。必要先介绍一下Linux中的进程与控制终端,登录会话和进程组之间的关系:进程属于 一个进程组,进程组号(GID)就是进程组长的进程号(PID)。登录会话可以包含多个进程组。这些进程组共享一个控制终端。这个控制终端通常是创建进程的登录终端。 控制终端,登录会话和进程组通常是从父进程继承下来的。我们的目的就是要摆脱它们 ,使之不受它们的影响。方法是在这里调用setsid()使进程成为会话组长:
/* obtain a new process session group */
setsid();
if (!noclose)
{
进程从创建它的父进程那里继承了打开的文件描述符。如不关闭,将会浪费系统资源, 造成进程所在的文件系统无法卸下以及引起无法预料的错误。按如下方法关闭它们:
/* close all descriptors */
for (i = getdtablesize(); i >= 0; --i)
close(i);
因为子进程在后台运行,他们不需要标准输入,标准输出,标准出错.这时我们可以将他们全部重定向到/dev/null文件下去
fd = open("dev/null", O_RDWR); /* Redirect Standard input [0] to /dev/null */
dup(fd); /* Redirect Standard output [1] to /dev/null */
dup(fd); /* Redirect Standard error [2] to /dev/null */
}
进程从创建它的父进程那里继承了文件创建掩码。它可能修改守护进程所创建的文件的存取位。为防止这一点,将文件创建掩码掩模清除:
umask(0);
进程活动时,其工作目录所在的文件系统不能卸下。一般需要将工作目录
改变到根目录 。对于需要转储核心,写运行日志的进程将工作目录改变到特定目录如/tmp
if (!nochdir)
chdir("/");
return;
}
uCLinux下的守护进程编写:
/*由于uClinux不支持fork(),只支持vfork()的特性,同时在一些库中也没有提供像标准linux系统中能提供的daemon()函数,所以我们必须自己来实现uclinux操作系统下的daemon()函数:
这里我们主要是通过,第一次vfork()让父进程结束,同时让子进程再次运行一次代码来实现。所以这里的argc和argv参数主要使用在子进程中再次以同样的参数来运行自己。
*/
void daemon_ucLinux (int noclose, int argc, char **argv)
{
int iRet = 0;
int fd = -1;
5, 父进程是init进程,说明已在后台运行,这是孙子进程加载运行程序了
if (1 == getppid ()) /* already a daemon*/
{
setsid (); /* set new process group */
if (!noclose)
{
/*Close all the file description.*/
for (iRet = getdtablesize (); 0 <= iRet; --iRet)
{
fd = iRet;
close(fd);
}
fd = open("/dev/null", O_RDWR); /*Redirect Standard input [0] to /dev/null */
dup (fd); /*Redirect Standard output [1] to /dev/null */
dup (fd); /*Redirect Standard error [2] to /dev/null */
}
umask (027); /* set newly created file mode mask */
chdir("/"); // change running directory
return ;
}
else /*1, 第一次启动该程序的时候,系统将运行到这里*/
{
iRet = vfork ();
/*2, 通过vfrok()创建子进程,由于vfork()会导致父进程阻塞直到子进程推出或调用exec()函数,所以这里父进程并没有结束*/
if (0 > iRet) _exit (1); /* fork error */
if (0 < iRet) _exit (0); /* parent exists */
3, 再次调用vfork()产生孙子进程,再让子进程结束,这时上面的父进程就会退出,释放终端,子进程则阻塞在vfork()上,程序跑到后台了。
/*Child process(daemon) continue and fork again.*/
iRet = vfork ();
if (0 > iRet) _exit (1); /* fork error */
if (0 < iRet) _exit (0); /* parent exists */
4, 孙子进程调用execv()函数重新装载程序运行,子进程将不再阻塞在vfork()上,这时子进程也退出了。孙子进程被init进程领养,他的Parent PID将变成1
/* Parent exit, so child process parent pid should be init process(pid is 1) */
execv (argv[0], argv); /*Run the pograme again.*/
_exit (1); /*If execv() returned, then something error happend.*/
}
}
http://fanqiang.chinaunix.net/program/netpro/2001-04-19/1904.shtml
#include <sys/types.h>
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
int main()
{
FILE *stream;
stream=popen("ps aux|grep rpc","r");
char buf[4096];
fread(buf,sizeof(char),4096,stream);
printf("%s",buf);
}
#include "stdio.h"
int main()
{
char a[256];
system("ps axu|grep httpd>a.txt");
printf("%c",a);
return 0;
}
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
int main()
{
FILE *stream;
stream=popen("ps aux|grep rpc","r");
char buf[4096];
fread(buf,sizeof(char),4096,stream);
printf("%s",buf);
}
#include "stdio.h"
int main()
{
char a[256];
system("ps axu|grep httpd>a.txt");
printf("%c",a);
return 0;
}
. 设置crontab文件,并用它来提交作业
. 使用at命令来提交作业
. 在后台提交作业
. 使用nohup命令提交作业
cron 系统调度进程,使用它在每天得非高峰负荷时间段运行作业,或者在一周或一月中得不同时段运行
at命令,在特定时间运行一些特殊作业
& 使用它在后台运行一个占用时间不长得进程
Nohup 使用它在后台运行一个命令,即使在用户退出时也不受影响。
3.1 cron和crontab
cron 是系统得调度进程,在无需人工干预得情况下运行作业。
crontab命令允许用户提交,编辑或者删除相应得作业。每个用户都有一个crontab文件来保存调度信息,
3.1.1 crontab的域
第1列 分钟1~59
第2列 小时1~23
第3列 日1-31
第4列 月1~12
第5列 星期0~6
第6列 要运行的命令
3.1.2 条目举例
30 21 * * * /apps/bin/clearnup.sh
每晚的21:30运行/apps/bin目录下的cleanup.sh
45 4 1,10,22 * * /apps/bin/backup.sh
每月的1,20,22号的4:45执行/apps/bin 目录下的 backup.sh
10 1 * * 6,0 /bin/find -name "core" rm {} \;
每个星期六。日1:10运行一个find 命令
0,30 18-23 * * * /apps/bin/dbcheck.sh
每天在18:00~23:00每隔30分钟执行/apps/bin目录下的dbcheck.sh
0 23 * * 6 /apps/bin/qtrend.sh
每周六的23:00执行/apps/bin/qtrend.sh
3.1.3 crontab的命令选项
crontab [-u user] -e -l -r
-u 用户名
-e 编辑crontab文件
-l 列出crontab文件的内容
-r 删除crontab文件
3.1.4 创建一个crontab文件
crontab faintbearcron
3.1.5 列出crontab文件
crontab -l
crontab -l >$HOME/faintbearcron
(备份)
3.1.6 编辑crontab文件
crontab -e
3.1.7 删除crontab文件
crontab -r
3.1.8 恢复丢失的crontab文件
crontab <filename>
其中<filename>就是你在$HOME目录中的副本的文件名
. 使用at命令来提交作业
. 在后台提交作业
. 使用nohup命令提交作业
cron 系统调度进程,使用它在每天得非高峰负荷时间段运行作业,或者在一周或一月中得不同时段运行
at命令,在特定时间运行一些特殊作业
& 使用它在后台运行一个占用时间不长得进程
Nohup 使用它在后台运行一个命令,即使在用户退出时也不受影响。
3.1 cron和crontab
cron 是系统得调度进程,在无需人工干预得情况下运行作业。
crontab命令允许用户提交,编辑或者删除相应得作业。每个用户都有一个crontab文件来保存调度信息,
3.1.1 crontab的域
第1列 分钟1~59
第2列 小时1~23
第3列 日1-31
第4列 月1~12
第5列 星期0~6
第6列 要运行的命令
3.1.2 条目举例
30 21 * * * /apps/bin/clearnup.sh
每晚的21:30运行/apps/bin目录下的cleanup.sh
45 4 1,10,22 * * /apps/bin/backup.sh
每月的1,20,22号的4:45执行/apps/bin 目录下的 backup.sh
10 1 * * 6,0 /bin/find -name "core" rm {} \;
每个星期六。日1:10运行一个find 命令
0,30 18-23 * * * /apps/bin/dbcheck.sh
每天在18:00~23:00每隔30分钟执行/apps/bin目录下的dbcheck.sh
0 23 * * 6 /apps/bin/qtrend.sh
每周六的23:00执行/apps/bin/qtrend.sh
3.1.3 crontab的命令选项
crontab [-u user] -e -l -r
-u 用户名
-e 编辑crontab文件
-l 列出crontab文件的内容
-r 删除crontab文件
3.1.4 创建一个crontab文件
crontab faintbearcron
3.1.5 列出crontab文件
crontab -l
crontab -l >$HOME/faintbearcron
(备份)
3.1.6 编辑crontab文件
crontab -e
3.1.7 删除crontab文件
crontab -r
3.1.8 恢复丢失的crontab文件
crontab <filename>
其中<filename>就是你在$HOME目录中的副本的文件名
二维数组可以看作是由两个一维数组(一个指针数组,一个基本型别的数组)嵌套而成;
二维数组可以看作是由两个一维数组(一个指针数组,一个基本型别的数组)嵌套而成;
比如a[10][10];
其中a是一个数组,他的每个元素(a[i])(0<=i<10)又是一个数组,如a[i]就是一个数组,它的每个元素为a[i][j](0<=j<10);
其实2维数组中*a就是表示a[0];
实际上在内存中只存在一维数组,它只是在语言这一层次上用二维的方式呈现给语言的使用者。
其实二维数组和一维数组差不多,
一维数组中a[i]与*等同,都指的是值;
二维数组中a[i][j]可分为两部分,a[i]代表指向第i行的指针,也就是第i行的首地址;
a[i][j]代表第i行的第j个元素
#include <stdio.h>
int main()
{
int i;
float a[9]={0,1,2,3,4,5,6,7,8};
float *p=(float*)malloc(9*sizeof(float));
for(i=0;i<9;i++)
{
scanf("%f",p+i);
}
for(i=0;i<9;i++)
{
printf("p==%f\n",*(p+i));
}
}
动态分配内存并填充里面的内容:
[codes=php]
#include <stdlib.h>
#include <stdio.h>
int main()
{
int i,j;
int *p[300];
printf("len of int=%d\n",sizeof(int));
for(i=0;i<300;i++){
p[i]=(int *)malloc(262013*sizeof(int));
for(j=0;j<262013;j++){
*(p[i]+j)=1;
//sleep(1);
//printf("p=%d\n",*(p[i]+j));
}
//sleep(0.1);
}
printf("End...\n");
while(1){
sleep(1);
}
}
[/codes]
make malloc
cc malloc.c -o malloc
./malloc
watch -n 2 ps -opid,ppid,rss,vsz,cmd -C malloc Mon Sep 2 17:17:11 2019
PID PPID RSS VSZ CMD
880 30676 45888 49320 ./malloc
经查实核对,上面的RSS是kb,想要看到M,则要45888/1024=44M ,
244667 ==>280M
61166 ==> 70M
42816 ==> 49.57M (共享内存SO动态链接库占用)为何是50M,是300个数组指针,42816*4*300/1024=50.175
256896 ==>294M
262013 ==>300.42M (主进程)
引入了动态链接库在RSS,VSZ上的一个系统不一致研究并查看前的动态链接库的编译基础原理链接,
[实践OK]Linux如何解决动态库的版本控制, Linux如何解决动态库的版本控制,C语言HelloWorld示例,readelf -d libhello.so.0.0.0 |grep SONAME。
[url=https://jackxiang.com/post/7717/]https://jackxiang.com/post/7717/[/url]
整型点几个字节的问题决定上面能使用多少M的内存:
[codes=php]
#include<stdio.h>
main()
{
char a;
char* b;
short int c;
int d;
unsigned int e;
float f;
double g;
long h;
long long i;
unsigned long j;
printf("char a=%d\n",(int)sizeof(a));
printf("char*b=%d\n",(int)sizeof(b));
printf("short int c=%d\n",(int)sizeof(c));
printf("int d=%d\n",(int)sizeof(d));
printf("unsigned int e=%d\n",(int)sizeof(e));
printf("float f=%d\n",(int)sizeof(f));
printf("double g=%d\n",(int)sizeof(g));
printf("long h=%d\n",(int)sizeof(h));
printf("long long i=%d\n",(int)sizeof(i));
printf("unsiged long j=%d\n",(int)sizeof(j));
}
[/codes]
make int
./int
char a=1
char*b=8
short int c=2
int d=4
unsigned int e=4
float f=4
double g=8
long h=8
long long i=8
unsiged long j=8
来自:https://www.cnblogs.com/TGSB/p/4824246.html
[url=https://www.wntool.com/filesize/]https://www.wntool.com/filesize/[/url]里得出:
整型变量中,上面得出的1个整型有4个字节(byte),也就是4个byte的类型据上面查出有( 8 )位bit。
1M=1048576个Byte,1048576/4=262144是小B,得乘以8=8388608 .
dmesg | grep oom
[ 91.458107] stress-ng (10288): /proc/10288/oom_adj is deprecated, please use /proc/10288/oom_score_adj instead.
dmesg | less -I
用pmap -x pid查看:
pidof main
1589
pmap -x 1589
pmap -x 1589
1589: ./main
Address Kbytes RSS Dirty Mode Mapping
0000000000400000 4 4 0 r-x-- main
0000000000600000 4 4 4 r---- main
0000000000601000 4 4 4 rw--- main
00007f79c04da000 355752 355752 355752 rw--- [ anon ]
00007f79d6044000 1804 276 0 r-x-- libc-2.17.so
00007f79d6207000 2044 0 0 ----- libc-2.17.so
00007f79d6406000 16 16 16 r---- libc-2.17.so
00007f79d640a000 8 8 8 rw--- libc-2.17.so
00007f79d640c000 20 12 12 rw--- [ anon ]
00007f79d6411000 4 4 0 r-x-- libhello.so.0.0.0
00007f79d6412000 2044 0 0 ----- libhello.so.0.0.0
00007f79d6611000 4 4 4 r---- libhello.so.0.0.0
00007f79d6612000 4 4 4 rw--- libhello.so.0.0.0
00007f79d6613000 136 108 0 r-x-- ld-2.17.so
00007f79d6658000 1860 1860 1860 rw--- [ anon ]
00007f79d6832000 8 8 8 rw--- [ anon ]
00007f79d6834000 4 4 4 r---- ld-2.17.so
00007f79d6835000 4 4 4 rw--- ld-2.17.so
00007f79d6836000 4 4 4 rw--- [ anon ]
00007fff427ad000 1156 12 12 rw--- [ stack ]
00007fff429e3000 8 4 0 r-x-- [ anon ]
ffffffffff600000 4 0 0 r-x-- [ anon ]
---------------- ------- ------- -------
total kB 364896 358092 357696
二维数组可以看作是由两个一维数组(一个指针数组,一个基本型别的数组)嵌套而成;
比如a[10][10];
其中a是一个数组,他的每个元素(a[i])(0<=i<10)又是一个数组,如a[i]就是一个数组,它的每个元素为a[i][j](0<=j<10);
其实2维数组中*a就是表示a[0];
实际上在内存中只存在一维数组,它只是在语言这一层次上用二维的方式呈现给语言的使用者。
其实二维数组和一维数组差不多,
一维数组中a[i]与*等同,都指的是值;
二维数组中a[i][j]可分为两部分,a[i]代表指向第i行的指针,也就是第i行的首地址;
a[i][j]代表第i行的第j个元素
#include <stdio.h>
int main()
{
int i;
float a[9]={0,1,2,3,4,5,6,7,8};
float *p=(float*)malloc(9*sizeof(float));
for(i=0;i<9;i++)
{
scanf("%f",p+i);
}
for(i=0;i<9;i++)
{
printf("p==%f\n",*(p+i));
}
}
动态分配内存并填充里面的内容:
[codes=php]
#include <stdlib.h>
#include <stdio.h>
int main()
{
int i,j;
int *p[300];
printf("len of int=%d\n",sizeof(int));
for(i=0;i<300;i++){
p[i]=(int *)malloc(262013*sizeof(int));
for(j=0;j<262013;j++){
*(p[i]+j)=1;
//sleep(1);
//printf("p=%d\n",*(p[i]+j));
}
//sleep(0.1);
}
printf("End...\n");
while(1){
sleep(1);
}
}
[/codes]
make malloc
cc malloc.c -o malloc
./malloc
watch -n 2 ps -opid,ppid,rss,vsz,cmd -C malloc Mon Sep 2 17:17:11 2019
PID PPID RSS VSZ CMD
880 30676 45888 49320 ./malloc
经查实核对,上面的RSS是kb,想要看到M,则要45888/1024=44M ,
244667 ==>280M
61166 ==> 70M
42816 ==> 49.57M (共享内存SO动态链接库占用)为何是50M,是300个数组指针,42816*4*300/1024=50.175
256896 ==>294M
262013 ==>300.42M (主进程)
引入了动态链接库在RSS,VSZ上的一个系统不一致研究并查看前的动态链接库的编译基础原理链接,
[实践OK]Linux如何解决动态库的版本控制, Linux如何解决动态库的版本控制,C语言HelloWorld示例,readelf -d libhello.so.0.0.0 |grep SONAME。
[url=https://jackxiang.com/post/7717/]https://jackxiang.com/post/7717/[/url]
整型点几个字节的问题决定上面能使用多少M的内存:
[codes=php]
#include<stdio.h>
main()
{
char a;
char* b;
short int c;
int d;
unsigned int e;
float f;
double g;
long h;
long long i;
unsigned long j;
printf("char a=%d\n",(int)sizeof(a));
printf("char*b=%d\n",(int)sizeof(b));
printf("short int c=%d\n",(int)sizeof(c));
printf("int d=%d\n",(int)sizeof(d));
printf("unsigned int e=%d\n",(int)sizeof(e));
printf("float f=%d\n",(int)sizeof(f));
printf("double g=%d\n",(int)sizeof(g));
printf("long h=%d\n",(int)sizeof(h));
printf("long long i=%d\n",(int)sizeof(i));
printf("unsiged long j=%d\n",(int)sizeof(j));
}
[/codes]
make int
./int
char a=1
char*b=8
short int c=2
int d=4
unsigned int e=4
float f=4
double g=8
long h=8
long long i=8
unsiged long j=8
来自:https://www.cnblogs.com/TGSB/p/4824246.html
[url=https://www.wntool.com/filesize/]https://www.wntool.com/filesize/[/url]里得出:
整型变量中,上面得出的1个整型有4个字节(byte),也就是4个byte的类型据上面查出有( 8 )位bit。
1M=1048576个Byte,1048576/4=262144是小B,得乘以8=8388608 .
dmesg | grep oom
[ 91.458107] stress-ng (10288): /proc/10288/oom_adj is deprecated, please use /proc/10288/oom_score_adj instead.
dmesg | less -I
用pmap -x pid查看:
pidof main
1589
pmap -x 1589
pmap -x 1589
1589: ./main
Address Kbytes RSS Dirty Mode Mapping
0000000000400000 4 4 0 r-x-- main
0000000000600000 4 4 4 r---- main
0000000000601000 4 4 4 rw--- main
00007f79c04da000 355752 355752 355752 rw--- [ anon ]
00007f79d6044000 1804 276 0 r-x-- libc-2.17.so
00007f79d6207000 2044 0 0 ----- libc-2.17.so
00007f79d6406000 16 16 16 r---- libc-2.17.so
00007f79d640a000 8 8 8 rw--- libc-2.17.so
00007f79d640c000 20 12 12 rw--- [ anon ]
00007f79d6411000 4 4 0 r-x-- libhello.so.0.0.0
00007f79d6412000 2044 0 0 ----- libhello.so.0.0.0
00007f79d6611000 4 4 4 r---- libhello.so.0.0.0
00007f79d6612000 4 4 4 rw--- libhello.so.0.0.0
00007f79d6613000 136 108 0 r-x-- ld-2.17.so
00007f79d6658000 1860 1860 1860 rw--- [ anon ]
00007f79d6832000 8 8 8 rw--- [ anon ]
00007f79d6834000 4 4 4 r---- ld-2.17.so
00007f79d6835000 4 4 4 rw--- ld-2.17.so
00007f79d6836000 4 4 4 rw--- [ anon ]
00007fff427ad000 1156 12 12 rw--- [ stack ]
00007fff429e3000 8 4 0 r-x-- [ anon ]
ffffffffff600000 4 0 0 r-x-- [ anon ]
---------------- ------- ------- -------
total kB 364896 358092 357696
[img]attachment/%e6%9c%aa%e5%91%bd%e5%90%8d.bmp[/img]#include <stdio.h>
main()
{
int i=0,j=0;
float a[3][3]={0,1,2,3,4,5,6,7,8};
printf("&a[0]=%p\n",a[0]);
printf("&a[1]=%p\n",a[1]);
printf("&a[2]=%p\n\n",a[2]);
printf("&a[0][0]=%p\n",&a[0][0]);
printf("&a[1][0]=%p\n",&a[1][0]);
printf("&a[2][0]=%p\n",&a[2][0]);
printf("&a[2][2]=%p\n",&a[2][2]);
printf("\n&a[0]+1=%p\n",a[0]+1);
printf("&a[1]+1=%p\n",a[1]+1);
for(i=0;i<3;i++)
{
for(j=0;j<3;j++)
{
printf("\na[%d][%d]=%p\t",i,j,&a[i][j]);
}
}
for(i=0;i<3;i++)
{
printf("\na+%d=%p\n\n",i,a+i);
}
//printf("%f\n",*(a[1]+2));
printf("sizeof:a[0]==%d\n",sizeof(a[0]));
printf("sizeof:a[1]==%d\n",sizeof(a[1]));
printf("sizeof:a[2]==%d\n",sizeof(a[2]));
printf("\nsizeof a = %d\n",sizeof(a));
for(i=0;i<3;i++)
{
printf("\n&(a[0]+%d)=%p\n",i,a[0]+i);
}
for(i=0;i<3;i++)
{
printf("\n&(a[1]+%d)=%p\n",i,a[1]+i);
}
for(i=0;i<3;i++)
{
printf("\n&(a[2]+%d)=%p\n",i,a[2]+i);
}
printf("\n&a+0=%p\n",a+0);
printf("\n&a+1=%p\n",a+1);
printf("\n&a+2=%p\n",a+2);
printf("&(a[1]+2)=%p\n",a[1]+2);
printf("\na[1]+2=%f\n",*(a[1]+2));
printf("&(a+1)=%p\n",a+1);
printf("a+1=%f\n",*(a+1));
}
float a[3][3]={0,1,2,3,4,5,6,7,8};
&a[0]=0xbfbfeac0
&a[1]=0xbfbfeacc
&a[2]=0xbfbfead8
&a[0][0]=0xbfbfeac0
&a[1][0]=0xbfbfeacc
&a[2][0]=0xbfbfead8
&a[2][2]=0xbfbfeae0
a[0][0]=0xbfbfeac0
a[0][1]=0xbfbfeac4
a[0][2]=0xbfbfeac8
a[1][0]=0xbfbfeacc
a[1][1]=0xbfbfead0
a[1][2]=0xbfbfead4
a[2][0]=0xbfbfead8
a[2][1]=0xbfbfeadc
a[2][2]=0xbfbfeae0
a+0=0xbfbfeac0
a+1=0xbfbfeacc
a+2=0xbfbfead8
sizeof a = 36
sizeof:a[0]==12
sizeof:a[1]==12
sizeof:a[2]==12
&(a[0]+0)=0xbfbfeac0
&(a[0]+1)=0xbfbfeac4
&(a[0]+2)=0xbfbfeac8
&(a[1]+0)=0xbfbfeacc
&(a[1]+1)=0xbfbfead0
&(a[1]+2)=0xbfbfead4
&(a[2]+0)=0xbfbfead8
&(a[2]+1)=0xbfbfeadc
&(a[2]+2)=0xbfbfeae0
&a+0=0xbfbfeac0
&a+1=0xbfbfeacc
&a+2=0xbfbfead8
&(a[1]+2)=0xbfbfead4
a[1]+2=5.000000
&(a+1)=0xbfbfeacc
a+1=5.000003
main()
{
int i=0,j=0;
float a[3][3]={0,1,2,3,4,5,6,7,8};
printf("&a[0]=%p\n",a[0]);
printf("&a[1]=%p\n",a[1]);
printf("&a[2]=%p\n\n",a[2]);
printf("&a[0][0]=%p\n",&a[0][0]);
printf("&a[1][0]=%p\n",&a[1][0]);
printf("&a[2][0]=%p\n",&a[2][0]);
printf("&a[2][2]=%p\n",&a[2][2]);
printf("\n&a[0]+1=%p\n",a[0]+1);
printf("&a[1]+1=%p\n",a[1]+1);
for(i=0;i<3;i++)
{
for(j=0;j<3;j++)
{
printf("\na[%d][%d]=%p\t",i,j,&a[i][j]);
}
}
for(i=0;i<3;i++)
{
printf("\na+%d=%p\n\n",i,a+i);
}
//printf("%f\n",*(a[1]+2));
printf("sizeof:a[0]==%d\n",sizeof(a[0]));
printf("sizeof:a[1]==%d\n",sizeof(a[1]));
printf("sizeof:a[2]==%d\n",sizeof(a[2]));
printf("\nsizeof a = %d\n",sizeof(a));
for(i=0;i<3;i++)
{
printf("\n&(a[0]+%d)=%p\n",i,a[0]+i);
}
for(i=0;i<3;i++)
{
printf("\n&(a[1]+%d)=%p\n",i,a[1]+i);
}
for(i=0;i<3;i++)
{
printf("\n&(a[2]+%d)=%p\n",i,a[2]+i);
}
printf("\n&a+0=%p\n",a+0);
printf("\n&a+1=%p\n",a+1);
printf("\n&a+2=%p\n",a+2);
printf("&(a[1]+2)=%p\n",a[1]+2);
printf("\na[1]+2=%f\n",*(a[1]+2));
printf("&(a+1)=%p\n",a+1);
printf("a+1=%f\n",*(a+1));
}
float a[3][3]={0,1,2,3,4,5,6,7,8};
&a[0]=0xbfbfeac0
&a[1]=0xbfbfeacc
&a[2]=0xbfbfead8
&a[0][0]=0xbfbfeac0
&a[1][0]=0xbfbfeacc
&a[2][0]=0xbfbfead8
&a[2][2]=0xbfbfeae0
a[0][0]=0xbfbfeac0
a[0][1]=0xbfbfeac4
a[0][2]=0xbfbfeac8
a[1][0]=0xbfbfeacc
a[1][1]=0xbfbfead0
a[1][2]=0xbfbfead4
a[2][0]=0xbfbfead8
a[2][1]=0xbfbfeadc
a[2][2]=0xbfbfeae0
a+0=0xbfbfeac0
a+1=0xbfbfeacc
a+2=0xbfbfead8
sizeof a = 36
sizeof:a[0]==12
sizeof:a[1]==12
sizeof:a[2]==12
&(a[0]+0)=0xbfbfeac0
&(a[0]+1)=0xbfbfeac4
&(a[0]+2)=0xbfbfeac8
&(a[1]+0)=0xbfbfeacc
&(a[1]+1)=0xbfbfead0
&(a[1]+2)=0xbfbfead4
&(a[2]+0)=0xbfbfead8
&(a[2]+1)=0xbfbfeadc
&(a[2]+2)=0xbfbfeae0
&a+0=0xbfbfeac0
&a+1=0xbfbfeacc
&a+2=0xbfbfead8
&(a[1]+2)=0xbfbfead4
a[1]+2=5.000000
&(a+1)=0xbfbfeacc
a+1=5.000003
一些c 常见技巧
/*砍掉字符串中所有空格*/
void CUT_ALL_SPC(char *s)
{
int i,n;
char d[10000];
n=0;
for(i=0;i
if(s[i]!=32)
{
d[n]=s[i];
n++;
}
d[n]=0;
strcpy(s,d);
}
/*砍掉字符串左边空格*/
void CUT_LEFT_SPACE(char *s)
{
int i,j,k=0;
i=strlen(s)+1;
for(j=0;j
for(k=0;j
}
/*砍掉字符串右边空格*/
void CUT_RIGHT_SPACE(char *s)
{
int i,j;
i=strlen(s)-1;
for(j=i;j>-1;j--) if (s[j]!=' ') break;
s[j+1]=0;
}
/*获取机器日期*/
int DATE(char *s,char type)
{
char dat[30];
int num;
struct tm *tblock;
time_t t;
t=time(NULL);
tblock=localtime(&t);
strcpy(dt,asctime(tblock));
strcpy(s,"");
switch(type)
{
case 'N':
num=(*tblock).tm_year+1900;
itoa(num,s,10);
break;
case 'Y':
num=(*tblock).tm_mon+1;
itoa(num,s,10);
break;
case 'R':
num=(*tblock).tm_mday;
itoa(num,s,10);
break;
case 'S':
strcpy(dt,asctime(tblock));
MID(s,dt,12,8);
break;
}
}
return num;
}
/*成批拷贝文件*/
int CopyFile(char *sfile,char *dfile,int f2d,int barlong,int height,int x,int y)
{
int Copyfile(char *sf,char *df);
int MakeNdir(char *Dir);
char filename[200][13],d[40],s[40],s1[40];
struct ffblk ffblk;
int done,i,j,l,len;
i=0;
done=findfirst(sfile,&ffblk,0);
if (!done) strcpy(filename[i],ffblk.ff_name);
while(!done)
{
done=findnext(&ffblk);
if (!done)
{
i++;
strcpy(filename[i],ffblk.ff_name);
}
}
if (f2d)
{
Copyfile(sfile,dfile);
return 1;
}
strcpy(s,sfile);
l=strlen(sfile);
for(j=l-1;j>=0;j--)
if (s[j]=='')
{
s[j+1]=0;
break;
}
/*拷贝一个文件*/
int Copyfile(char *sf,char *df)
{
FILE *in,*out;
char ch;
in=0;
out=0;
if ((in=fopen(sf,"rb"))==NULL)
exit(0);
if ((out=fopen(df,"wb"))==NULL)
exit(0);
while(!feof(in))
{
ch=fgetc(in);
if (ferror(in)) return 0;
fputc(ch,out);
if (ferror(out)) return 0;
}
fclose(in);
fclose(out);
return 1;
}
/*建立目录*/
int MakeNdir(char *Dir)
{
int i,l,j;
char s[10][40];
j=0;
l=strlen(Dir);
for(i=0;i
if (Dir[i]=='')
{
LEFT(s[j],Dir,i);
j++;
}
strcpy(s[j],Dir);
for(i=0;i<=j;i++)
if (access(s[i],0)) mkdir(s[i]);
return 1;
}
/*得到目录*/
int GetDir(char *dirF,char dataK[][14])
{
struct ffblk ffblk;
int done;
int i;
i=0;
done=findfirst(dirF,&ffblk,FA_DIREC);
while(!done)
{
if (ffblk.ff_attrib==16||ffblk.ff_attrib==17)
{
strcpy(dataK[i],ffblk.ff_name);
strcat(dataK[i],"");
i++;
}
done=findnext(&ffblk);
}
return i;
}
/*得到文件名*/
int GetFile(char *dirF,char dataK[][14])
{
struct ffblk ffblk;
int done;
int i;
i=0;
done=findfirst(dirF,&ffblk,0);
while(!done)
{
strcpy(dataK[i],ffblk.ff_name);
done=findnext(&ffblk);
i++;
}
return i;
}
/*砍掉字符串中所有空格*/
void CUT_ALL_SPC(char *s)
{
int i,n;
char d[10000];
n=0;
for(i=0;i
if(s[i]!=32)
{
d[n]=s[i];
n++;
}
d[n]=0;
strcpy(s,d);
}
/*砍掉字符串左边空格*/
void CUT_LEFT_SPACE(char *s)
{
int i,j,k=0;
i=strlen(s)+1;
for(j=0;j
for(k=0;j
}
/*砍掉字符串右边空格*/
void CUT_RIGHT_SPACE(char *s)
{
int i,j;
i=strlen(s)-1;
for(j=i;j>-1;j--) if (s[j]!=' ') break;
s[j+1]=0;
}
/*获取机器日期*/
int DATE(char *s,char type)
{
char dat[30];
int num;
struct tm *tblock;
time_t t;
t=time(NULL);
tblock=localtime(&t);
strcpy(dt,asctime(tblock));
strcpy(s,"");
switch(type)
{
case 'N':
num=(*tblock).tm_year+1900;
itoa(num,s,10);
break;
case 'Y':
num=(*tblock).tm_mon+1;
itoa(num,s,10);
break;
case 'R':
num=(*tblock).tm_mday;
itoa(num,s,10);
break;
case 'S':
strcpy(dt,asctime(tblock));
MID(s,dt,12,8);
break;
}
}
return num;
}
/*成批拷贝文件*/
int CopyFile(char *sfile,char *dfile,int f2d,int barlong,int height,int x,int y)
{
int Copyfile(char *sf,char *df);
int MakeNdir(char *Dir);
char filename[200][13],d[40],s[40],s1[40];
struct ffblk ffblk;
int done,i,j,l,len;
i=0;
done=findfirst(sfile,&ffblk,0);
if (!done) strcpy(filename[i],ffblk.ff_name);
while(!done)
{
done=findnext(&ffblk);
if (!done)
{
i++;
strcpy(filename[i],ffblk.ff_name);
}
}
if (f2d)
{
Copyfile(sfile,dfile);
return 1;
}
strcpy(s,sfile);
l=strlen(sfile);
for(j=l-1;j>=0;j--)
if (s[j]=='')
{
s[j+1]=0;
break;
}
/*拷贝一个文件*/
int Copyfile(char *sf,char *df)
{
FILE *in,*out;
char ch;
in=0;
out=0;
if ((in=fopen(sf,"rb"))==NULL)
exit(0);
if ((out=fopen(df,"wb"))==NULL)
exit(0);
while(!feof(in))
{
ch=fgetc(in);
if (ferror(in)) return 0;
fputc(ch,out);
if (ferror(out)) return 0;
}
fclose(in);
fclose(out);
return 1;
}
/*建立目录*/
int MakeNdir(char *Dir)
{
int i,l,j;
char s[10][40];
j=0;
l=strlen(Dir);
for(i=0;i
if (Dir[i]=='')
{
LEFT(s[j],Dir,i);
j++;
}
strcpy(s[j],Dir);
for(i=0;i<=j;i++)
if (access(s[i],0)) mkdir(s[i]);
return 1;
}
/*得到目录*/
int GetDir(char *dirF,char dataK[][14])
{
struct ffblk ffblk;
int done;
int i;
i=0;
done=findfirst(dirF,&ffblk,FA_DIREC);
while(!done)
{
if (ffblk.ff_attrib==16||ffblk.ff_attrib==17)
{
strcpy(dataK[i],ffblk.ff_name);
strcat(dataK[i],"");
i++;
}
done=findnext(&ffblk);
}
return i;
}
/*得到文件名*/
int GetFile(char *dirF,char dataK[][14])
{
struct ffblk ffblk;
int done;
int i;
i=0;
done=findfirst(dirF,&ffblk,0);
while(!done)
{
strcpy(dataK[i],ffblk.ff_name);
done=findnext(&ffblk);
i++;
}
return i;
}
一 家用高性价比标准配置 平台AMD!!
1.CPU:AMD Athlon64 X2 4000+ AM2(65纳米/盒)
参考价格:465
入选原因:1.几乎没有假盒,因为散装4000+单加杂牌风扇还要2次包装之后利润几乎是0。
2.体制好,配合一般的主板,在不加风冷和电压的情况下几乎全都能稳定超到4800+使用,而且相当稳定。相当于花465元买到了价值710的产品。
2.主板:捷波 悍马HA02-GT
参考价格:580
入选原因: 1.采用的是570x的主芯片组,做工精良。配合4000+稳定的运行在4800+甚至更高。配合AMD的CPU和ATi的显卡组成传说中的3A. 兼容性现在以不是问题,但这样的组合更给人自信。570X的芯片组也是现在组建A平台的最佳主流选择。
2.现在的570x的主板几乎都是捷波的代工,虽然这款是价格最高的,但是毫无疑问也是性能最稳定,做工最奢华的。我自己用的是盈通的A570X,其实就是这款主板的简化版,只是简化了一个供电和散热 ,但价格便宜了180。稳超4800+!
3.显卡:蓝宝石 X1950GT黄金版
参考价格:790
入选原因:显卡这一块,确实很难选择,但是考虑到整机的价位水准和使用性的话,我还是给大家推荐1950GT.目前所有的DX9.0游戏1950GT都能给大家一个满意的成绩。而且这个价格也是相当到位。蓝宝石这个牌子在论坛里的口碑更不用说。
4.硬盘:希捷 160G 7200.10 8M(串口/5年盒)
参考价格:465
入选理由:大家共同的选择,虽然希捷硬盘的价格偏贵,但是毕竟是5年的质保,也算给大家一个心理安慰吧。我自己用的是日立的,这里还是给大家推荐了希捷,为什么?因为这是论坛里面大多数人的选择,少数服从多数的选择。160G硬盘对家用来说已经足够了,配合刻录机使用,我个人觉得更大的硬盘完全没有必要。80G的话,现在来说,确实有点落伍了,不是么?
5.内存:Kingmax 1GB DDR2 800 Long-DIMM
Kingmax 1GB DDR2 800 Long-DIMM
参考价格:185+185=370
入选理由:最可靠,最便宜的800内存之一。2G双通道轻松对应现在的各种电脑使用环境。
其实667也是很好的选择,我在这里推荐800是因为以后的延续性更好,而且这个价位,确实也不贵。
6.机箱:新战线新灵830
参考价格:185
入选理由:机箱这个东东,大家各有所爱,但是这款机箱确实是相同价位中,做工最扎实!设计最精良!价格最便宜!最人性化的。很像品牌机的机箱,模样中规中矩。本人使用的就是这个,怎么说也是在中关村转了一天之后的选择。所以特别推荐给大家。
7.电源:航嘉 磐石400
参考价格:280
入选理由:额定功率350w,最大输出450w。配合以上推荐的配置,游刃有余。可以完全发挥出个配件的性能,稳定,安静,可靠。航嘉长城这些电源的口碑还是很好的,在论坛里面的选择也是最多的。
3.DVD LG GSA-H55L
参考价格:280
入选理由:DVD刻录的选择很多,这个还可以,带光雕。
整体价格:3415左右!
配合22寸/24寸液晶显示器,家用经典大众主流电脑。以上配件稳定,可靠,没有任何兼容问题,没有任何使用中的瓶颈。还可以适当超频(4000+到4800+稳定)。性价比高。当然,很多配件朋友们还可以找到更便宜的代替,我自己也是这样做的,但是以上配置绝对是今年论坛里面最经典的一套。
整理一下
CPU P4 620 1200元
RAM DDR2 533 kingston/kingmax 512M 300元
HardDisk 160G SATA 500元
VideoCard 双敏7058 Turbo 699元
MotherBoard ASUS P5PL2 999元
Monitor samsung or viewsonic 1040
DVD-ROM 190元 or DVD-RW 399元
其他 300元
总价5200元左右吧,虽然比很多人期望的5000元的价格贵了200元,但是,你在使用的时候却远远不是这500元可以买到的,东西虽贵,但是值得。最后就是装机的时候要祝你好运
===============================================================
还有个`设计的话CPU最好用Intel 游戏最好用AMD
显示器 明基 FP71G+s 1980元 示器主色为银色,响应时间为8ms,具备DVI数字接口,显示效果不错
显示器 三星710N 12ms 2200元 (17寸液晶显示器)
光驱 BenQ 1650V 180元
音箱 慧海 乐吧D-102 150元 慧海的音箱更是使整套配置充满了时尚的气息,再搭配一台轻薄的17寸液晶显示器,更体现出了机主MM的简洁、大方。
漫步者 R101T06
机箱 永阳YY-A209 250元(挺漂亮的)
鼠标、键盘 三星anypro 水系套装(水晶键+水之灵动) 120元
罗技 光电高手800套装
整机主要以白色和银色为主,既美观,又不会造成颜色的巨大反差。
美观虽然很重要,但可不能作为选择的唯一参考,一定要在权衡了性能、价格、质量后再决定选购,切勿为了美观牺牲了性能。
cpu和内在的东西一定要强悍。。。思考中。。
内存:金士顿 800 1G RAM DDR2 533 kingston/kingmax 512M 300元
4.硬盘:希捷 160G 7200.10 8M(串口/5年盒)
参考价格:465
入选理由:大家共同的选择,虽然希捷硬盘的价格偏贵,但是毕竟是5年的质保,也算给大家一个心理安慰吧。我自己用的是日立的,这里还是给大家推荐了希捷,为什么?因为这是论坛里面大多数人的选择,少数服从多数的选择。160G硬盘对家用来说已经足够了,配合刻录机使用,我个人觉得更大的硬盘完全没有必要。80G的话,现在来说,确实有点落伍了,不是么?
5.内存:Kingmax 1GB DDR2 800 Long-DIMM
Kingmax 1GB DDR2 800 Long-DIMM
参考价格:185+185=370
入选理由:最可靠,最便宜的800内存之一。2G双通道轻松对应现在的各种电脑使用环境。
硬盘:西捷160G HardDisk 160G SATA 500元
MotherBoard ASUS P5PL2 999元
1.CPU P4 620 1200元
2.CPU:AMD Athlon64 X2 4000+ AM2(65纳米/盒)
入选原因:
1.几乎没有假盒,因为散装4000+单加杂牌风扇还要2次包装之后利润几乎是0。
2.体制好,配合一般的主板,在不加风冷和电压的情况下几乎全都能稳定超到4800+使用,而且相当稳定。相当于花465元买到了价值710的产品。
电源:航嘉 磐石400参考价格:280
电源长城350的+机箱240元左右
入选理由:额定功率350w,最大输出450w。配合以上推荐的配置,游刃有余。可以完全发挥出个配件的性能,稳定,安静,可靠。航嘉长城这些电源的口碑还是很好的,在论坛里面的选择也是最多的。
DVD-ROM 190元 or DVD-RW 399元
显卡7900/8600/8800系列的有钱就买8800
======================================================================
华硕945P主板型号为P5LD2 SE,采用Intel 945P+ICH7芯片组,不仅支持1066MHz的前端总线,同时还支持新一代65nm制程的单、双核CPU以及EM64T、EIST、Hyper-Threading等新技术。供电部分,采用三相供电设计,搭配品质过硬的松下电容,提供了良好的稳定性。扩展部分,主板提供了主流的一个PCI-E X16、两个PCI-E 1X、三个传统的PCI插槽。内存方面,主板具备四根可支持DDR2 667的内存插槽,最大容量可扩充至4G,足以应付日常使用。另外,主板集成千兆网卡以及5.1声道声卡。
编辑点评:
虽然此款主板在这5款主板中是最贵的,基本已经达到945系列主板的价格顶峰,基本同965系列的二线主板一个价位,但华硕主板的品质在广大消费支心中还是有一定影响力的。且这款主板的规格确实是相当高。对于800多元的价格还是说得过去的。
总结
P965与945之争早已由价格决定,价格又决定了其产品规格。如果您不是需要8个以上的USB口,更多的SATA接口,千兆网卡……,本着够用的就好的经济原则,选一片性能稳定的945主板搭建双核平台又有什么不可以的呢。
机箱 永阳YY-A209 250元
Kingmax 1GB DDR2 800 Long-DIMM 参考价格:185+185=370 2G双通道
硬盘:西捷160G HardDisk 160G SATA 500元
系列型号:Core 2 Duo(酷睿2) 1220元
电源长城350 150 元
华硕主板945p P5LD2 SE 豪华版本 700元
明基 FP94VW 1949元
鼠标、键盘 三星anypro 水系套装(水晶键+水之灵动) 120元
机箱:永阳YY-A209 价格:250元; 内存:Kingmax 1GB DDR2 800 Long-DIMM 价格:185+185=370 组2G双通道;硬盘:西捷160G HardDisk 160G SATA 500元 cpu:Core 2 Duo(酷睿e6300) 1220元;电源:长城350 150 元; 主板:华硕主板945p P5LD2 SE 豪华版本 700元 液显:明基 FP94VW 1949元 鼠标、键盘 三星anypro 水系套装(水晶键+水之灵动) 120元 光驱:BenQ 1650V 180元 合计(5439)
1.CPU:AMD Athlon64 X2 4000+ AM2(65纳米/盒)
参考价格:465
入选原因:1.几乎没有假盒,因为散装4000+单加杂牌风扇还要2次包装之后利润几乎是0。
2.体制好,配合一般的主板,在不加风冷和电压的情况下几乎全都能稳定超到4800+使用,而且相当稳定。相当于花465元买到了价值710的产品。
2.主板:捷波 悍马HA02-GT
参考价格:580
入选原因: 1.采用的是570x的主芯片组,做工精良。配合4000+稳定的运行在4800+甚至更高。配合AMD的CPU和ATi的显卡组成传说中的3A. 兼容性现在以不是问题,但这样的组合更给人自信。570X的芯片组也是现在组建A平台的最佳主流选择。
2.现在的570x的主板几乎都是捷波的代工,虽然这款是价格最高的,但是毫无疑问也是性能最稳定,做工最奢华的。我自己用的是盈通的A570X,其实就是这款主板的简化版,只是简化了一个供电和散热 ,但价格便宜了180。稳超4800+!
3.显卡:蓝宝石 X1950GT黄金版
参考价格:790
入选原因:显卡这一块,确实很难选择,但是考虑到整机的价位水准和使用性的话,我还是给大家推荐1950GT.目前所有的DX9.0游戏1950GT都能给大家一个满意的成绩。而且这个价格也是相当到位。蓝宝石这个牌子在论坛里的口碑更不用说。
4.硬盘:希捷 160G 7200.10 8M(串口/5年盒)
参考价格:465
入选理由:大家共同的选择,虽然希捷硬盘的价格偏贵,但是毕竟是5年的质保,也算给大家一个心理安慰吧。我自己用的是日立的,这里还是给大家推荐了希捷,为什么?因为这是论坛里面大多数人的选择,少数服从多数的选择。160G硬盘对家用来说已经足够了,配合刻录机使用,我个人觉得更大的硬盘完全没有必要。80G的话,现在来说,确实有点落伍了,不是么?
5.内存:Kingmax 1GB DDR2 800 Long-DIMM
Kingmax 1GB DDR2 800 Long-DIMM
参考价格:185+185=370
入选理由:最可靠,最便宜的800内存之一。2G双通道轻松对应现在的各种电脑使用环境。
其实667也是很好的选择,我在这里推荐800是因为以后的延续性更好,而且这个价位,确实也不贵。
6.机箱:新战线新灵830
参考价格:185
入选理由:机箱这个东东,大家各有所爱,但是这款机箱确实是相同价位中,做工最扎实!设计最精良!价格最便宜!最人性化的。很像品牌机的机箱,模样中规中矩。本人使用的就是这个,怎么说也是在中关村转了一天之后的选择。所以特别推荐给大家。
7.电源:航嘉 磐石400
参考价格:280
入选理由:额定功率350w,最大输出450w。配合以上推荐的配置,游刃有余。可以完全发挥出个配件的性能,稳定,安静,可靠。航嘉长城这些电源的口碑还是很好的,在论坛里面的选择也是最多的。
3.DVD LG GSA-H55L
参考价格:280
入选理由:DVD刻录的选择很多,这个还可以,带光雕。
整体价格:3415左右!
配合22寸/24寸液晶显示器,家用经典大众主流电脑。以上配件稳定,可靠,没有任何兼容问题,没有任何使用中的瓶颈。还可以适当超频(4000+到4800+稳定)。性价比高。当然,很多配件朋友们还可以找到更便宜的代替,我自己也是这样做的,但是以上配置绝对是今年论坛里面最经典的一套。
整理一下
CPU P4 620 1200元
RAM DDR2 533 kingston/kingmax 512M 300元
HardDisk 160G SATA 500元
VideoCard 双敏7058 Turbo 699元
MotherBoard ASUS P5PL2 999元
Monitor samsung or viewsonic 1040
DVD-ROM 190元 or DVD-RW 399元
其他 300元
总价5200元左右吧,虽然比很多人期望的5000元的价格贵了200元,但是,你在使用的时候却远远不是这500元可以买到的,东西虽贵,但是值得。最后就是装机的时候要祝你好运
===============================================================
还有个`设计的话CPU最好用Intel 游戏最好用AMD
显示器 明基 FP71G+s 1980元 示器主色为银色,响应时间为8ms,具备DVI数字接口,显示效果不错
显示器 三星710N 12ms 2200元 (17寸液晶显示器)
光驱 BenQ 1650V 180元
音箱 慧海 乐吧D-102 150元 慧海的音箱更是使整套配置充满了时尚的气息,再搭配一台轻薄的17寸液晶显示器,更体现出了机主MM的简洁、大方。
漫步者 R101T06
机箱 永阳YY-A209 250元(挺漂亮的)
鼠标、键盘 三星anypro 水系套装(水晶键+水之灵动) 120元
罗技 光电高手800套装
整机主要以白色和银色为主,既美观,又不会造成颜色的巨大反差。
美观虽然很重要,但可不能作为选择的唯一参考,一定要在权衡了性能、价格、质量后再决定选购,切勿为了美观牺牲了性能。
cpu和内在的东西一定要强悍。。。思考中。。
内存:金士顿 800 1G RAM DDR2 533 kingston/kingmax 512M 300元
4.硬盘:希捷 160G 7200.10 8M(串口/5年盒)
参考价格:465
入选理由:大家共同的选择,虽然希捷硬盘的价格偏贵,但是毕竟是5年的质保,也算给大家一个心理安慰吧。我自己用的是日立的,这里还是给大家推荐了希捷,为什么?因为这是论坛里面大多数人的选择,少数服从多数的选择。160G硬盘对家用来说已经足够了,配合刻录机使用,我个人觉得更大的硬盘完全没有必要。80G的话,现在来说,确实有点落伍了,不是么?
5.内存:Kingmax 1GB DDR2 800 Long-DIMM
Kingmax 1GB DDR2 800 Long-DIMM
参考价格:185+185=370
入选理由:最可靠,最便宜的800内存之一。2G双通道轻松对应现在的各种电脑使用环境。
硬盘:西捷160G HardDisk 160G SATA 500元
MotherBoard ASUS P5PL2 999元
1.CPU P4 620 1200元
2.CPU:AMD Athlon64 X2 4000+ AM2(65纳米/盒)
入选原因:
1.几乎没有假盒,因为散装4000+单加杂牌风扇还要2次包装之后利润几乎是0。
2.体制好,配合一般的主板,在不加风冷和电压的情况下几乎全都能稳定超到4800+使用,而且相当稳定。相当于花465元买到了价值710的产品。
电源:航嘉 磐石400参考价格:280
电源长城350的+机箱240元左右
入选理由:额定功率350w,最大输出450w。配合以上推荐的配置,游刃有余。可以完全发挥出个配件的性能,稳定,安静,可靠。航嘉长城这些电源的口碑还是很好的,在论坛里面的选择也是最多的。
DVD-ROM 190元 or DVD-RW 399元
显卡7900/8600/8800系列的有钱就买8800
======================================================================
华硕945P主板型号为P5LD2 SE,采用Intel 945P+ICH7芯片组,不仅支持1066MHz的前端总线,同时还支持新一代65nm制程的单、双核CPU以及EM64T、EIST、Hyper-Threading等新技术。供电部分,采用三相供电设计,搭配品质过硬的松下电容,提供了良好的稳定性。扩展部分,主板提供了主流的一个PCI-E X16、两个PCI-E 1X、三个传统的PCI插槽。内存方面,主板具备四根可支持DDR2 667的内存插槽,最大容量可扩充至4G,足以应付日常使用。另外,主板集成千兆网卡以及5.1声道声卡。
编辑点评:
虽然此款主板在这5款主板中是最贵的,基本已经达到945系列主板的价格顶峰,基本同965系列的二线主板一个价位,但华硕主板的品质在广大消费支心中还是有一定影响力的。且这款主板的规格确实是相当高。对于800多元的价格还是说得过去的。
总结
P965与945之争早已由价格决定,价格又决定了其产品规格。如果您不是需要8个以上的USB口,更多的SATA接口,千兆网卡……,本着够用的就好的经济原则,选一片性能稳定的945主板搭建双核平台又有什么不可以的呢。
机箱 永阳YY-A209 250元
Kingmax 1GB DDR2 800 Long-DIMM 参考价格:185+185=370 2G双通道
硬盘:西捷160G HardDisk 160G SATA 500元
系列型号:Core 2 Duo(酷睿2) 1220元
电源长城350 150 元
华硕主板945p P5LD2 SE 豪华版本 700元
明基 FP94VW 1949元
鼠标、键盘 三星anypro 水系套装(水晶键+水之灵动) 120元
机箱:永阳YY-A209 价格:250元; 内存:Kingmax 1GB DDR2 800 Long-DIMM 价格:185+185=370 组2G双通道;硬盘:西捷160G HardDisk 160G SATA 500元 cpu:Core 2 Duo(酷睿e6300) 1220元;电源:长城350 150 元; 主板:华硕主板945p P5LD2 SE 豪华版本 700元 液显:明基 FP94VW 1949元 鼠标、键盘 三星anypro 水系套装(水晶键+水之灵动) 120元 光驱:BenQ 1650V 180元 合计(5439)
如果安装了securetCRT那么有一个好东西,
/usr/ports/comms/zmtx-zmrx
安装后,zmtx是用来发送文件,zmrx是用来接收文件 的。“接收”和“发送”是相对于FreeBSD的那台机器来说的。
出现没有那个目录,是没有安装Port:
root@iZ25z0ugwgtZ:~ # cd /usr/ports/comms/zm
/usr/ports/comms/ not found
root@iZ25z0ugwgtZ:~ # cd /usr/ports/comms/
/usr/ports/comms/: No such file or directory.
————————————————————————————————————————————————————
FreeBSD系统的一个非常方便的地方就是它的ports,如果系统没安装ports的话可用下面几种方式安装ports:
1、sysinstall--->configure----->distributions----->ports----->选择Ftp或其它的网络连接即可。
2、直接从官方站点下载ports.tar.gz再解压,这样很快。位置:ftp://ftp.freebsd.org/pub/FreeBSD/ports/ports/parts.tar.gz
3、如果是FreeBSD6。0,那么运行:
#portsnap fetch
#portsnap extract
#portsnap update
另外:
更新ports后,如果运行make search name=xxx不成功,请在/usr/ports下运行make fetchindex。
摘自:http://www.jb51.net/os/Unix/1484.html
————————————————————————————————————————————————————
root@iZ25z0ugwgtZ:~ # sysinstall
sysinstall: Command not found.
现在的freebsd 10已经没有了sysinstall 的命令了,已经换成bsdinstall 这个安装命令,和bsdconfig配置的这两个命令了,你要装mpd5可以用ports来安装
whereis mpd5
cd /usr/ports/net/mpd5
make fetch-recursive
make config-recursive
make install clean
这样就装上去了,然后就是修改配置文件,设定开机启动
/usr/ports/comms/zmtx-zmrx
安装后,zmtx是用来发送文件,zmrx是用来接收文件 的。“接收”和“发送”是相对于FreeBSD的那台机器来说的。
出现没有那个目录,是没有安装Port:
root@iZ25z0ugwgtZ:~ # cd /usr/ports/comms/zm
/usr/ports/comms/ not found
root@iZ25z0ugwgtZ:~ # cd /usr/ports/comms/
/usr/ports/comms/: No such file or directory.
————————————————————————————————————————————————————
FreeBSD系统的一个非常方便的地方就是它的ports,如果系统没安装ports的话可用下面几种方式安装ports:
1、sysinstall--->configure----->distributions----->ports----->选择Ftp或其它的网络连接即可。
2、直接从官方站点下载ports.tar.gz再解压,这样很快。位置:ftp://ftp.freebsd.org/pub/FreeBSD/ports/ports/parts.tar.gz
3、如果是FreeBSD6。0,那么运行:
#portsnap fetch
#portsnap extract
#portsnap update
另外:
更新ports后,如果运行make search name=xxx不成功,请在/usr/ports下运行make fetchindex。
摘自:http://www.jb51.net/os/Unix/1484.html
————————————————————————————————————————————————————
root@iZ25z0ugwgtZ:~ # sysinstall
sysinstall: Command not found.
现在的freebsd 10已经没有了sysinstall 的命令了,已经换成bsdinstall 这个安装命令,和bsdconfig配置的这两个命令了,你要装mpd5可以用ports来安装
whereis mpd5
cd /usr/ports/net/mpd5
make fetch-recursive
make config-recursive
make install clean
这样就装上去了,然后就是修改配置文件,设定开机启动
转自IT168 觉得对框架的认识和定位很有帮助,虽然讲的不是php,但是思想是一致的。
1 空前繁荣的开源世界
大致2000年以前,Java世界还是Sun一言九鼎,唯我独尊的时代。Sun发布的任何规范和标准都无一例外地被Java社区有意无意的追捧着,Java世界沉浸在一片歌功颂德,前拥后簇的氛围里。IBM,Bea,Oracle这些Java阵营的代表者也都为能最先最快实现Sun的各种规范而弹冠相庆。
但这三四年来,Java的列车驶进了春秋战国百家争鸣,百花齐放的时代, Apache,JBoss,opensymphony,Eclipse,Codehaus等开源组织个个门庭若市,车水马龙。Java世界似乎天天在过年——张灯结彩,新桃换旧符。打开theserverside.com网站,每天映入眼帘是一条条各种开源项目发布、升级的新闻。虽然嘈杂了些,但却异彩纷呈,惊艳四座。在Java世界里,十室之内必有隐士,十步之内必有芳草,有才华的程序员太多了,抑或怀才的程序员被独裁式的统治压抑太久了,一旦找到了海德公园,庞涓、孙膑、苏秦、张仪式的高手纷纷走出隐居的鬼谷,在开源舞台上劲舞一支,高歌一曲,用一个个开源项目彰显着自己独特的魅力。
从客户端到数据库,从页面流程控制到业务流程控制,从全文搜索到地图搜索,从论坛到博客,在各种应用领域你都可以方便地找到多个相似的Java开源框架。开源框架的空前繁荣有力的促进了Java技术的交流和分享。一些面向开源的社区,论坛纷纷建立,国内比较著名的就有满江红开源论坛、中文Spring论坛、JavaScud开源平台、JavaEye社区等,宣讲、争论、协作、互动,无数激情和智慧碰撞出耀眼的火花。
随着开源项目的日益增多,国内甚至出现了象open-open.com Java开源大全的汇总整理网站,它如一个开源项目的大集市,将开源项目分类整理,提供简要的描述说明信息,方便使用者了解、查询和比较。
开源项目的繁荣还为技术图书业创造了机会,不管是国外的Amazon,还是china-pub或dearbook,开源框架或产品的技术图书,如Spring,Hibernate,Struts,Eclipse等等都成为荣登榜首的畅销先锋。
这场几乎来源于民间的开源飓风给开发者和CTO们的思路和决策带来了巨大的影响,据Bea的调查,全球排名前2000家软件开发公司中有70%以上在使用一种或多种开源框架——多达28%的公司在开发环境中使用了一种以上的应用服务器。
同时开源也给走传统路线的Java巨头们带来战略性的影响:Sun去年宣布将其旗舰产品——Solaris开源;去年IBM向第三方厂商开放了其高性能通用并行文件系统(GPFS)的源代码;Unisys也改变企业战略定位投入开源怀抱等等不胜枚举,它们纷纷将营利模式从原来的产品销售调整为支持与服务。
2 开源框架带来的烦恼
虽然开源的框架、类库越来越丰富,可供选择的替代者越来越多,但Java程序员却感觉自己慢慢陷入到了技术的漩涡之中:因为他们发现只要一段时间不关注开源社区,就有潮水般陌生的技术框架、专业术语、英文缩略词挟裹着一团团亢奋的热浪将自己淹没,让他们觉得随时都有被Java世界抛弃的危险。许多年纪稍大的程序员甚至觉得职位转换,甩掉技术干管理已经时不我待。
选择的困惑
雨后春笋般涌现的开源框架都声称自己是最好的,有过多次因盲从于技术鼓吹而失望伤心的经历后,现在的开发者都变得成熟理智了,他们不会轻易相信某个框架自身的承诺,不会轻易附和他人的宣传,这确实是件好事。为了作出理智的选择,他们往往要自己亲自摸索以做出评判。
有时,我们会发现向上司推荐一个框架已经变成一件困难的事情,因为上司会冒出各种各样的问题:如Webwork比Struts好在哪里?Hibernate和iBatis有什么区别?OpenWFE比之jBpm有什么优势等等。所以要确定一个框架时,往往需要将相似的框架都研究一遍,以便有充足的理由让上司相信我们的选择是最优的。
但是,要将同类的框架都做一次研究并比较优劣并非易事,如开源工作流引擎就有Willow,OpenWFE,jBpm,Werkflow,OSWorkflow等不下30余种的框架,炫耀的声音一个比一个响亮。每种框架都有自己的设计思路和实现方案,况且这种技术预研性的工作,又不可能在项目周期内占用太多的时间,而不深入预研又不可能客观地作出评判,所以往往是熬红的双眼依然带着迷茫的目光。
此外,用人单位为了减少新员工的培训时间,对求职者往往有明确的框架使用技能和经验的要求。求职者为了能找到一个好工作,不得不逼迫自己学习更多的框架,以便让自己拥有更多的求职机会。
搭配的困难
开源的繁荣虽然给各个领域都造就了许多优秀的框架,如Spring,Struts,Hibernate,Lucene、OSCache等等,但却没有出现一个一站式,统管全局的整合开发框架。开发者在享用大餐之前,事先得充当大橱的角色,将这些盐,油、酱、菜按合理的方式调配好。
虽然,我们一直强调整体大于单个的总和,但是如何将单个“个体”正确的组合成发挥更大效应的“整体”却并非易事。因为这些单独的框架都由不同的团队开发,框架与框架之间存在天然的阻抗,这种框架和框架之间的“代沟”需要额外配置和编码才能弥合。
每个框架都拥有自己的配置文件,框架的整合经常带来配置的灾难,如将Spring和Struts整合时,不仅Struts本身的配置文件一个不能少,在Spring中还需要每个Action提供配置信息,而且两者需要遵守一定的契约。
相互搭配的框架和框架之间经常会出现相似的或重复的功能,如何取舍,如何使用往往让开发者们为难。如Spring本身提供了AOP方法返回结果的缓存功能,而Hibernate本身也提供二级缓存,究竟两者都使用呢,还是择一而从?往往中间又会引出很多争论。
框架整合的问题已经日益突出,我们可以在各开源论坛或社区发现大量有关讨论的主题。目前也出现了一些试图解决的框架整合问题的开源项目,如国外的AppFuse,国内的SpringSide,为框架的整合提供了专业的指导。但是并没有很好的解决现实开发中的实际需要。这些整合框架为了增加通用性,网都撒得太大,导致整合框架本身象一个庞然大物,让人望而生畏,定制性和灵巧性上都存在不足,降低了它们的实用性,所以这些整合性的开源项目往往降格为指导性的实例。
升级的困扰
活跃的框架每天都在升级改造,丰富功能。其次由于开源框架在一定程度上存在随意性,往往导致框架在实际使用后,发现大量隐含的Bug,所以有时对某个框架的升级变得不可避免。开源框架比之Sun正规的规范有着更加灵活的升级方式,高低版本不兼容的问题已经成为司空见惯的事情。如著名的Hibernate,其3.0版本和2.0版本的包名都发生了彻底的变化,刚发布的Acegi和低版本也存在很大的差异,无法兼容。
一个整合性的框架由多个出自于不同团队的框架组成,整合框架在这些组合框架之上高位运行,底层框架的升级变化就造成了组合框架水涨船高的局面,整合框架脆弱的稳定性很容易被打破。
组合框架的升级还直接带来了开发团队学习的压力,为了熟悉框架新功能和改进,在开发工作之余,他们不得不努力压榨自己的业余时间不断地充电学习。总是某个框架新功能学习还未完成,另一个框架的新版本又在一阵欢呼声中闪亮登场,让开发人员发现自己所有的努力只是一场骑牛追马游戏。
3 开发者如何走出迷局
框架的爆炸性增长和技术更替一日千里的速度,让刚刚从传统J2EE迷局中走出来的开发者重新堕入了新的困境之中。有许多切身体验的开发者在网上大倒苦水,甚至有许多声音在呐喊,希望重新回到JSP+JavaBean+JDBC那个纯真的年代中去。
框架的作者们本想还软件开发一个清新美满的世界,不想个体性的良性企盼变成了一种整体性的混乱纷争。在纷繁复杂的开源世界如何走出迷局和困境,把握自己技术航船的方向,是每个开发者们冥思遐想的事情。
重点学习 触类旁通
每个人的时间是有限的,对于周期紧,进度急,加班赶的开发者来说更加如此,使得开发者不可能 “识遍天下字,读尽人间书”逐个学习框架。选择好适合自己、适合项目的框架进行重点学习尤为重要。不但要掌握技术细节,更要理解框架的原理和思想,这样在接触相关框架时,我们才能触类旁通,慧眼识真。
如果你深入理解了Struts框架的MVC的原理和思想,在接触Tapestry,Spring MVC等框架时,你会发现两者只是形上的区别,而非质上的差异,即使因现实需要确实要转换框架时,也可以轻松平滑地过渡。
不求最好 但求适用
开发人员往往都是完美主义者,吹毛求疵,带着浓重的偏执狂倾向。是的,偏执狂是优秀程序员的一个特点,时下《只有偏执狂才能生存》也正在大卖热卖,Rod Johnson,Gavin King,Oberg也都是偏执狂。
但在有进度工期压力的情况下,我们不得不向实现妥协。对于公司来说,利润永远都是第一位的,不管用不用框架或用什么框架,只要能如期保质保量完成用户的所有功能需求,就是最好的项目。客户永远看不到,也不关心你使用了哪个优秀的技术和框架。
所以,在实际的开发中,也许我们常常需要委曲内心的冲动,只要目前的框架能满足需求,我们没有必须象服装界一样赶追时髦,一切不求最好,但求适用。
如果Spring Template JDBC已经很好的满足了目前的需求,就没有必要一定要上Hibernate,如果自己开发的简要列表控件效果不错,就无须转换为ExtremeTable。新框架的学习需要代价,但这种代价的价值在实际发挥功效之前是不被肯定的。况且看似不合时宜的那些简单而古老的技术也可以做出强大的系统,如世界上最大的java项目——巴西全国医疗系统,就是构建在JSP+JavaBean+Servlet之上。
注重积累 搭建平台
我们常常发现一些软件公司自身没有任何积累,完全寄希望于这些整合框架解决所有的问题。开源框架解决的都是某个领域的通用性问题,每个公司由于其所处行业,服务用户的不同,要求公司拥有自己的解决方案,框架的通用性和公司的个性化需求是存在矛盾的。软件公司应该加强自身的积累,在这些框架的基础上搭建好符合自身需求的快速开发平台,屏蔽掉底层框架的复杂功能和细枝末节,降低对开发人员的技能要求,以便新员工能够快速参与到项目中,而无需进行一个个开源框架的学习。
虽然这种积累和平台的建设会耗费额外的工作量,但首先它是一个循序渐进的过程,其次这种任务仅由两三个技术突出的技术人员承担,带来的好处是直接降低了其他开发人员使用难度和技术要求,在一定程序上避免了开源框架的所带来的不稳定性影响。
4 小结
开源的繁荣带来了丰富的框架,有力的推动了业界的发展,同时我们也看到,这种繁荣所带来的惊喜背后紧跟着许多困惑的眼神,迷失在繁荣的混乱之中的开发者们希望走出困惑,走出迷局。
如何在嘈杂喧闹的开源世界把握方向寻求突破,不管是对于开发者还是软件公司的决策者都值得深深的思考。
1 空前繁荣的开源世界
大致2000年以前,Java世界还是Sun一言九鼎,唯我独尊的时代。Sun发布的任何规范和标准都无一例外地被Java社区有意无意的追捧着,Java世界沉浸在一片歌功颂德,前拥后簇的氛围里。IBM,Bea,Oracle这些Java阵营的代表者也都为能最先最快实现Sun的各种规范而弹冠相庆。
但这三四年来,Java的列车驶进了春秋战国百家争鸣,百花齐放的时代, Apache,JBoss,opensymphony,Eclipse,Codehaus等开源组织个个门庭若市,车水马龙。Java世界似乎天天在过年——张灯结彩,新桃换旧符。打开theserverside.com网站,每天映入眼帘是一条条各种开源项目发布、升级的新闻。虽然嘈杂了些,但却异彩纷呈,惊艳四座。在Java世界里,十室之内必有隐士,十步之内必有芳草,有才华的程序员太多了,抑或怀才的程序员被独裁式的统治压抑太久了,一旦找到了海德公园,庞涓、孙膑、苏秦、张仪式的高手纷纷走出隐居的鬼谷,在开源舞台上劲舞一支,高歌一曲,用一个个开源项目彰显着自己独特的魅力。
从客户端到数据库,从页面流程控制到业务流程控制,从全文搜索到地图搜索,从论坛到博客,在各种应用领域你都可以方便地找到多个相似的Java开源框架。开源框架的空前繁荣有力的促进了Java技术的交流和分享。一些面向开源的社区,论坛纷纷建立,国内比较著名的就有满江红开源论坛、中文Spring论坛、JavaScud开源平台、JavaEye社区等,宣讲、争论、协作、互动,无数激情和智慧碰撞出耀眼的火花。
随着开源项目的日益增多,国内甚至出现了象open-open.com Java开源大全的汇总整理网站,它如一个开源项目的大集市,将开源项目分类整理,提供简要的描述说明信息,方便使用者了解、查询和比较。
开源项目的繁荣还为技术图书业创造了机会,不管是国外的Amazon,还是china-pub或dearbook,开源框架或产品的技术图书,如Spring,Hibernate,Struts,Eclipse等等都成为荣登榜首的畅销先锋。
这场几乎来源于民间的开源飓风给开发者和CTO们的思路和决策带来了巨大的影响,据Bea的调查,全球排名前2000家软件开发公司中有70%以上在使用一种或多种开源框架——多达28%的公司在开发环境中使用了一种以上的应用服务器。
同时开源也给走传统路线的Java巨头们带来战略性的影响:Sun去年宣布将其旗舰产品——Solaris开源;去年IBM向第三方厂商开放了其高性能通用并行文件系统(GPFS)的源代码;Unisys也改变企业战略定位投入开源怀抱等等不胜枚举,它们纷纷将营利模式从原来的产品销售调整为支持与服务。
2 开源框架带来的烦恼
虽然开源的框架、类库越来越丰富,可供选择的替代者越来越多,但Java程序员却感觉自己慢慢陷入到了技术的漩涡之中:因为他们发现只要一段时间不关注开源社区,就有潮水般陌生的技术框架、专业术语、英文缩略词挟裹着一团团亢奋的热浪将自己淹没,让他们觉得随时都有被Java世界抛弃的危险。许多年纪稍大的程序员甚至觉得职位转换,甩掉技术干管理已经时不我待。
选择的困惑
雨后春笋般涌现的开源框架都声称自己是最好的,有过多次因盲从于技术鼓吹而失望伤心的经历后,现在的开发者都变得成熟理智了,他们不会轻易相信某个框架自身的承诺,不会轻易附和他人的宣传,这确实是件好事。为了作出理智的选择,他们往往要自己亲自摸索以做出评判。
有时,我们会发现向上司推荐一个框架已经变成一件困难的事情,因为上司会冒出各种各样的问题:如Webwork比Struts好在哪里?Hibernate和iBatis有什么区别?OpenWFE比之jBpm有什么优势等等。所以要确定一个框架时,往往需要将相似的框架都研究一遍,以便有充足的理由让上司相信我们的选择是最优的。
但是,要将同类的框架都做一次研究并比较优劣并非易事,如开源工作流引擎就有Willow,OpenWFE,jBpm,Werkflow,OSWorkflow等不下30余种的框架,炫耀的声音一个比一个响亮。每种框架都有自己的设计思路和实现方案,况且这种技术预研性的工作,又不可能在项目周期内占用太多的时间,而不深入预研又不可能客观地作出评判,所以往往是熬红的双眼依然带着迷茫的目光。
此外,用人单位为了减少新员工的培训时间,对求职者往往有明确的框架使用技能和经验的要求。求职者为了能找到一个好工作,不得不逼迫自己学习更多的框架,以便让自己拥有更多的求职机会。
搭配的困难
开源的繁荣虽然给各个领域都造就了许多优秀的框架,如Spring,Struts,Hibernate,Lucene、OSCache等等,但却没有出现一个一站式,统管全局的整合开发框架。开发者在享用大餐之前,事先得充当大橱的角色,将这些盐,油、酱、菜按合理的方式调配好。
虽然,我们一直强调整体大于单个的总和,但是如何将单个“个体”正确的组合成发挥更大效应的“整体”却并非易事。因为这些单独的框架都由不同的团队开发,框架与框架之间存在天然的阻抗,这种框架和框架之间的“代沟”需要额外配置和编码才能弥合。
每个框架都拥有自己的配置文件,框架的整合经常带来配置的灾难,如将Spring和Struts整合时,不仅Struts本身的配置文件一个不能少,在Spring中还需要每个Action提供配置信息,而且两者需要遵守一定的契约。
相互搭配的框架和框架之间经常会出现相似的或重复的功能,如何取舍,如何使用往往让开发者们为难。如Spring本身提供了AOP方法返回结果的缓存功能,而Hibernate本身也提供二级缓存,究竟两者都使用呢,还是择一而从?往往中间又会引出很多争论。
框架整合的问题已经日益突出,我们可以在各开源论坛或社区发现大量有关讨论的主题。目前也出现了一些试图解决的框架整合问题的开源项目,如国外的AppFuse,国内的SpringSide,为框架的整合提供了专业的指导。但是并没有很好的解决现实开发中的实际需要。这些整合框架为了增加通用性,网都撒得太大,导致整合框架本身象一个庞然大物,让人望而生畏,定制性和灵巧性上都存在不足,降低了它们的实用性,所以这些整合性的开源项目往往降格为指导性的实例。
升级的困扰
活跃的框架每天都在升级改造,丰富功能。其次由于开源框架在一定程度上存在随意性,往往导致框架在实际使用后,发现大量隐含的Bug,所以有时对某个框架的升级变得不可避免。开源框架比之Sun正规的规范有着更加灵活的升级方式,高低版本不兼容的问题已经成为司空见惯的事情。如著名的Hibernate,其3.0版本和2.0版本的包名都发生了彻底的变化,刚发布的Acegi和低版本也存在很大的差异,无法兼容。
一个整合性的框架由多个出自于不同团队的框架组成,整合框架在这些组合框架之上高位运行,底层框架的升级变化就造成了组合框架水涨船高的局面,整合框架脆弱的稳定性很容易被打破。
组合框架的升级还直接带来了开发团队学习的压力,为了熟悉框架新功能和改进,在开发工作之余,他们不得不努力压榨自己的业余时间不断地充电学习。总是某个框架新功能学习还未完成,另一个框架的新版本又在一阵欢呼声中闪亮登场,让开发人员发现自己所有的努力只是一场骑牛追马游戏。
3 开发者如何走出迷局
框架的爆炸性增长和技术更替一日千里的速度,让刚刚从传统J2EE迷局中走出来的开发者重新堕入了新的困境之中。有许多切身体验的开发者在网上大倒苦水,甚至有许多声音在呐喊,希望重新回到JSP+JavaBean+JDBC那个纯真的年代中去。
框架的作者们本想还软件开发一个清新美满的世界,不想个体性的良性企盼变成了一种整体性的混乱纷争。在纷繁复杂的开源世界如何走出迷局和困境,把握自己技术航船的方向,是每个开发者们冥思遐想的事情。
重点学习 触类旁通
每个人的时间是有限的,对于周期紧,进度急,加班赶的开发者来说更加如此,使得开发者不可能 “识遍天下字,读尽人间书”逐个学习框架。选择好适合自己、适合项目的框架进行重点学习尤为重要。不但要掌握技术细节,更要理解框架的原理和思想,这样在接触相关框架时,我们才能触类旁通,慧眼识真。
如果你深入理解了Struts框架的MVC的原理和思想,在接触Tapestry,Spring MVC等框架时,你会发现两者只是形上的区别,而非质上的差异,即使因现实需要确实要转换框架时,也可以轻松平滑地过渡。
不求最好 但求适用
开发人员往往都是完美主义者,吹毛求疵,带着浓重的偏执狂倾向。是的,偏执狂是优秀程序员的一个特点,时下《只有偏执狂才能生存》也正在大卖热卖,Rod Johnson,Gavin King,Oberg也都是偏执狂。
但在有进度工期压力的情况下,我们不得不向实现妥协。对于公司来说,利润永远都是第一位的,不管用不用框架或用什么框架,只要能如期保质保量完成用户的所有功能需求,就是最好的项目。客户永远看不到,也不关心你使用了哪个优秀的技术和框架。
所以,在实际的开发中,也许我们常常需要委曲内心的冲动,只要目前的框架能满足需求,我们没有必须象服装界一样赶追时髦,一切不求最好,但求适用。
如果Spring Template JDBC已经很好的满足了目前的需求,就没有必要一定要上Hibernate,如果自己开发的简要列表控件效果不错,就无须转换为ExtremeTable。新框架的学习需要代价,但这种代价的价值在实际发挥功效之前是不被肯定的。况且看似不合时宜的那些简单而古老的技术也可以做出强大的系统,如世界上最大的java项目——巴西全国医疗系统,就是构建在JSP+JavaBean+Servlet之上。
注重积累 搭建平台
我们常常发现一些软件公司自身没有任何积累,完全寄希望于这些整合框架解决所有的问题。开源框架解决的都是某个领域的通用性问题,每个公司由于其所处行业,服务用户的不同,要求公司拥有自己的解决方案,框架的通用性和公司的个性化需求是存在矛盾的。软件公司应该加强自身的积累,在这些框架的基础上搭建好符合自身需求的快速开发平台,屏蔽掉底层框架的复杂功能和细枝末节,降低对开发人员的技能要求,以便新员工能够快速参与到项目中,而无需进行一个个开源框架的学习。
虽然这种积累和平台的建设会耗费额外的工作量,但首先它是一个循序渐进的过程,其次这种任务仅由两三个技术突出的技术人员承担,带来的好处是直接降低了其他开发人员使用难度和技术要求,在一定程序上避免了开源框架的所带来的不稳定性影响。
4 小结
开源的繁荣带来了丰富的框架,有力的推动了业界的发展,同时我们也看到,这种繁荣所带来的惊喜背后紧跟着许多困惑的眼神,迷失在繁荣的混乱之中的开发者们希望走出困惑,走出迷局。
如何在嘈杂喧闹的开源世界把握方向寻求突破,不管是对于开发者还是软件公司的决策者都值得深深的思考。
修改字段存储类型:
最常用同步关于库权限查询指令,基础预备:查一下数据库有用户及权限:
select distinct concat('user: ''',user,'''@''',host,''';') as query from mysql.user;
=================================================================
mysql> show create database jackxiang_mysql;
+-----------------+----------------------------------------------------------------------------+
| Database | Create Database |
+-----------------+----------------------------------------------------------------------------+
| jackxiang_mysql | CREATE DATABASE `jackxiang_mysql` /*!40100 DEFAULT CHARACTER SET latin1 */ |
+-----------------+----------------------------------------------------------------------------+
依葫芦画瓢:
建表基本结构都有:
如果是字符串的 NOT NULL DEFAULT '';
如果是数字型的 NOT NULL DEFAULT 0;
有兄弟说default '' 跨库也麻烦,不如default null:
用is null 和 ='' 都无法全部查询出来,有些人写程序,很容易把默认null的字段 插入成'',但是默认的很多数据又是null,这样一个字段会有 ''和null同时存在。
在上面表中加个索引:
Default值设定及Null设定
字符串:
alter table t_tst_root add busiLevel varchar(255) default '' not null after f_Id;
Int整形:
alter table t_tst_root add f_TstProjId int(10) default 0 not null after f_Id;
ALTER TABLE `Tbl_File` ADD PRIMARY KEY ( `FFileId` )
ALTER TABLE `Tbl_File` CHANGE `FFileId` `FFileId` INT( 11 ) NOT NULL AUTO_INCREMENT
________________________________________________________________________________________________
ALTER TABLE `temperatureone` CHANGE `nowTempValue` `nowTempValue` FLOAT(5,2) NOT NULL COMMENT '现在温度'
PHPMyadmin:
字段 nowTempValue
类型 FLOAT
长度/值*1 5,2
________________________________________________________________________________________________
alter table test add index id(id); //id不能有引号
加一个字段:
如果想要添加在某个字段的后面则是这样的:
想要修改一个字段及默认值:
把字段由text类似修改为var_char类型(我的bo-blog,里的字段作优化,bo-blog不更新了,自己作如下优化):
mysql里的长度简述:
CHAR(M) 0<M<=255(建议CHAR(1)外,超过此长度的用VARCHAR) M个字符(所占空间跟字符集等有关系)
VARCHAR(M) 0<M<65532/N M个字符(N大小由字符集,以及是否为中文还是字母数字等有关系)
TEXT 64K个字符 所占空间跟字符集等有关系
想删除某个字段:
添加索引、删除索引:
alter table `boblog_blogs` add index blogalias(blogalias);
alter table `boblog_blogs` drop index blogalias;
alter table boblog_blogs add FULLTEXT(content); //全文索引
扩大字段的长度:
修改表字段的默认值为空不为null:
修改表字段enum的新类型none:
# 修改表的注释
# 修改字段的注释 --注意:字段名和字段类型照写就行
添加一个自增长字段并且添在最前面用first,如下:
ALTER TABLE `object_video_upload` ADD `id` INT(32) NOT NULL AUTO_INCREMENT COMMENT 'auto_increase comment' first;
如果我在一个表上两个字段指定auto_increment呢?
mysql> create table tab_auto_incr(a int not null auto_increment,b int not null auto_increment,primary key (a),unique key (b));
ERROR 1075 (42000): Incorrect table definition; there can be only one auto column and it must be defined as a key//是:Innodb:
InnoDb引擎中对auto_increment的误解:http://jiuchen.sinaapp.com/136.html
结论3:在InnoDB中,auto_increment至少需要单独为索引字段(主键也是索引的一种)。
结论4:在InnoDB中,auto_increment可以和其他字段组成联合索引,但auto_increment必须位于最左侧(和MyISAM不同)。
所以,如果表已经建立好了,想加一个id并为auto_increment的字段得,先建立这个字段(暂还能有auto_increment的属性,否则报错如上),再添加索引,再修改这个字段为auto_increment,操作如下,并实践Ok,发现这个新加的id确实变为自动由1,2,3...这样的值了:
MYISM:
不行!说明一个表只能有一个auto_increment,且该列必须是primary key或者unique key。
更多:http://blog.csdn.net/feihong247/article/details/7748788
# 查看字段注释的方法 --show
要想一个字段是AUTO_INCREMENT ,它必须是一个索引,刚才一时心急,搞忘记了,备份下。
其根本是:create table Tbl_File_XXX(select * from Tbl_File_Bak_XXX); 这个语句在表Tbl_File_XXX中是不会有索引的。
以后怎么解决这个问题:
create table aaa like Tbl_User
insert into table select xxx
加在最前面FIRST :
ALTER TABLE `jiu` ADD `11` VARCHAR( 1 ) NOT NULL FIRST ;
测试Ok,索引带过来了:
create table aaa like Tbl_File_Bak;
导入: insert into aaa select * from Tbl_File_Bak;
示例:
=====================================================================
drop table Tbl_File;
create table Tbl_File like Tbl_File_Bak;
insert into Tbl_File select * from Tbl_File_Bak;
source /home/admin/745.txt
=====================================================================
直接select into 不会复制任何索引和主键,一般先create table like ,然后再select into
mysql> create table cc like test;
Query OK, 0 rows affected (0.04 sec)
mysql> desc cc;
+---------+-------------+------+-----+---------+-------+
| Field | Type | Null | Key | Default | Extra |
+---------+-------------+------+-----+---------+-------+
| id | int(5) | YES | | NULL | |
| name | varchar(20) | YES | | NULL | |
| address | varchar(50) | YES | | NULL | |
+---------+-------------+------+-----+---------+-------+
3 rows in set (0.02 sec)
mysql> insert into cc (select * from test);
mysql>Query OK, 5 rows affected (0.01 sec)
Over!
create table cs_bak1 as(select * from test) ; //备份表数据一模一样的,但索引没了,默认引擎和编码:服务器默认的存储引擎而不是源表的存储引擎
create table cs_bak like test; //相同的结构来创建一个新表,列名、数据类型、空指和索引也将复制,但是表的内容不会被复制。外键和专用的权限也没有被复制。
参考对比URL:http://phpdeveloper.cn/mysql-mysql%E5%A4%8D%E5%88%B6%E8%A1%A8%E7%9A%84%E4%B8%A4%E7%A7%8D%E6%96%B9%E6%B3%95
select into在Mysql中不适用,不支持:
现在有张表为student,我想将这个表里面的数据复制到一个为dust的新表中去,虽然可以用以下语句进行复制,总觉得不爽,希望各位帮助下我,谢谢。
answer 01:
create table dust select * from student;//用于复制前未创建新表dust的情况下
answer 02:
insert into dust select * from student;//已经创建了新表dust的情况下
现在请各位用select..into..语句实现以上东东,谢谢支持,再次感谢!
网友回复:该回复于2008-09-29 06:37:03被版主删除
网友回复:sqlserver可以那樣寫,但是mySql中貌似不行哦,另外樓主你說的那兩種寫法,應該是最常見的啊
网友回复:create table dust select * from student;
网友回复:楼上的写的是我发的里面的啊,我要的是 select into 语句的啊?
请各位大侠帮忙,谢谢!
网友回复:MySQL不支持Select Into语句直接备份表结构和数据,一些种方法可以代替, 也有其它方法可以处理,总结如下:
方法1:
MYSQL不支持:
Select * Into new_table_name from old_table_name;
替代方法:
Create table new_table_name (Select * from old_table_name);
来源:http://hi.baidu.com/%D3%D2%CA%D6%C4%AA%D4%FA%CC%D8%B5%C4/blog/item/ed236851533190818d5430b3.html
一、
`id` int(11) NOT NULL AUTO_INCREMENT COMMENT '合作伙伴ID',
PRIMARY KEY (`id`),
UNIQUE KEY `verifycode` (`verifycode`,`eid`)
二、
新加一个字段:
ALTER TABLE `object_partner` ADD `eid` VARCHAR( 16 ) NOT NULL AFTER `coop_type`
三、
把这新加的字段再加为联合索引:
ALTER TABLE `cntv_sns_language`.`object_partner` DROP PRIMARY KEY ,
ADD PRIMARY KEY ( `verifycode` , `eid` )
四、
添加一个id字段与表开头 FIRST:
ALTER TABLE `object_partner` ADD `id` INT( 11 ) NOT NULL FIRST
五、
给这个id字段加上primary索引(因前面加过primary索引,不能现再加出现下面提示):
ALTER TABLE `object_partner` ADD `id` INT( 11 ) NOT NULL FIRST
六、给前面第三步的primary联合索引去掉后加上 UNIQUE 索引:
ALTER TABLE `cntv_sns_language`.`object_partner` DROP PRIMARY KEY ,
ADD UNIQUE (
`verifycode` ,
`eid`
)
七、去掉后,给id字段加上前面的primary 索引:
ALTER TABLE `cntv_sns_language`.`object_partner` ADD INDEX ( `id` )
use 某个DB后:
status
可以看到DB的情况,尤其是乱码的问题等:
默认:
Server characterset: latin1
Db characterset: latin1
Client characterset: latin1
Conn. characterset: latin1
set names utf8 后:
Server characterset: latin
Db characterset: latin
Client characterset: utf8
Conn. characterset: utf8
导入依旧有问题,正确的应该默认就是DB:Utf8,导入才会可能正常:
Server characterset: latin1
Db characterset: utf8
Client characterset: latin1
Conn. characterset: latin1
mysql> ALTER TABLE `Tbl_User_NNN` ADD INDEX ( `FMemo_Extra` ( 16 ) ) ;
Query OK, 3 rows affected (0.12 sec)
Records: 3 Duplicates: 0 Warnings: 0
添加mysql数据库字段
使用ALTER TABLE [表名] ADD [字段]s语句……
索引
1.普通索引。
这是最基本的索引,它没有任何限制。它有以下几种创建方式:
(1)创建索引:CREATE INDEX indexName ON tableName(tableColumns(length));如果是CHAR,VARCHAR类型,length可以小于字段实际长度;如果是BLOB 和 TEXT 类型,必须指定length,下同。
(2)修改表结构:ALTER tableName ADD INDEX [indexName] ON (tableColumns(length))
(3)创建表的时候直接指定:CREATE TABLE tableName ( [...], INDEX [indexName] (tableColumns(length)) ;
2.唯一索引。
它与前面的"普通索引"类似,不同的就是:索引列的值必须唯一,但允许有空值。如果是组合索引,则列值的组合必须唯一。它有以下几种创建方式:
(1)创建索引:CREATE UNIQUE INDEX indexName ON tableName(tableColumns(length))
(2)修改表结构:ALTER tableName ADD UNIQUE [indexName] ON (tableColumns(length))
(3)创建表的时候直接指定:CREATE TABLE tableName ( [...], UNIQUE [indexName] (tableColumns(length));
实践成功如下:
3.主键索引
它是一种特殊的唯一索引,不允许有空值。一般是在建表的时候同时创建主键索引:CREATE TABLE testIndex(i_testID INT NOT NULL AUTO_INCREMENT,vc_Name VARCHAR(16) NOT NULL,PRIMARY KEY(i_testID)); 当然也可以用ALTER命令。
//主键549830479
alter table tabelname add new_field_id int(5) unsigned default 0 not null auto_increment ,add primary key (new_field_id);//增加一个新列549830479
alter table t2 add d timestamp;
alter table infos add ex tinyint not null default '0';//删除列549830479
alter table t2 drop column c;//重命名列549830479
alter table t1 change a b integer;
//改变列的类型549830479
alter table t1 change b b bigint not null;
alter table infos change list list tinyint not null default '0';
//重命名表549830479
alter table t1 rename t2;加索引549830479
mysql> alter table tablename change depno depno int(5) not null;
mysql> alter table tablename add index 索引名 (字段名1[,字段名2 …]);
mysql> alter table tablename add index emp_name (name);加主关键字的索引549830479
mysql> alter table tablename add primary key(id);加唯一限制条件的索引549830479
mysql> alter table tablename add unique emp_name2(cardnumber);删除某个索引549830479
mysql>alter table tablename drop index emp_name;修改表:549830479
增加字段:549830479
mysql> ALTER TABLE table_name ADD field_name field_type;修改原字段名称及类型:549830479
mysql> ALTER TABLE table_name CHANGE old_field_name new_field_name field_type;删除字段:549830479
mysql> ALTER TABLE table_name DROP field_name;
参考:http://flyer2010.iteye.com/blog/1064390
最常用同步关于库权限查询指令,基础预备:查一下数据库有用户及权限:
select distinct concat('user: ''',user,'''@''',host,''';') as query from mysql.user;
=================================================================
mysql> show create database jackxiang_mysql;
+-----------------+----------------------------------------------------------------------------+
| Database | Create Database |
+-----------------+----------------------------------------------------------------------------+
| jackxiang_mysql | CREATE DATABASE `jackxiang_mysql` /*!40100 DEFAULT CHARACTER SET latin1 */ |
+-----------------+----------------------------------------------------------------------------+
依葫芦画瓢:
建表基本结构都有:
如果是字符串的 NOT NULL DEFAULT '';
如果是数字型的 NOT NULL DEFAULT 0;
有兄弟说default '' 跨库也麻烦,不如default null:
用is null 和 ='' 都无法全部查询出来,有些人写程序,很容易把默认null的字段 插入成'',但是默认的很多数据又是null,这样一个字段会有 ''和null同时存在。
在上面表中加个索引:
Default值设定及Null设定
字符串:
alter table t_tst_root add busiLevel varchar(255) default '' not null after f_Id;
Int整形:
alter table t_tst_root add f_TstProjId int(10) default 0 not null after f_Id;
ALTER TABLE `Tbl_File` ADD PRIMARY KEY ( `FFileId` )
ALTER TABLE `Tbl_File` CHANGE `FFileId` `FFileId` INT( 11 ) NOT NULL AUTO_INCREMENT
________________________________________________________________________________________________
ALTER TABLE `temperatureone` CHANGE `nowTempValue` `nowTempValue` FLOAT(5,2) NOT NULL COMMENT '现在温度'
PHPMyadmin:
字段 nowTempValue
类型 FLOAT
长度/值*1 5,2
________________________________________________________________________________________________
alter table test add index id(id); //id不能有引号
加一个字段:
如果想要添加在某个字段的后面则是这样的:
想要修改一个字段及默认值:
把字段由text类似修改为var_char类型(我的bo-blog,里的字段作优化,bo-blog不更新了,自己作如下优化):
mysql里的长度简述:
CHAR(M) 0<M<=255(建议CHAR(1)外,超过此长度的用VARCHAR) M个字符(所占空间跟字符集等有关系)
VARCHAR(M) 0<M<65532/N M个字符(N大小由字符集,以及是否为中文还是字母数字等有关系)
TEXT 64K个字符 所占空间跟字符集等有关系
想删除某个字段:
添加索引、删除索引:
alter table `boblog_blogs` add index blogalias(blogalias);
alter table `boblog_blogs` drop index blogalias;
alter table boblog_blogs add FULLTEXT(content); //全文索引
扩大字段的长度:
修改表字段的默认值为空不为null:
修改表字段enum的新类型none:
# 修改表的注释
# 修改字段的注释 --注意:字段名和字段类型照写就行
添加一个自增长字段并且添在最前面用first,如下:
ALTER TABLE `object_video_upload` ADD `id` INT(32) NOT NULL AUTO_INCREMENT COMMENT 'auto_increase comment' first;
如果我在一个表上两个字段指定auto_increment呢?
mysql> create table tab_auto_incr(a int not null auto_increment,b int not null auto_increment,primary key (a),unique key (b));
ERROR 1075 (42000): Incorrect table definition; there can be only one auto column and it must be defined as a key//是:Innodb:
InnoDb引擎中对auto_increment的误解:http://jiuchen.sinaapp.com/136.html
结论3:在InnoDB中,auto_increment至少需要单独为索引字段(主键也是索引的一种)。
结论4:在InnoDB中,auto_increment可以和其他字段组成联合索引,但auto_increment必须位于最左侧(和MyISAM不同)。
所以,如果表已经建立好了,想加一个id并为auto_increment的字段得,先建立这个字段(暂还能有auto_increment的属性,否则报错如上),再添加索引,再修改这个字段为auto_increment,操作如下,并实践Ok,发现这个新加的id确实变为自动由1,2,3...这样的值了:
MYISM:
不行!说明一个表只能有一个auto_increment,且该列必须是primary key或者unique key。
更多:http://blog.csdn.net/feihong247/article/details/7748788
# 查看字段注释的方法 --show
要想一个字段是AUTO_INCREMENT ,它必须是一个索引,刚才一时心急,搞忘记了,备份下。
其根本是:create table Tbl_File_XXX(select * from Tbl_File_Bak_XXX); 这个语句在表Tbl_File_XXX中是不会有索引的。
以后怎么解决这个问题:
create table aaa like Tbl_User
insert into table select xxx
加在最前面FIRST :
ALTER TABLE `jiu` ADD `11` VARCHAR( 1 ) NOT NULL FIRST ;
测试Ok,索引带过来了:
create table aaa like Tbl_File_Bak;
导入: insert into aaa select * from Tbl_File_Bak;
示例:
=====================================================================
drop table Tbl_File;
create table Tbl_File like Tbl_File_Bak;
insert into Tbl_File select * from Tbl_File_Bak;
source /home/admin/745.txt
=====================================================================
直接select into 不会复制任何索引和主键,一般先create table like ,然后再select into
mysql> create table cc like test;
Query OK, 0 rows affected (0.04 sec)
mysql> desc cc;
+---------+-------------+------+-----+---------+-------+
| Field | Type | Null | Key | Default | Extra |
+---------+-------------+------+-----+---------+-------+
| id | int(5) | YES | | NULL | |
| name | varchar(20) | YES | | NULL | |
| address | varchar(50) | YES | | NULL | |
+---------+-------------+------+-----+---------+-------+
3 rows in set (0.02 sec)
mysql> insert into cc (select * from test);
mysql>Query OK, 5 rows affected (0.01 sec)
Over!
create table cs_bak1 as(select * from test) ; //备份表数据一模一样的,但索引没了,默认引擎和编码:服务器默认的存储引擎而不是源表的存储引擎
create table cs_bak like test; //相同的结构来创建一个新表,列名、数据类型、空指和索引也将复制,但是表的内容不会被复制。外键和专用的权限也没有被复制。
参考对比URL:http://phpdeveloper.cn/mysql-mysql%E5%A4%8D%E5%88%B6%E8%A1%A8%E7%9A%84%E4%B8%A4%E7%A7%8D%E6%96%B9%E6%B3%95
select into在Mysql中不适用,不支持:
现在有张表为student,我想将这个表里面的数据复制到一个为dust的新表中去,虽然可以用以下语句进行复制,总觉得不爽,希望各位帮助下我,谢谢。
answer 01:
create table dust select * from student;//用于复制前未创建新表dust的情况下
answer 02:
insert into dust select * from student;//已经创建了新表dust的情况下
现在请各位用select..into..语句实现以上东东,谢谢支持,再次感谢!
网友回复:该回复于2008-09-29 06:37:03被版主删除
网友回复:sqlserver可以那樣寫,但是mySql中貌似不行哦,另外樓主你說的那兩種寫法,應該是最常見的啊
网友回复:create table dust select * from student;
网友回复:楼上的写的是我发的里面的啊,我要的是 select into 语句的啊?
请各位大侠帮忙,谢谢!
网友回复:MySQL不支持Select Into语句直接备份表结构和数据,一些种方法可以代替, 也有其它方法可以处理,总结如下:
方法1:
MYSQL不支持:
Select * Into new_table_name from old_table_name;
替代方法:
Create table new_table_name (Select * from old_table_name);
来源:http://hi.baidu.com/%D3%D2%CA%D6%C4%AA%D4%FA%CC%D8%B5%C4/blog/item/ed236851533190818d5430b3.html
一、
`id` int(11) NOT NULL AUTO_INCREMENT COMMENT '合作伙伴ID',
PRIMARY KEY (`id`),
UNIQUE KEY `verifycode` (`verifycode`,`eid`)
二、
新加一个字段:
ALTER TABLE `object_partner` ADD `eid` VARCHAR( 16 ) NOT NULL AFTER `coop_type`
三、
把这新加的字段再加为联合索引:
ALTER TABLE `cntv_sns_language`.`object_partner` DROP PRIMARY KEY ,
ADD PRIMARY KEY ( `verifycode` , `eid` )
四、
添加一个id字段与表开头 FIRST:
ALTER TABLE `object_partner` ADD `id` INT( 11 ) NOT NULL FIRST
五、
给这个id字段加上primary索引(因前面加过primary索引,不能现再加出现下面提示):
ALTER TABLE `object_partner` ADD `id` INT( 11 ) NOT NULL FIRST
六、给前面第三步的primary联合索引去掉后加上 UNIQUE 索引:
ALTER TABLE `cntv_sns_language`.`object_partner` DROP PRIMARY KEY ,
ADD UNIQUE (
`verifycode` ,
`eid`
)
七、去掉后,给id字段加上前面的primary 索引:
ALTER TABLE `cntv_sns_language`.`object_partner` ADD INDEX ( `id` )
use 某个DB后:
status
可以看到DB的情况,尤其是乱码的问题等:
默认:
Server characterset: latin1
Db characterset: latin1
Client characterset: latin1
Conn. characterset: latin1
set names utf8 后:
Server characterset: latin
Db characterset: latin
Client characterset: utf8
Conn. characterset: utf8
导入依旧有问题,正确的应该默认就是DB:Utf8,导入才会可能正常:
Server characterset: latin1
Db characterset: utf8
Client characterset: latin1
Conn. characterset: latin1
mysql> ALTER TABLE `Tbl_User_NNN` ADD INDEX ( `FMemo_Extra` ( 16 ) ) ;
Query OK, 3 rows affected (0.12 sec)
Records: 3 Duplicates: 0 Warnings: 0
添加mysql数据库字段
使用ALTER TABLE [表名] ADD [字段]s语句……
索引
1.普通索引。
这是最基本的索引,它没有任何限制。它有以下几种创建方式:
(1)创建索引:CREATE INDEX indexName ON tableName(tableColumns(length));如果是CHAR,VARCHAR类型,length可以小于字段实际长度;如果是BLOB 和 TEXT 类型,必须指定length,下同。
(2)修改表结构:ALTER tableName ADD INDEX [indexName] ON (tableColumns(length))
(3)创建表的时候直接指定:CREATE TABLE tableName ( [...], INDEX [indexName] (tableColumns(length)) ;
2.唯一索引。
它与前面的"普通索引"类似,不同的就是:索引列的值必须唯一,但允许有空值。如果是组合索引,则列值的组合必须唯一。它有以下几种创建方式:
(1)创建索引:CREATE UNIQUE INDEX indexName ON tableName(tableColumns(length))
(2)修改表结构:ALTER tableName ADD UNIQUE [indexName] ON (tableColumns(length))
(3)创建表的时候直接指定:CREATE TABLE tableName ( [...], UNIQUE [indexName] (tableColumns(length));
实践成功如下:
3.主键索引
它是一种特殊的唯一索引,不允许有空值。一般是在建表的时候同时创建主键索引:CREATE TABLE testIndex(i_testID INT NOT NULL AUTO_INCREMENT,vc_Name VARCHAR(16) NOT NULL,PRIMARY KEY(i_testID)); 当然也可以用ALTER命令。
//主键549830479
alter table tabelname add new_field_id int(5) unsigned default 0 not null auto_increment ,add primary key (new_field_id);//增加一个新列549830479
alter table t2 add d timestamp;
alter table infos add ex tinyint not null default '0';//删除列549830479
alter table t2 drop column c;//重命名列549830479
alter table t1 change a b integer;
//改变列的类型549830479
alter table t1 change b b bigint not null;
alter table infos change list list tinyint not null default '0';
//重命名表549830479
alter table t1 rename t2;加索引549830479
mysql> alter table tablename change depno depno int(5) not null;
mysql> alter table tablename add index 索引名 (字段名1[,字段名2 …]);
mysql> alter table tablename add index emp_name (name);加主关键字的索引549830479
mysql> alter table tablename add primary key(id);加唯一限制条件的索引549830479
mysql> alter table tablename add unique emp_name2(cardnumber);删除某个索引549830479
mysql>alter table tablename drop index emp_name;修改表:549830479
增加字段:549830479
mysql> ALTER TABLE table_name ADD field_name field_type;修改原字段名称及类型:549830479
mysql> ALTER TABLE table_name CHANGE old_field_name new_field_name field_type;删除字段:549830479
mysql> ALTER TABLE table_name DROP field_name;
参考:http://flyer2010.iteye.com/blog/1064390
An old web technology is slowly being resurrected from the depths of history. Browser features that have gone untouched for years are once again being employed to bring better responsiveness to UIs. Servers are learning to cope with a new way of doing things. And I’m not talking about Ajax.
New services like Jot Live and Meebo are built with a style of data transmission that is neither traditional nor Ajax. Their brand of low-latency data transfer to the browser is unique, and it is becoming ever-more common. Lacking a better term, I’ve taken to calling this style of event-driven, server-push data streaming “Comet”. It doesn’t stand for anything, and I’m not sure that it should. There is much confusion about how these techniques work, and so using pre-existing definitions and names is as likely to get as much wrong as it would get right.
Defining Comet
For a new term to be useful, at a minimum we need some examples of the technology, a list of the problems being solved, and properties which distinguish it from other techniques. As with Ajax, these aren’t hard to find. A short list of example applications includes:
So what makes these apps special? What makes them different from other things that might at first glance appear similar? Fundamentally, they all use long-lived HTTP connections to reduce the latency with which messages are passed to the server. In essence, they do not poll the server occasionally. Instead the server has an open line of communication with which it can push data to the client.
From the perspective of network activity, we can modify JJG’s original Ajax diagram to illustrate how Comet differs:
As is illustrated above, Comet applications can deliver data to the client at any time, not only in response to user input. The data is delivered over a single, previously-opened connection. This approach reduces the latency for data delivery significantly.
The architecture relies on a view of data which is event driven on both sides of the HTTP connection. Engineers familiar with SOA or message oriented middleware will find this diagram to be amazingly familiar. The only substantive change is that the endpoint is the browser.
While Comet is similar to Ajax in that it’s asynchronous, applications that implement the Comet style can communicate state changes with almost negligible latency. This makes it suitable for many types of monitoring and multi-user collaboration applications which would otherwise be difficult or impossible to handle in a browser without plugins.
Why Is Comet Better For Users?
Regular Ajax improves the responsiveness of a UI for a single user, but at the cost of allowing the context to go “stale” for long-lived pages. Changes to data from others users is lost until a user refreshes the whole page. An application can alternately return to the “bad old days” and maintain some sort of state mechanism by which it tells client about changes since the last time they’ve communicated. The user has to either wait until they preform some action which would kick off a request to see the updated state from other users (which might impact the action they wanted to preform!) or request changes from the server at some interval (called “polling”). Since the web is inherently multi-user, it’s pretty obvious that regular Ajax imposes usability and transparency hurdles for users. Applications that employ the Comet technique can avoid this problem by pushing updates to all clients as they happen. UI state does not go out of sync and everyone using an application can easily understand what their changes will mean for other users. Ajax improves single-user responsiveness. Comet improves application responsiveness for collaborative, multi-user applications and does it without the performance headaches associated with intermittent polling.
But Does It Scale?
New server software is often required to make applications built using Comet scale, but the patterns for event-driven IO on the server side are becoming better distributed. Even Apache will provide a Comet-ready worker module in the upcoming 2.2 release. Until then, tools like Twisted, POE, Nevow, mod_pubsub, and other higher-level event-driven IO abstractions are making Comet available to developers on the bleeding edge. Modern OSes almost all now support some sort of kernel-level event-driven IO system as well. I’ve even heard that Java’s NIO packages will start to take advantage of them in a forthcoming release. These tools are quietly making the event-driven future a reality. This stuff will scale, and most of the tools are in place already.
I’ll be giving a more on this topic at ETech and describing the various techniques that Comet applications can employ to push data from the server to the client. As always, I’ll post the slides here as well.
The future of the read-write web is multi-user. There is life after Ajax.
Endnotes
First, a word on terminology and its importance. “Ajax” was coined to describe background request/response data transfer. Many of us had worked on solutions to do exactly this, but it wasn’t until a simple name and accompanying description were provided that it was possible for people not directly building applications to describe what it was they liked about it. Common terminology acts not only as a shortcut in discussions between technical folks, but also as a bridge for those who may not be able to give a technical rundown of exactly how it works.
As with Ajax, those of us who build technology are now faced with another communication challenge. We have a hard problem for which solutions are available (and have been for some time) but no way to communicate about them. Terminology is again the missing link. Today, keeping an HTTP connection open for doing low-latency data transfer to the browser has no digestible name. When I describe a cool new hack, there’s nothing to associate it with. When people say “how the hell did they do that?”, we don’t have a compact answer. Therefore, in the spirit of improved communication (and not technology invention), I’m proposing a new name for this stuff.
Next, for those who are network-level programmers or are familiar with sockets and/or basic TCP/IP programming, you will probably scoff at the concept of web applications finally getting this kind of datagram packet support. Fair enough. It is however interesting to note that while more responsive UIs have been available on a variety of platforms to date, the Web has “won” the broad majority of market share for most classes of applications in which the browser provides enough native (non-plugin) support to make the performance and/or UI feasible. Comet may be a new name for an old set of concepts wrapped in some pretty grotty hacks, but that in no way diminishes the market impact it will have (and is already having).
Lastly, as current Dojo users might expect, Dojo already supports Comet via
dojo.io.bind()
. More than a year ago we designed the API with Comet in mind. In the next couple of weeks I’ll be showing how bind
’s pluggable transport layer can be combined with Dojo’s event topic mechanism to provide message delivery on top of a message bus.
我们先来看一段代码。
<?php
for ($i=10; $i>0; $i--)
{
echo $i;
flush();
sleep(1);
}
?>
按照php手册里的说法
该函数将当前为止程序的所有输出发送到用户的浏览器。
上面的这段代码,应该隔一秒钟输出一次$i。但是实际中却不一定是这样。有可能是等了10秒钟后,所有的输出同时呈现出来。
好,我们来改一下这段代码,改成
<?php
ob_end_clean();//修改部分
for ($i=10; $i>0; $i--)
{
echo $i;
flush();
sleep(1);
}
?>
嘿,加了这一句ob_end_clean();,居然就OK了。实际上,我们把ob_end_clean()换成ob_end_flush()也一样OK。
我再来改一改。
<?php
for ($i=10; $i>0; $i--)
{
echo $i;
ob_flush();//修改部分
flush();
sleep(1);
}
?>
运行一下,是不是发现$i也隔一秒输出一次了?这是为什么呢?
别急,我们来看看php.ini。
打开php.ini,搜索output_buffering,我们会看到类似这样的设置 output_buffering = 4096。正如它的名字output_buffering一样,这个设置的作用就是把输出缓冲一下,缓冲大小为4096bytes.
在我们的第一段代码里,之所以没有按预期的输出,正是因为这个output_buffering把那些输出都缓冲了。没达到4096bytes或者脚本结束,输出是不会被发送出去的。
而第二段代码中的ob_end_clean()和ob_end_flush()的作用,就是终止缓冲。这样就不用等到有4096bytes的缓冲之后才被发送出去了。
第三段代码中,用了一句ob_flush(),它的作用就是把缓冲的数据发送出去,但是并不会终止缓冲,所以它必须在每次flush()前使用。
如果不想使用ob_end_clean(),ob_end_flush()和ob_flush(),我们就必须把php.ini里的 output_buffering设得足够小,例如设为0。需要注意的是,如果你打算在脚本中使用ini_set(” output_buffering”,”0″)来设置,那么请停下来吧,这种方法是不行的。因为在脚本一开始的时候,缓冲设置就已经被载入,然后缓冲就开始了。
可能你会问了,既然ob_flush()是把缓冲的数据发送出去,那么为什么还需要用flush()???直接用下面这段代码不行吗??
<?php
for ($i=10; $i>0; $i--)
{
echo $i;
ob_flush();
sleep(1);
}
?>
请注意ob_flush()和flush()的区别。前者是把数据从PHP的缓冲中释放出来,后者是把不在缓冲中的或者说是被释放出来的数据发送到浏览器。所以当缓冲存在的时候,我们必须ob_flush()和flush()同时使用。
那是不是flush()在这里就是不可缺少的呢?不是的,我们还有另外一种方法,使得当有数据输出的时候,马上被发送到浏览器。下面这两段代码就是不需要使用flush()了。(当你把output_buffering设为0的时候,连ob_flush()和ob_end_clean()都不需要了)
<?php
ob_implicit_flush(true);
for ($i=10; $i>0; $i--)
{
echo $i;
ob_flush();
sleep(1);
}
?>
<?php
ob_end_clean();
ob_implicit_flush(true);
for ($i=10; $i>0; $i--)
{
echo $i;
sleep(1);
}
?>
请注意看上面的ob_implicit_flush(true),这个函数强制每当有输出的时候,即刻把输出发送到浏览器。这样就不需要每次输出(echo)后,都用flush()来发送到浏览器了。
以上所诉可能在某些浏览器中不成立。因为浏览器也有自己的规则。我是用Firefox1.5,IE6,opera8.5来测试的。其中opera就不能正常输出,因为它有一个规则,就是不遇到一个HTML标签,就绝对不输出,除非到脚本结束。而FireFox和IE还算比较正常的。
最后附上一段非常有趣的代码,作者为PuTTYshell。在一个脚本周期里,每次输出,都会把前一次的输出覆盖掉。
以下代码只在firefox下可用,其他浏览器并不支持multipart/x-mixed-replace的Content-Type.
<?php
header('Content-type: multipart/x-mixed-replace;boundary=endofsection');
print "\n--endofsection\n";
$pmt = array("-", "\\", "|", "/" );
for( $i = 0; $i <10; $i ++ ){
sleep(1);
print "Content-type: text/plain\n\n";
print "Part $i\t".$pmt[$i % 4];
print "--endofsection\n";
ob_flush();
flush();
}
print "Content-type: text/plain\n\n";
print "The end\n";
print "--endofsection--\n";
?>
<?php
for ($i=10; $i>0; $i--)
{
echo $i;
flush();
sleep(1);
}
?>
按照php手册里的说法
该函数将当前为止程序的所有输出发送到用户的浏览器。
上面的这段代码,应该隔一秒钟输出一次$i。但是实际中却不一定是这样。有可能是等了10秒钟后,所有的输出同时呈现出来。
好,我们来改一下这段代码,改成
<?php
ob_end_clean();//修改部分
for ($i=10; $i>0; $i--)
{
echo $i;
flush();
sleep(1);
}
?>
嘿,加了这一句ob_end_clean();,居然就OK了。实际上,我们把ob_end_clean()换成ob_end_flush()也一样OK。
我再来改一改。
<?php
for ($i=10; $i>0; $i--)
{
echo $i;
ob_flush();//修改部分
flush();
sleep(1);
}
?>
运行一下,是不是发现$i也隔一秒输出一次了?这是为什么呢?
别急,我们来看看php.ini。
打开php.ini,搜索output_buffering,我们会看到类似这样的设置 output_buffering = 4096。正如它的名字output_buffering一样,这个设置的作用就是把输出缓冲一下,缓冲大小为4096bytes.
在我们的第一段代码里,之所以没有按预期的输出,正是因为这个output_buffering把那些输出都缓冲了。没达到4096bytes或者脚本结束,输出是不会被发送出去的。
而第二段代码中的ob_end_clean()和ob_end_flush()的作用,就是终止缓冲。这样就不用等到有4096bytes的缓冲之后才被发送出去了。
第三段代码中,用了一句ob_flush(),它的作用就是把缓冲的数据发送出去,但是并不会终止缓冲,所以它必须在每次flush()前使用。
如果不想使用ob_end_clean(),ob_end_flush()和ob_flush(),我们就必须把php.ini里的 output_buffering设得足够小,例如设为0。需要注意的是,如果你打算在脚本中使用ini_set(” output_buffering”,”0″)来设置,那么请停下来吧,这种方法是不行的。因为在脚本一开始的时候,缓冲设置就已经被载入,然后缓冲就开始了。
可能你会问了,既然ob_flush()是把缓冲的数据发送出去,那么为什么还需要用flush()???直接用下面这段代码不行吗??
<?php
for ($i=10; $i>0; $i--)
{
echo $i;
ob_flush();
sleep(1);
}
?>
请注意ob_flush()和flush()的区别。前者是把数据从PHP的缓冲中释放出来,后者是把不在缓冲中的或者说是被释放出来的数据发送到浏览器。所以当缓冲存在的时候,我们必须ob_flush()和flush()同时使用。
那是不是flush()在这里就是不可缺少的呢?不是的,我们还有另外一种方法,使得当有数据输出的时候,马上被发送到浏览器。下面这两段代码就是不需要使用flush()了。(当你把output_buffering设为0的时候,连ob_flush()和ob_end_clean()都不需要了)
<?php
ob_implicit_flush(true);
for ($i=10; $i>0; $i--)
{
echo $i;
ob_flush();
sleep(1);
}
?>
<?php
ob_end_clean();
ob_implicit_flush(true);
for ($i=10; $i>0; $i--)
{
echo $i;
sleep(1);
}
?>
请注意看上面的ob_implicit_flush(true),这个函数强制每当有输出的时候,即刻把输出发送到浏览器。这样就不需要每次输出(echo)后,都用flush()来发送到浏览器了。
以上所诉可能在某些浏览器中不成立。因为浏览器也有自己的规则。我是用Firefox1.5,IE6,opera8.5来测试的。其中opera就不能正常输出,因为它有一个规则,就是不遇到一个HTML标签,就绝对不输出,除非到脚本结束。而FireFox和IE还算比较正常的。
最后附上一段非常有趣的代码,作者为PuTTYshell。在一个脚本周期里,每次输出,都会把前一次的输出覆盖掉。
以下代码只在firefox下可用,其他浏览器并不支持multipart/x-mixed-replace的Content-Type.
<?php
header('Content-type: multipart/x-mixed-replace;boundary=endofsection');
print "\n--endofsection\n";
$pmt = array("-", "\\", "|", "/" );
for( $i = 0; $i <10; $i ++ ){
sleep(1);
print "Content-type: text/plain\n\n";
print "Part $i\t".$pmt[$i % 4];
print "--endofsection\n";
ob_flush();
flush();
}
print "Content-type: text/plain\n\n";
print "The end\n";
print "--endofsection--\n";
?>