Chinaunix首页 | 论坛 | 博客
  • 博客访问: 1571992
  • 博文数量: 289
  • 博客积分: 11086
  • 博客等级: 上将
  • 技术积分: 3291
  • 用 户 组: 普通用户
  • 注册时间: 2006-06-22 17:06
个人简介

徐小玉的博客。

文章分类

全部博文(289)

文章存档

2023年(6)

2022年(1)

2021年(2)

2020年(9)

2019年(9)

2018年(6)

2017年(10)

2016年(10)

2014年(3)

2013年(4)

2011年(12)

2010年(16)

2009年(14)

2008年(119)

2007年(48)

2006年(20)

我的朋友

分类: LINUX

2017-09-06 17:57:33

aaa          35-38
fffff        68-71,2-75,76-80
fffff        101-105       
fffff        106-110       
kkkk         35-37       
kkkk         38-40       
77777        1-8       
77777        9-16       
77777        39-40       
77777        41-42       
77777        43-44       
77777        45-46       
2222         25-26,27-28

上述文本假设其中n条,把有逗号的行第二列中间部分去掉,并且添加符号n,如下所示,perl,awk python分别如何实现
aaa                35-38
fffff        68n80
fffff        101-105       
fffff        106-110       
kkkk        35-37       
kkkk        38-40       
77777        1-8       
77777        9-16       
77777        39-40       
77777        41-42       
77777        43-44       
77777        45-46       
2222        25n28

--------------------------

awk '/,/{sub("-.*-","n",$0)}1' file4

awk '/,/{sub("-.*-","n",$0)}{print $0}' file4


1. sub函数

# echo "a b c 2011-11-22 a:d" | awk 'sub(/-/,"",$4)'
a b c 201111-22 a:d


2. gsub函数

# echo "a b c 2011-11-22 a:d" | awk 'gsub(/-/,"",$4)'
a b c 20111122 a:d

sub函数只实现第一个位置的替换,gsub函数实现全局的替换。

3: 1 作何解释?
1 =>  1{print $0}  => if(1){print $0}  => {print $0}

-------------------
awk  '{sub(/-.*..*-/,"n",$2)}1' file4

---------
sed 's/-.*-/n/' file4

------perl


#!/usr/bin/perl
use strict;
use warnings;

while(){
        y/.//d and s/-.*-/n/;
        print;
}

__DATA__
aaa                35-38
fffff        68-71.72-75.76-80
fffff        101-105
fffff        106-110
kkkk        35-37
kkkk        38-40
77777        1-8
77777        9-16
77777        39-40
77777        41-42
77777        43-44
77777        45-46
2222        25-26.27-28

找了不少关于tr的用法,发现最后面附的那个帖子算是比较详细的。

个人测试的结果总结如下:

(1)/c表示把匹配不上的字符进行替换.

$temp="AAAABCDEF";

$count=$temp=~tr/A/H/c;

print "$temp\t$count\n";

结果:AAAAHHHHH 5

(2)/d:表示把匹配上的字符全部替换

$temp="AAAABCDEF";

$count=$temp=~tr/A/H/d;

print "$temp\t$count\n";

结果:HHHHBCDEF 4

(3)/s:表示如果要替换的字符中出现连续多个一样的字符,则去冗余:

$temp="AAAABCDEF";

$count=$temp=~tr/A/H/ds;

print "$temp\t$count\n";

结果:HBCDEF 4

$temp="AAAABCDEF";

$count=$temp=~tr/A/H/cs;

print "$temp\t$count\n";

结果:AAAAH 5

=============================================================

另外,我再总结一下tr的用处:

$count=$temp=~tr/A//; #表示计算$temp中出现A的次数,$temp并不改变值

$count=$temp=~tr/A/A/; #表示计算$temp中出现A的次数,$temp并不改变值 和上面的意思一样的


$temp="AAAABCDEF";

$count=$temp=~tr/[A-Z]/[a-z]/; #表示进行大小写转换

print "$temp\t$count\n";

结果:aaaabcdef 9 
如果写成$count=$temp=~tr/[A-Z]/[A-Z]/; 则$temp不会改变,仅统计$temp里面大写字母的个数
结果:AAAABCDEF 9

===================================================================

下面是转载的内容

===================================================================

关键词1perl 2tr///

此文完整连结
文章欢迎转载,请尊重版权注明连结来源。



Perl 中s/// 和 tr/// 的差别

