函数 strcmp_挑战最难学函数, 网络爬虫的基础学习: 使用正则表达式函数来实现几乎所有的字符处理函数功能...

1964ef2196bdab9933bae3fce6acf21f.png

如果从MATLAB的基础包(专业的工具箱除外的部分)里面投票: 哪个函数最难学?

我肯定投票给那两个正则表示式函数: regexp(regexpi的功能与regexp类似, 差别小)和regexprep.

这两个函数我学了N次, 或者半途而废, 或者学完就忘(这篇文章的目的就是写给未来的自己, 怕以后忘了, 能看完这篇文章快速想起来).

我写完正则表达式以后, 感觉到了它的强大, 并且发现它能实现几乎所有的字符处理函数功能.

当然, 我的动机并不是想要让正则表达式来代替那些函数, 而是借此机会, 去练习一下刚刚好不容易入门的正则表达式.

正则表达式的一个典型应用: 网络爬虫.

一般一个简单的网络爬虫(不考虑大规律的爬虫, 也不考虑反反爬虫)的典型流程:

1 将网页的内容下载下来.

可以使用webread函数或者urlread函数等.

2 提取出你感兴趣的部分

可以网页的内容很多, 有图片, 有文字, 有链接, 有声音, 有视频等等.

可以通过regexp等字符处理函数的功能提取出你感兴趣的部分.

其中regexp的功能最强大.

---------------正文分割线-------------------------------------------------------------

使用regexprep来实现reverse的功能

使用reverse函数, 将字符串反转.

str = ["airport","control tower","radar","runway"];
newStr = reverse(str)

newStr =
  1×4 string 数组
  1 至 3 列
    "tropria"    "rewot lortnoc"    "radar"
  4 列
    "yawnur"

使用regexpprep实现相同的功能.

regexprep(str, '.*', '${fliplr($0)}')

ans =
  1×4 string 数组
  1 至 3 列
    "tropria"    "rewot lortnoc"    "radar"
  4 列
    "yawnur"

'.*'中的'.'表示任意字符, '*'表示任意长度. 连起来就是任意长度的任意字符, 也就是全选.

'${fliplr($0)}'中'${cmd}'表示执行cmd命令, 返回的结果代替被匹配的字符.

'$0'表示被匹配的字符(这里就是整个单词).

现在就也难理解.

使用regexp来实现strcmp的功能

s1 = ["A","bc";
      "def","G"];
s2 = ["B","c";
      "def","G"];
tf = strcmp(s1,s2)

tf =
  2×2 logical 数组
   0   0
   1   1

限定为完整的匹配, 而不是部分匹配

"^" + s2 + "$"
ans =
  2×2 string 数组
    "^B$"      "^c$"
    "^def$"    "^G$"

string支持加法运算来拼接字符串, 而且支持广播(自动扩展, bsxfun的功能), 在这里就很方便了.

"^"表示字符串的起始位置, "$"表示字符串的结束位置.

"^def$"就表示必须完全匹配单词"def", 不能多, 不能少.

regexp(s1, "^" + s2 + "$")
ans =
  2×2 cell 数组
    {0×0 double}    {0×0 double}
    {[       1]}    {[       1]}

返回的是cell型, 而且元素内容是匹配起始位置的索引值.

通过cellfun, 来转换输出类型

cellfun(@(x) ~isempty(x), (regexp(s1, "^" + s2 + "$")))
ans =
  2×2 logical 数组
   0   0
   1   1

使用regexp来实现strtrim的功能

chr = sprintf('  t   Remove    leading whitespace')

chr =
    '         Remove    leading whitespace'
newChr = strtrim(chr)

newChr =
    'Remove    leading whitespace'

strtrim的功能是去除字符串头部与尾部的空白字符. 这个例子里面, 尾部没有空白字符.

newChr2 = string(regexp(chr, '^s*(w.*w)s*$', 'tokens'));

newChr2 =
    'Remove    leading whitespace'

'^s*(w.*w)s*$'中的"^"与"$"我就不说了, 前面说过了.

's'表示空白字符.'*'表示任意个长度.

那么's*'表示任意个空白字符.

'(w.*w)'中的括号表示我要讲将括号里面的内容看成一个整体(与数学里面的括号功能类似)

