sort是在Linux里非常常用的一个命令,管排序的,集中精力,五分钟搞定sort,现在开始!
1 sort的工作原理
sort将文件的每一行作为一个单位,相互比较,比较原则是从首字符向后,依次按ASCII码值进行比较,最后将他们按升序输出。
[rocrocket@rocrocket programming]$ cat seq.txt
banana
apple
pear
orange
[rocrocket@rocrocket programming]$ sort seq.txt
apple
banana
orange
pear
2 sort的-u选项
它的作用很简单,就是在输出行中去除重复行。
[rocrocket@rocrocket programming]$ cat seq.txt
banana
apple
pear
orange
pear
[rocrocket@rocrocket programming]$ sort seq.txt
apple
banana
orange
pear
pear
[rocrocket@rocrocket programming]$ sort -u seq.txt
apple
banana
orange
pear
pear由于重复被-u选项无情的删除了。
3 sort的-r选项
sort默认的排序方式是升序,如果想改成降序,就加个-r就搞定了。
[rocrocket@rocrocket programming]$ cat number.txt
1
3
5
2
4
[rocrocket@rocrocket programming]$ sort number.txt
1
2
3
4
5
[rocrocket@rocrocket programming]$ sort -r number.txt
5
4
3
2
1
4 sort的-o选项
由于sort默认是把结果输出到标准输出,所以需要用重定向才能将结果写入文件,形如sort filename > newfile。
但是,如果你想把排序结果输出到原文件中,用重定向可就不行了。
[rocrocket@rocrocket programming]$ sort -r number.txt > number.txt
[rocrocket@rocrocket programming]$ cat number.txt
[rocrocket@rocrocket programming]$
看,竟然将number清空了。
就在这个时候,-o选项出现了,它成功的解决了这个问题,让你放心的将结果写入原文件。这或许也是-o比重定向的唯一优势所在。
[rocrocket@rocrocket programming]$ cat number.txt
1
3
5
2
4
[rocrocket@rocrocket programming]$ sort -r number.txt -o number.txt
[rocrocket@rocrocket programming]$ cat number.txt
5
4
3
2
1
5 sort的-n选项
你有没有遇到过10比2小的情况。我反正遇到过。出现这种情况是由于排序程序将这些数字按字符来排序了,排序程序会先比较1和2,显然1小,所以就将10放在2前面喽。这也是sort的一贯作风。
我们如果想改变这种现状,就要使用-n选项,来告诉sort,“要以数值来排序”!
[rocrocket@rocrocket programming]$ cat number.txt
1
10
19
11
2
5
[rocrocket@rocrocket programming]$ sort number.txt
1
10
11
19
2
5
[rocrocket@rocrocket programming]$ sort -n number.txt
1
2
5
10
11
19
6 sort的-t选项和-k选项
如果有一个文件的内容是这样:
[rocrocket@rocrocket programming]$ cat facebook.txt
banana:30:5.5
apple:10:2.5
pear:90:2.3
orange:20:3.4
这个文件有三列,列与列之间用冒号隔开了,第一列表示水果类型,第二列表示水果数量,第三列表示水果价格。
那么我想以水果数量来排序,也就是以第二列来排序,如何利用sort实现?
幸好,sort提供了-t选项,后面可以设定间隔符。(是不是想起了cut和paste的-d选项,共鸣~~)
指定了间隔符之后,就可以用-k来指定列数了。
[rocrocket@rocrocket programming]$ sort -n -k 2 -t : facebook.txt
apple:10:2.5
orange:20:3.4
banana:30:5.5
pear:90:2.3
我们使用冒号作为间隔符,并针对第二列来进行数值升序排序,结果很令人满意。
7 其他的sort常用选项
-f会将小写字母都转换为大写字母来进行比较,亦即忽略大小写
-c会检查文件是否已排好序,如果乱序,则输出第一个乱序的行的相关信息,最后返回1
-C会检查文件是否已排好序,如果乱序,不输出内容,仅返回1
-M会以月份来排序,比如JAN小于FEB等等
-b会忽略每一行前面的所有空白部分,从第一个可见字符开始比较。
有时候学习脚本,你会发现sort命令后面跟了一堆类似-k1,2,或者-k1.2 -k3.4的东东,有些匪夷所思。今天,我们就来搞定它—-k选项!
1 准备素材
$ cat facebook.txt
google 110 5000
baidu 100 5000
guge 50 3000
sohu 100 4500
第一个域是公司名称,第二个域是公司人数,第三个域是员工平均工资。(除了公司名称,其他的别信,都瞎写的^_^)
2 我想让这个文件按公司的字母顺序排序,也就是按第一个域进行排序:(这个facebook.txt文件有三个域)
$ sort -t ‘ ‘ -k 1 facebook.txt
baidu 100 5000
google 110 5000
guge 50 3000
sohu 100 4500
看到了吧,就直接用-k 1设定就可以了。(其实此处并不严格,稍后你就会知道)
3 我想让facebook.txt按照公司人数排序
$ sort -n -t ‘ ‘ -k 2 facebook.txt
guge 50 3000
baidu 100 5000
sohu 100 4500
google 110 5000
不用解释,我相信你能懂。
但是,此处出现了问题,那就是baidu和sohu的公司人数相同,都是100人,这个时候怎么办呢?按照默认规矩,是从第一个域开始进行升序排序,因此baidu排在了sohu前面。
4 我想让facebook.txt按照公司人数排序,人数相同的按照员工平均工资升序排序:
$ sort -n -t ‘ ‘ -k 2 -k 3 facebook.txt
guge 50 3000
sohu 100 4500
baidu 100 5000
google 110 5000
看,我们加了一个-k2 -k3就解决了问题。对滴,sort支持这种设定,就是说设定域排序的优先级,先以第2个域进行排序,如果相同,再以第3个域进行排序。(如果你愿意,可以一直这么写下去,设定很多个排序优先级)
5 我想让facebook.txt按照员工工资降序排序,如果员工人数相同的,则按照公司人数升序排序:(这个有点难度喽)
$ sort -n -t ‘ ‘ -k 3r -k 2 facebook.txt
baidu 100 5000
google 110 5000
sohu 100 4500
guge 50 3000
此处有使用了一些小技巧,你仔细看看,在-k 3后面偷偷加上了一个小写字母r。你想想,再结合我们上一篇文章,能得到答案么?揭晓:r和-r选项的作用是一样的,就是表示逆序。因为sort默认是按照升序排序的,所以此处需要加上r表示第三个域(员工平均工资)是按照降序排序。此处你还可以加上n,就表示对这个域进行排序时,要按照数值大小进行排序,举个例子吧:
$ sort -t ‘ ‘ -k 3nr -k 2n facebook.txt
baidu 100 5000
google 110 5000
sohu 100 4500
guge 50 3000
看,我们去掉了最前面的-n选项,而是将它加入到了每一个-k选项中了。
6 -k选项的具体语法格式
要继续往下深入的话,就不得不来点理论知识。你需要了解-k选项的语法格式,如下:
[ FStart [ .CStart ] ] [ Modifier ] [ , [ FEnd [ .CEnd ] ][ Modifier ] ]
这个语法格式可以被其中的逗号(“,”)分为两大部分,Start部分和End部分。
先给你灌输一个思想,那就是“如果不设定End部分,那么就认为End被设定为行尾”。这个概念很重要的,但往往你不会重视它。
Start部分也由三部分组成,其中的Modifier部分就是我们之前说过的类似n和r的选项部分。我们重点说说Start部分的FStart和C.Start。
C.Start也是可以省略的,省略的话就表示从本域的开头部分开始。之前例子中的-k 2和-k 3就是省略了C.Start的例子喽。
FStart.CStart,其中FStart就是表示使用的域,而CStart则表示在FStart域中从第几个字符开始算“排序首字符”。
同理,在End部分中,你可以设定FEnd.CEnd,如果你省略.CEnd,则表示结尾到“域尾”,即本域的最后一个字符。或者,如果你将CEnd设定为0(零),也是表示结尾到“域尾”。
7 突发奇想,从公司英文名称的第二个字母开始进行排序:
$ sort -t ‘ ‘ -k 1.2 facebook.txt
baidu 100 5000
sohu 100 4500
google 110 5000
guge 50 3000
看,我们使用了-k 1.2,这就表示对第一个域的第二个字符开始到本域的最后一个字符为止的字符串进行排序。你会发现baidu因为第二个字母是a而名列榜首。sohu和 google第二个字符都是o,但sohu的h在google的o前面,所以两者分别排在第二和第三。guge只能屈居第四了。
8 又突发奇想,,只针对公司英文名称的第二个字母进行排序,如果相同的按照员工工资进行降序排序:
$ sort -t ‘ ‘ -k 1.2,1.2 -k 3,3nr facebook.txt
baidu 100 5000
google 110 5000
sohu 100 4500
guge 50 3000
由于只对第二个字母进行排序,所以我们使用了-k 1.2,1.2的表示方式,表示我们“只”对第二个字母进行排序。(如果你问“我使用-k 1.2怎么不行?”,当然不行,因为你省略了End部分,这就意味着你将对从第二个字母起到本域
建模方法:
1:数据汇总:
eg:pagerank
通过数据来反映网页的重要性,即随机一个用户处于该页的概率
2:聚类
3:特征抽取
1:频繁项集 frequent itemset:
eg:大多数用户买A的同时也购买了B,则当有用户买A的时候给他推荐B
2:相似项 similar item:协同过滤
寻找相似用户或者相似商品,用于推荐
邦弗郎尼原理:胰造正相关,即当在一定的数据中寻找特定特征,即使数据完全随机,随着数据的增长该特征也会出现并增多
TF.IDF:TF*IDF
TF:词项频率
TF(ij) = f(ij)/maxk f(kj)
即在文档j中,词项i的频率为i的频次除以频次最高的词项k的频次(归一化,不考虑停用词)
IDF:逆文档频率
假设文档数为N,词项i在n(i)篇文档中出现过则词项i的IDF为:
IDF(i)=log(2) N/n(i)
未完待需。。。
数组做参数或返回值时,传入传出的值将会是数组首地址。其方式与指针类似。
示例源码:
char* FuncReturnArray(char szAry[]) { return szAry; } void main() { char szArray[] = "hello"; FuncReturnArray(szArray); }反汇编代码:
char szArray[] = "hello"; 004130F8 mov eax,dword ptr [string "hello" (4157A0h)] 004130FD mov dword ptr [ebp-10h],eax //ebp-10h处存储数组”hello”的首地址 00413100 mov cx,word ptr ds:[4157A4h] 00413107 mov word ptr [ebp-0Ch],cx FuncReturnArray(szArray); 0041310B lea eax,[ebp-10h] //取eax = ebp-10h,此时eax中是一个指针, //指向”hello”的首地址 0041310E push eax //将ebp-10h压栈,其实是以指针形式 0041310F call FuncReturnArray (4111E0h) 00413114 add esp,4 char* FuncReturnArray(char szAry[]) { 004113C0 push ebp 004113C1 mov ebp,esp 004113C3 sub esp,0C0h 004113C9 push ebx 004113CA push esi 004113CB push edi 004113CC lea edi,[ebp-0C0h] 004113D2 mov ecx,30h 004113D7 mov eax,0CCCCCCCCh 004113DC rep stos dword ptr es:[edi] //以上为函数的保存环境和初始化过程 return szAry; 004113DE mov eax,dword ptr [szAry] //把szAry中的值,也就是在main中push的那个 } //指针值,赋值到eax并返回,依然与指针的返回一致 004113E1 pop edi //以上为函数的恢复环境过程 004113E2 pop esi 004113E3 pop ebx 004113E4 mov esp,ebp 004113E6 pop ebp 004113E7 ret2.默认拷贝构造的结构体或类对象做参数或返回值
现在在笔试面试中常问的一个问题是:结构体与类有什么区别?
首先我先对这个题目想表达的意思表示怀疑:这里的结构体是指C中的结构体还是C++中的结构体,因为C与C++所支持的结构体功能相差是很大的。
我可以很负责任的告诉大家,c++中的结构体与类的差别仅仅在于成员的默认访问权限和默认继承方式,struct默认public,class默认private。
因此我们可以说,C++中的结构体与类基本是一致的。所以此处我们就不针对结构体和类一一进行示例分析了。
当类对象或结构体对象作为函数参数或者返回值时,需要调用一个特殊的构造函数,拷贝构造函数。如果用户没有定义拷贝构造函数,编译器会对原对象与拷贝对象中的各数据成员直接进行数据复制,称为默认拷贝构造函数(其实这可以看作是一种内联的函数,因为这种数据复制的过程中没有函数的调用)。
1.size小于等于四字节
做参数时: 把结构体内容压栈,默认拷贝构造。
做返回值时:返回值存入到eax中,接收的时候通过拷贝构造或者赋值操作完成。
2.size大于四字节小于等于8字节
做参数时: 把结构体内容压栈,默认拷贝构造。
做返回值时:把结构体内容压栈,返回值存入到eax和edx中,接收的时候通过拷贝构造或者赋值操作完成。
3.size大于8字节
做参数时: 把结构体内容压栈,默认拷贝构造。
做返回值时:把结构体内容压栈,返回值需要用到返回对象(接收对象或临时对象),其指针存入eax中,接收的时候通过拷贝构造或者赋值操作完成。
示例源码:
struct tagNotGreaterThan4 { char ch; bool b; }; tagNotGreaterThan4 FuncTestStruct1(tagNotGreaterThan4 tag) { return tag; } struct tagGreaterThan4NotGreaterThan8 { int n; char ch; }; tagGreaterThan4NotGreaterThan8 FuncTestStruct2(tagGreaterThan4NotGreaterThan8 tag) { return tag; } struct tagGreaterThan8 { int n; char ch[5]; }; tagGreaterThan8 FuncTestStruct3(tagGreaterThan8 tag) { return tag; } void main() { tagNotGreaterThan4 tagArg1 = {'a',false}; tagNotGreaterThan4 tagRet1 = {0}; tagRet1 = FuncTestStruct1(tagArg1); tagGreaterThan4NotGreaterThan8 tagArg2 = {100,'X'}; tagGreaterThan4NotGreaterThan8 tagRet2 ={0}; tagRet2 = FuncTestStruct2(tagArg2); tagGreaterThan8 tagArg3 = {100,"test"}; tagGreaterThan8 tagRet3 ={0}; tagRet3 = FuncTestStruct3(tagArg3); }反汇编代码分析:
void main() {//之上省略main函数保存环境,及初始化 tagNotGreaterThan4 tagArg1 = {'a',true}; 00411508 mov byte ptr [ebp-0Ch],61h 0041150C mov byte ptr [ebp-0Bh],0 tagNotGreaterThan4 tagRet1 = {0}; 00411510 mov byte ptr [ebp-18h],0 00411514 xor eax,eax 00411516 mov byte ptr [ebp-17h],al tagRet1 = FuncTestStruct1(tagArg1); 00411519 movzx eax,word ptr [ebp-0Ch] //取结构体中的char字符 0041151D push eax //压栈,默认拷贝构造到形参 0041151E call FuncTestStruct1 (411181h) 00411523 add esp,4 //此时返回的结构体内容在eax中 00411526 mov word ptr [ebp-162h],ax //从此往下三句是从eax赋值给接收者 0041152D mov cx,word ptr [ebp-162h] //相当于是默认的operator= 00411534 mov word ptr [ebp-18h],cx // tagGreaterThan4NotGreaterThan8 tagArg2 = {100,'X'}; 00411538 mov dword ptr [ebp-28h],64h 0041153F mov byte ptr [ebp-24h],58h tagGreaterThan4NotGreaterThan8 tagRet2 ={0}; 00411543 mov dword ptr [ebp-38h],0 0041154A xor eax,eax 0041154C mov dword ptr [ebp-34h],eax tagRet2 = FuncTestStruct2(tagArg2); 0041154F mov eax,dword ptr [ebp-24h] //通过两次压栈把大于4字节 00411552 push eax //不大于8字节的结构体 00411553 mov ecx,dword ptr [ebp-28h] //拷贝到栈上 00411556 push ecx //作为形参,默认拷贝构造 00411557 call FuncTestStruct2 (411019h) 0041155C add esp,8 //此时返回的结构体内容在eax/edx中 0041155F mov dword ptr [ebp-158h],eax //从这往下六句是从eax赋值给接收者 00411565 mov dword ptr [ebp-154h],edx //相当于调用了默认的operator=函数 0041156B mov edx,dword ptr [ebp-158h] //把eax/edx的值,几经转储 00411571 mov dword ptr [ebp-38h],edx //最后赋值到目标对象 00411574 mov eax,dword ptr [ebp-154h] // 0041157A mov dword ptr [ebp-34h],eax // tagGreaterThan8 tagArg3 = {100,"test"}; 0041157D mov dword ptr [ebp-4Ch],64h 00411584 mov eax,dword ptr [string "test" (41573Ch)] 00411589 mov dword ptr [ebp-48h],eax 0041158C mov cl,byte ptr ds:[415740h] 00411592 mov byte ptr [ebp-44h],cl tagGreaterThan8 tagRet3 ={0}; 00411595 mov dword ptr [ebp-60h],0 0041159C xor eax,eax 0041159E mov dword ptr [ebp-5Ch],eax 004115A1 mov dword ptr [ebp-58h],eax tagRet3 = FuncTestStruct3(tagArg3); 004115A4 sub esp,0Ch //调整栈顶,为形参留出空间 004115A7 mov eax,esp //esp传递给eax做this指针 004115A9 mov ecx,dword ptr [ebp-4Ch] //此处6次mov 004115AC mov dword ptr [eax],ecx //构成3次转储 004115AE mov edx,dword ptr [ebp-48h] //相当于3次压栈, 004115B1 mov dword ptr [eax+4],edx //把实参ebp-4Ch的内容 004115B4 mov ecx,dword ptr [ebp-44h] //依次拷贝到栈上 004115B7 mov dword ptr [eax+8],ecx //形成默认拷贝构造 004115BA lea edx,[ebp-134h] //编译器隐含传入一个参数 004115C0 push edx //是被用作返回临时对象的指针 004115C1 call FuncTestStruct3 (4110AAh) 004115C6 add esp,10h 004115C9 mov ecx,dword ptr [eax] //以下一大堆mov来mov去的代码 004115CB mov dword ptr [ebp-148h],ecx //其实是编译器默认提供的operator=的操作 004115D1 mov edx,dword ptr [eax+4] //因为operator=的参数和返回值均为对象 004115D4 mov dword ptr [ebp-144h],edx //所以会有 004115DA mov eax,dword ptr [eax+8] //1.先把对象从ebp-134h拷贝到ebp-148h 004115DD mov dword ptr [ebp-140h],eax //相当于形参的拷贝构造 004115E3 mov ecx,dword ptr [ebp-148h] //2.再把对象从ebp-148h拷贝到ebp-60h 004115E9 mov dword ptr [ebp-60h],ecx //相当于把ebp-60h做返回对象 004115EC mov edx,dword ptr [ebp-144h] //见注1 004115F2 mov dword ptr [ebp-5Ch],edx //由于此处的代码看起来格外别扭 004115F5 mov eax,dword ptr [ebp-140h] //下述注解的部分我们来把这个影响我们求证 004115FB mov dword ptr [ebp-58h],eax //的部分想办法绕开 }//之下为main函数恢复环境 ,略 tagNotGreaterThan4 FuncTestStruct1(tagNotGreaterThan4 tag) {//省略保存环境和初始化 return tag; 004113DE mov ax,word ptr [tag] //因为结构体大小不大于4字节,所以直接eax返回 }//省略恢复环境 tagGreaterThan4NotGreaterThan8 FuncTestStruct2(tagGreaterThan4N