说真的,要学好 perl 还真的不简单,因为 perl 的程序代码比C来得精简一半1,靠得就是在撰写时的大脑运作。程设师得花更多时间写出精简的 code,同时也要将「语意上的错误」减少到最低,这就是要靠经验的累积。废话不多说,先说s/// 置换的功能,s 是 substitute的意思:

s/原来字符串/目的字符串/修饰子

s/// 会默认搜索 $ _,找出「原来字符串」,换成「目的字符串」。该运算符返回匹配的数量或进行替换的数量,如果没有进行任何匹配,则返回0。

ex:  把 Who 换成 What

$str = "Who are you?";
$str =~ s/Who/What/;

要取得比对成功的个数,可以写
print $str =~ s/Who/What/;   # 结果 1
但是 print $str;

What are you?
很有趣的差别,就算是老手也容易忘记,其实写成这样就容易种了 $cnt= ($str =~ s/Who/What/);   #$cnt=1

s/// 只会预设置换第一个找到的目标字符串,假若想全部置换,就要加 g 修饰子:

$str = "What a wonderful wonderful world.";
$str =~ s/w/W/g;     # str = "What a Wonderful Wonderful World."

假设比对原来的字符串不分大小写的话,就可加 i 修饰子:

$str = "What a wonderful wonderful world.";
$str =~ s/w/www/ig;     # str = "www a wwwonderful wwwonderful wwworld."

假若我们要将比对到的字符串,前后加上单引号,这在其他语言是困难的事,对perl来说像吃x一样简单,这里一个特殊变量 $& 就是比对到的字符串:

$str = "What a wonderful wonderful world.";
$str =~ s/w/'$&'/g;     # $str = "What a 'w'onderful 'w'onderful 'w'orld."

s/// 里面的「目的字符串」是可以放入函式的,例如以下几个常用的函数:

uc($str) 把$str 全转成大写

lc($str) 把$str 全转成小写

ucfirst($str) 把$str 第一码转成大写

假设我想把找到的结果全转成大写,一般的置换就伤透脑筋了,可是perl提供了不错的解法,但是要使用函数,就得加上 e 修饰子:

$str = "What a wonderful wonderful world.";
$str =~ s/w\w+/uc($&)/ge;    # $str = "What a WONDERFUL WONDERFUL WORLD"

如果没加 e 修饰子,则函式会被当成字符串丢出来:
What a uc(wonderful) uc(wonderful) uc(world)