'w'表示字母或者数字或者下划线, 看到这些, 读者是否有些眼熟? 没错, 就是命名变量名能够选用的字符.

'tokens'表示提取出括号里面的内容, 而不是匹配的所有内容.

再举个例子, 头部与尾部都有空白字符, 而且是一个数组(展示一个MATLAB的向量化运算)

str = ["   Gemini    ","   Apollo    ";
       "   ISS       ","   Skylab    "];
newStr = strtrim(str)
newStr =
  2×2 string 数组
    "Gemini"    "Apollo"
    "ISS"       "Skylab"
newChr2 = string(regexp(str, '^s*(w.*w)s*$', 'tokens'))

newChr2 =
  2×2 string 数组
    "Gemini"    "Apollo"
    "ISS"       "Skylab"

使用regexp来实现deblank的功能

str = ["  li hata   ", "ye da da", "    li zhi    ha"]

str =
  1×3 string 数组
    "  li hata   "    "ye da da"    "    li zhi    ha"
deblank(str)

ans =
  1×3 string 数组
    "  li hata"    "ye da da"    "    li zhi    ha"

deblank只去除尾部的空白字符, 不去除头部的空白字符.

string(regexp(str, '(.*w)s*$', 'tokens'))
ans =
  1×3 string 数组
    "  li hata"    "ye da da"    "    li zhi    ha"

使用regexprep来实现lower的功能

str = 'I Love MATLAB';
regexprep(str, '[A-Z]', '${char($0+32)}')

ans =
    'i love matlab'

大写全部变成小写.

'[A-Z]'表示A到Z的字母, 也就是全体大写字母.

double('A')
ans =
    65
double('a')
ans =
    97
double('Z')
ans =
    90
double('z')
ans =
   122
double('S')
ans =
    83
double('s')
ans =
   115

发现了什么没有?

任意大写字母与其对应的小写字母的ASCII码正好相差32!

因此, 大写字母转化为对应的小写字母可以通过以下方式得到:

char('S'+32)
ans =
    's'
regexprep(str,'[A-Z]','${char($0+32)}')

不能理解了吧? 所用的知识前面都讲到了.

再来个数组版的:

str = {                                 ...
'Whose woods these are I think I know.' ; ...
'His house is in the village though;'   ; ...
'He will not see me stopping here'      ; ...
'To watch his woods fill up with snow.'};

regexprep(str, '[A-Z]', '${char($0+32)}')

ans =
  4×1 cell 数组
    {'whose woods these are i think i know.'}
    {'his house is in the village though;'  }
    {'he will not see me stopping here'     }
    {'to watch his woods fill up with snow.'}

使用regexprep来实现upper的功能

str = 'I Love MATLAB';
regexprep(str, '[a-z]', '${char($0-32)}')

ans =
    'I LOVE MATLAB'
str = {                                 ...
'Whose woods these are I think I know.' ; ...
'His house is in the village though;'   ; ...
'He will not see me stopping here'      ; ...
'To watch his woods fill up with snow.'};

regexprep(str, '[a-z]', '${char($0-32)}')

ans =
  4×1 cell 数组
    {'WHOSE WOODS THESE ARE I THINK I KNOW.'}
    {'HIS HOUSE IS IN THE VILLAGE THOUGH;'  }
    {'HE WILL NOT SEE ME STOPPING HERE'     }
    {'TO WATCH HIS WOODS FILL UP WITH SNOW.'}

这个和lower版的类似, 就不解释了.

使用regexprep来实现insertBefore的功能

任务: 在连续(至少一个)空格前面添加逗号.

str = "bread     cheese wine";
regexprep(str, 's+', ',$0')

ans =
    "bread,     cheese, wine"

's+'中的's'表示空白字符, '+'表示一个或以上.注意与'*'的不同. '*'表示任意个, 也就是0个或以上.

使用regexprep来实现erase的功能

任务: 去除单词"the"以及后面的一个空格.

str = ["the quick brown fox jumps";
"over the lazy dog";
"the tthe then athen"];
regexprep(str, '<the ', '')

ans =
  3×1 string 数组
    "quick brown fox jumps"
    "over lazy dog"
    "tthe then athen"

"<"确保匹配的是单词的开头, 而不匹配中间部分.

''就是0长度的字符串, 用0长度的字符串来代替, 不就是相当于删除了吗?

