文档章节

Scala实战:巧用集合实现数据脱敏

羊八井
 羊八井
发布于 2016/07/25 23:11
字数 1427
阅读 249
收藏 1

(原文在:《http://www.yangbajing.me/2016/07/25/Scala实战:巧用集合实现数据脱敏 》,转载请注明!)

在日常开发中,经常会遇到对数据进行脱敏处理的需求。像隐藏身份证或者手机号中间几位。比如对于:13812345678这个手机号,我们会使用*号替换中间4位来达到隐藏的目的,就像这样:138****5678。这是一个很常见也很简单的功能需求,这里记录下开发中对这个需求的实现。从一开始命令式的风格到函数式风格,从一开始硬编码隐藏范围和替换字符到调用者可以自定义,从繁琐和代码实现到清晰、简洁……本文将一步一步的给读者展示Scala强大的表现力、丰富的API和高效的生产率体现。

命令式

首先来看看一开始实现的隐藏手机号函数:

scala> def hidePhone(phone: String): String = {
     |   phone.substring(0, 3) + "****" + phone.substring(7)
     | }
hidePhone: (phone: String)String

scala> hidePhone("13812345678")
res1: String = 138****5678

咋一看,代码量很少嘛,也很简洁明了,需求实现的非常好。但其实这段代码有很多坏的和不完善的地方。如:有3个数字,它们决定了哪些字符需要原样保留。但万一业务需求是隐藏末尾5个字符呢?难到我们需要再写一个hideLastPhone函数?这样子太low了……

于是,对hidePhone完成第一次改进,我们让调用方来决定需要隐藏哪些字符而不是在代码里写死保留哪些字符。

def hidePhone(phone: String, start: Int, end: Int): String = {
  val builder = new StringBuilder(phone.substring(0, start))
  var i = start
  while (i < end) {
    builder.append('*')
    i += 1
  }
  builder.append(phone.substring(end))
  builder.toString()
}

代码看起来有点多,但实现了调用方设置隐藏范围,实用性更好了。来看看测试效果:

scala> hidePhone("13812345678", 6, 6 + 5)
res2: String = 138123*****

scala> hidePhone("13812345678", 3, 3 + 4)
res3: String = 138****5678

不错,效果很好。正确!但是,我们换个参数再试试……

scala> hidePhone("13812345678", 6, 6 + 6)
java.lang.StringIndexOutOfBoundsException: String index out of range: -1
  at java.lang.String.substring(String.java:1931)
  at .hidePhone(<console>:18)
  ... 32 elided

Oh……数据越界错误。要修正这个错误也很简单:

def hidePhone(phone: String, start: Int, end: Int): String = {
  val builder = new StringBuilder(phone.substring(0, start))
  var i = start
  while (i < math.min(phone.length, end)) {
    builder.append('*')
    i += 1
  }
  if (end < phone.length) {
    builder.append(phone.substring(end))
  }
  builder.toString()
}

我们修复了两个地方:

  1. while语句不是直接小于end变量,而是小于phone.lengthend两个变量之间更小的那个
  2. 最后一个builder.append语句加上了一个if防卫措施,只有当end小于手机号长度时才添加。

这时,我们再次尝试刚才错误的那个示例。发现它已经可以正确的执行了。

scala> hidePhone("13812345678", 6, 6 + 6)
res5: String = 138123*****

函数式

我们已经看过了命令式的数据税敏代码,没想到这样一个简单的功能还是需要写不少代码的。现在已经使用了具有函数式特性的高级的Scala语言,我们能不能把代码写得更functional更漂亮呢***(代码丑陋也是不可忍的……)***?我们尝试着再次改进一下。

def hidePhone(phone: String, start: Int, end: Int): String = {
  phone
    .zipWithIndex
    .map { case (ch, idx) => if (idx >= start && idx < end) '*' else ch }
    .mkString
}

相比之前那段命令式的代码,这是不是简洁、清晰了很多?我们先使用.zipWithIndex方法将字符串变成一个带索引的Tuple序列,形如:Seq(('1', 0), ('3', 1), ('8', 2), ....)。再通过判断索引idx是否在[start, end)范围来判断返回对应字符还是返回替换后的*号字符。最后再调用mkString方法把Seq[Char](字符序列)格式化成一个字符串。

到了这里,我们还有改进的地方:

  1. 我们想可以自定义替换字符,用户可以使用'-'、'|'等符号来替换,而不是默认的'*'号。
  2. 对于end这个参数,当前代码实现是不替换这个索引的字符。那万一我们的需求是要替换这个索引的字符呢?当然,你可以说传入参数时将end + 1不就行了,但感觉不太好……因为我们还有更好的方案。
def hidePhone(phone: String, replaceRange: Range, replaceChar: Char = '*'): String = {
  phone
    .zipWithIndex
    .map { case (ch, idx) => if (replaceRange.contains(idx)) replaceChar else ch }
    .mkString
}

这时的使用方式就和之前不一样了:

scala> hidePhone("13812345678", 6 until 6 + 5)
res6: String = 138123*****

scala> hidePhone("13812345678", 3 until 3 + 4, '-')
res9: String = 138----5678

scala> hidePhone("13812345678", 3 to 3 + 4, '-')
res7: String = 138-----678

scala> hidePhone("13812345678", 6 until 6 + 6, '^')
res8: String = 138123^^^^^

总结

一个常用的数据脱敏函数,需要注意的地方还是挺多的。而函数式风格的实现相对命令式风格来说从可读性上更具优势。Scala以一种从左到右顺序编写、链式调用实现了数据脱敏这个功能,同时兼具了灵活性和健壮性。

© 著作权归作者所有

羊八井

羊八井

粉丝 93
博文 41
码字总数 51095
作品 0
朝阳
技术主管
私信 提问
Scala编程语言视频教程|Scala视频教程

Scala编程语言视频教程 分享网盘下载——https://pan.baidu.com/s/1kUFL6Ub 密码: m7k4 Scala是一种多范式的编程语言,其设计的初衷是要集成面向对象编程和函数式编程的各种特性。Scala运行于...

nitycka
2017/11/01
0
0
熟练的掌握Scala语言系列课程

课程名字:熟练的掌握Scala语言系列课程 课程网盘下载: http://pan.baidu.com/s/1pJuRAsV 密码: ahr3 课程内容: 共10章,58课时,总时长944分钟 想要深入研究spark,掌握好scala至关重要,目...

混坛魔王
2015/01/09
822
4
京东算法比赛的题目,大家有啥想法??

符号定义: S:提供的商品全集; P:候选的商品子集,P是S的子集; U:用户集合; A:用户对S的行为数据集合; C:S的评价数据。 训练数据部分: 提供2016-02-01到2016-04-15日用户集合U中的...

天池番薯
2017/03/21
379
1
【Spark亚太研究院系列丛书】Spark实战高手之路-第2章动手实战Scala第3小节(1)

函数式编程的核心特色之一是把函数作为参数传递给函数、在函数内部可以定义函数等。 1,动手实战Scala高阶函数 声明一个List集合实例: List集合所在的包已经被预定义自动导入,所以此处不需...

Spark亚太研究院
2014/12/01
0
0
【Scala】Scala中的函数式编程及集合操作

版权声明:本文为博主原创文章,转载请注明出处。 https://blog.csdn.net/gongxifacai_believe/article/details/81988990 1、Scala中的函数式编程简介 Scala中的函数是Java中完全没有的概念。...

魏晓蕾
2018/08/23
0
0

没有更多内容

加载失败,请刷新页面

加载更多

OSChina 周二乱弹 —— 吾不好梦中插人

Osc乱弹歌单(2019)请戳(这里) 【今日歌曲】 @鱼豆腐233 :#今日歌曲分享# 分享My Chemical Romance的单曲《I Don't Love You》: 《I Don't Love You》- My Chemical Romance 手机党少年们...

小小编辑
50分钟前
20
5
ss5 vpn 安装(linux版本)

1. 创建一个文件夹 /ss5 你也可以自定义,不过后续的地方需要注意自己的地址 2. 下载ss5文件(如果你的服务器没有安装wget请使用 yum -y install wget 命令安装 如果连yum都没安装自己查去)(下...

太黑_thj
今天
2
0
八、RabbitMQ的集群原理

集群架构 写在前面 RabbitMQ集群是按照低延迟环境设计的,千万不要跨越WAN或者互联网来搭建RabbitMQ集群。如果一定要在高延迟环境下使用RabbitMQ集群,可以参考使用Shovel和Federation工具。...

XuePeng77
今天
5
0
mac系统下,brew 安装mysql,用终端可以连接,navicat却连接不上?

问题: 1.报错? 2059 - Authentication plugin 'caching_sha2_password' cannot be loaded: dlopen(../Frameworks/caching_sha2_password.so, 2): image not found 2.自己通过设置,已经把密......

写bug的攻城狮
昨天
3
0
老生常谈,HashMap的死循环

问题 最近的几次面试中,我都问了是否了解HashMap在并发使用时可能发生死循环,导致cpu100%,结果让我很意外,都表示不知道有这样的问题,让我意外的是面试者的工作年限都不短。 由于HashMap...

群星纪元
昨天
7
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部