再来比较麻烦的是字符串中的换行 \n,字符串中的换行字符 '\n' 被当成是一个字符来处理,所以假设一个具有换行字符的(多行的的字符串,希望比对时忽略那个换行字符,就要加上  修饰子:

$str = "What a wonder\nful wonderful world.";
$str =~ s/wonder.?ful/www/g;
print $str;

What a wonder
ful www world

接下来加上 s  修饰子后,\n就等于是"一个字符",也等于'\n';否则未加s的情况则不属于一个字符,也就是和 '.' 比对不会成功:
$str =~ s/wonder.?ful/www/sg;

What a www www world

如果我们坚持一定要和换行比对成功,则:注意没加 s
$str =~ s/wonder\nful/www/g;

What a www wonderful world

 

另外比较少见的情形下会用到的 m  修饰子:一般比对时假设要找出字符串结尾的字符串,常会用变换字符 $,在带有换行的字符串中,变换字符 $只会比对最后的换行或是字符串结尾。如果希望 $能取得符合带有换行的字符串中,每个换行都视为结尾的话,就要加 m。这样形容比较抽象,看个范例:

$str = "line123\nline456\nline789";
$str =~ s/\d+$//g;    <== 注意没有加 m
print $str;

line123
line456
line     <==只比对最后一个

$str =~ s/\d+$//mg;   <== 注意现在加了 m
print $str;

line    <==每个换行符号都视为结尾
line
line

写了这么多 s///,现在来说 tr///,tr 的语法和 s 好像一样,其实还差异满大的,tr 主要作为项目列表的置换:

tr/原来比对列表/目的比对列表/选项

perl 的 tr把置换的功能再扩张,虽然 s很强,可是也有做不到的事,例如今天要把大写换成小写,「同时」小写也换成大写,s就一筹莫展了,但 tr 可以搞定:

$str = "Aine123\nBine789";
$str =~ tr/a-zA-Z/A-Za-z/;
print $str;

aINE123
bINE789

同时tr 也可以用来计算字数,只需要把自己换成自己就好,例如以下范例计算$doc出现的数字个数:

$doc="<78>Nov  3 11:20:01 163.17.44.1 crond[30367]: (root) CMD (LANG=C LC_ALL=C /usr/bin/mrtg /etc/mrtg/mrtg.cfg --lock-file /var/lock/mrtg/mrtg_l --confcache-file /var/lib/mrtg/mrtg.ok)";

print $doc=~ tr/0-9/0-9/;

结果:22

这里要注意,上式的 $doc 本身并没有改变,所以如果下一行 print $doc; 得到的结果还会是原字符串。所以如果要取得计算的字数,就得写成这样:

$count = $doc=~ tr/0-9/0-9/;

tr 还有一个别名,叫作 y/// 所以要把数字0和9互换,可以写成

$doc =~ y/09/90/; 
或是写成(把 / 换成 |;
$doc =~ y|09|90|;

接来看看 tr 的选项,tr 只有三个选项,英文是 perldoc 的说明,我把他的意思用我的话写出来:

c Complement the SEARCHLIST.  <== 列表没写到的就补给他右边列表的最后一个字符
d Delete found but unreplaced characters. <== 对照表中没有的项目就删掉
s Squash duplicate replaced characters. <== 连续重复出现的字压成一个

来看看范例:

my $text = 'good cheese';
$text =~ tr/eo/eu/s;
print "$text\n";
# 结果 gud chese ,连续重复出现的字已被压成一个

my $big = 'vowels are useful';
$big =~ tr/aeiou/AEI/d; # 注意看对照表左边只有三个字母,所以如果遇到 ou,就会被删掉
print "$big\n";
# 结果 vwEls ArE sEfl

合并以上两个参数
my $text = 'good cheese';
$text =~ tr/eogd/eu/ds;   # 写成 ds 或sd 都可以,顺序不重要
print "$text\n";
# 结果 u chese 

最后来看 c 这个选项,c 较复杂不易懂,大致以我的大脑所知的说明如下:

tr/左列表/右列表/c

规则、左列表没有的,就补右清单的东西。

my $text = 'good cheese';
$text =~ tr/eo/_/c;   #注意看对照表右边只有二个字母,只要右边列表没列到的就补'_',包括空白
print "$text\n";

# 结果 _oo____ee_e

如果右清单写了不只一个呢?会怎样?其实补的时候还是只会拿右清单的最后一个

$doc="<78>Nov  3 11:20:01 163.17.44.1 crond[30367]";
$doc =~ y/a-zA-Z/a-z/c;

# 结果不是英文全都补了右列表的最后一个字符'z'  $doc=
# zzzzNovzzzzzzzzzzzzzzzzzzzzzzzzzcrondzzzzzzz

当然不能都出简单的范例,来看看这个所以以下范列就可以把字符串中非英文字母a-zA-Z给删掉,成为空白。

$doc="<78>Nov  3 11:20:01 163.17.44.1 crond[30367]: (root) CMD (LANG=C LC_ALL=C /usr/bin/mrtg /etc/mrtg/mrtg.cfg --lock-file /var/lock/mrtg/mrtg_l --confcache-file /var/lib/mrtg/mrtg.ok)";
$doc =~ y/a-zA-Z/ /c;

# 结果 
    Nov                         crond          root  CMD  LANG C LC ALL C  usr bin mrtg  etc mrtg mrtg cfg   lock file  var lock mrtg mrtg l   confcache file  var lib mrtg mrtg ok
如果要把上例中连续空白变一个,就同时用 sc 就好了,结果略。
$doc =~ y/a-zA-Z/ /sc;

当然还要写一点不一样的,要不然我写这篇就没意思了。现在如果要把变量的结果放到 tr 里,该怎么写?因为tr的清单是在编译时就写完了,如果要在执行期把演算出来的结果放去清单,就要写成这样(可参考 perlop )

eval "tr/$oldlist/$newlist/";
die $@ if $@;

预设缺省是用 $_ 去评估,所以以下范例:

$doc="<78>Nov  3 11:20:01 163.17.44.1 crond[30367]";
my $leftlist="a-zA-Z";
$_=$doc;
eval "tr/$leftlist/ /cs, 1" or die $@;
print $_."\n" ;

# 结果  Nov crond

结论,s/// 和 tr/// 还有很多用法,不过两者不太一样,要能活用,得花时间,我也没完全搞清"此"。

原始连结: 精赞部落 note.tcc.edu.tw

阅读(1223) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~