类似的思想自数组里面经常使用:

a = 1:3

a =
     1     2     3

a(2) = []

a =
     1     3

作为对比:

regexprep(str, 'the ', '')


ans =
  3×1 string 数组
    "quick brown fox jumps"
    "over lazy dog"
    "tthen athen"

可以看到, 去除掉"<", 单词"tthe"中"the"被匹配进去了.

使用regexprep来实现eraseBetween的功能

任务: 去除quick与fox之间的单词, 同时, 不能有多余的空格或者缺失空格

str = "The quick brown fox";
regexprep(str, '(?<=quick )[ws]*(?=fox)', '')


ans =
    "The quick fox"

'(?<=quick )[ws]*(?=fox)'解释:

'(?<=quick )'表示匹配部分的前面必须是"quick "

'(?=fox)'表示匹配部分的后面必须是"fox"

'[ws]'表示w或s

使用regexp来实现strtok的功能

任务: 提取出第一个单词.

chr = '     Four score and seven years ago';
token = strtok(chr)

token =
    'Four'
allwords = regexp(chr, '<w*>', 'match')

allwords =
  1×6 cell 数组
  1 至 5 列
    {'Four'}    {'score'}    {'and'}    {'seven'}    {'years'}
  6 列
    {'ago'}

'<w*>'中'<'表示单词的开始位置, '>'表示单词的结束位置.

'match'表示返回匹配的字符串.

allwords{1}
ans =
    'Four'

使用regexp来实现strsplit的功能

任务: 提取出数字

data = '1.21, 1.985, 1.955, 2.015, 1.885';
C = strsplit(data,', ')

C =
  1×5 cell 数组
    {'1.21'}    {'1.985'}    {'1.955'}    {'2.015'}    {'1.885'}
str2double(C)
ans =
    1.2100    1.9850    1.9550    2.0150    1.8850
data = '1.21, 1.985, 1.955, 2.015, 1.885';
C = regexp(data, ', ', 'split')
C =
  1×5 cell 数组
    {'1.21'}    {'1.985'}    {'1.955'}    {'2.015'}    {'1.885'}

'split'表示根据匹配的字符, 来切分字符串.

str2double(C)
ans =
    1.2100    1.9850    1.9550    2.0150    1.8850

使用regexp来实现splitlines的功能

任务: 按行来进行切分.

chr = 'Whose woods these are I think I know.';
chr = [chr newline 'His house is in the village though;']

chr =
    'Whose woods these are I think I know.
     His house is in the village though;'
C = splitlines(chr)

C =
  2×1 cell 数组
    {'Whose woods these are I think I know.'}
    {'His house is in the village though;'  }
regexp(chr, 'n', 'split')'

ans =
  2×1 cell 数组
    {'Whose woods these are I think I know.'}
    {'His house is in the village though;'  }

对于其他的字符处理函数, 我就不写了, 要不然篇幅太长, 而且工作量比较大, 读者有兴趣可以举一反三写出来.

目前, 感觉有难度的字符处理函数为:

join
compose
sprintf

有没有发现有什么共同点?

没错, 都属于"增量"型的函数.

正则表达式本质是匹配出"子集", 然后又后续操作.好像与"增量"型函数功能相反.

------2018年11月14日更新--------------------------------------------------------

接触了一段时间的Cody, 发现regexp, regexprep是神器.

处理字符型问题, 就不说了, 这个它们的本职工作, 这篇文章提到了, 它们几乎可以替代所有的其他字符处理函数.

让我惊奇的是: 它们还能够解数值类的问题!

实战例子:

M31415926:CodyNote009:索引寻址系列探讨(Part.1)​zhuanlan.zhihu.com
67da89b8d929d2f7049c30163cf4a3ba.png

见最底下的几个解法.

吐糟:

距离写这篇文章差不多有6个月了, 我又差不多忘光了这两个函数的用法了!

╮(╯▽╰)╭

原因可能是我平时编程主要是处理数值类型的问题, 字符型问题几乎接触不到, 所以, regexp, regexprep函数也用不上. 所以, 学完就忘.

现在, 发现了它能够处理数值类型的问题, 如果经常用, 应该不容易忘了吧.

创作不易, 请大家"素质三连": 点赞, 收藏, 分享.

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值