银行卡格式化

昨天有这么个需求, 在输入或显示银行卡号码的时候添加空格进行分段, 可读性明显就提升了, 用户友好了,
但是在输入的时候怎么加呢? 今天就来解决一下.
因为是iOS的项目要求, 所以我们用swift实现(写这个文章的时候是swift 2.1)

1
2
3
4
// 给出银行卡号如下
4292xxxxxxxxxxxx
// 输出为
4392 xxxx xxxx xxxx

实现过程

过程其实感觉很简单, 只要每4个字符添加一个空格就好, 我觉得比较方便的做法就是先把字符串转换成字符数组

1
2
3
4
5
6
let cardNumber = "4292xxxxxxxxxxxx"
let cardNumberArray = Array<Character>(cardNumber.characters) // 指定Array的元素为 Character
print(cardNumberArray)

// output
["4", "2", "9", "2", "x", "x", "x", "x", "x", "x", "x", "x", "x", "x", "x", "x"]

变成了这样之后就该往里面加空格了, 很自然的就想着从前面往后面加了, 但是这有个问题

1
2
3
["4", "2", "9", "2", "x", "x", "x", "x", "x", "x", "x", "x", "x", "x", "x", "x"]
// index: 4 ^ 在这里加了空格之后整个数组的长度就会发生变化
// 本来index是 4 8 12, 但是第四个元素后加了空格之后下次的index就需要+1变成9, 在下一个变成12+2

我们需要插入空格的位置列表变成了 [4, 9, 14, 19, 24, 29, 34, 39, …] 发现这好像是个公差为5的等差数列…
那么构造这个等差数列就完了呗, 于是写成了这么个东西

实现1:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
let cardNumber = "4292xxxxxxxxxxxx"
// 需要操作cardNumberArray, 所以这里用var来声明
var cardNumberArray = Array<Character>(cardNumber.characters) // 指定Array的元素为 Character

let lengthOfCardNumber = cardNumber.lengthOfBytesUsingEncoding(NSUTF8StringEncoding)
for i in 0..<lengthOfCardNumber { // 循环条件
if (i + 1) % 5 == 0 { // 构造我们的等差数列
cardNumberArray.insert("_", atIndex: i) // 为了看着方便, 空格都用_代替了
}
}

print(cardNumberArray.map({String($0)}).joinWithSeparator(""))

// output
4292_xxxx_xxxx_xxxx

voila, 但是感觉好像哪里不对劲, 把卡号加长看看是啥样. 于是我们把实现1的第一行写成

1
2
3
4
let cardNumber = "123412341234123412341234123412341234123412341234123412341234"

// output
1234_1234_1234_1234_1234_1234_1234_1234_1234_1234_1234_1234_123412341234

问题就出现了, 因为我们实现1的循环条件是从0到卡号长度, 但是中间加了好多空格之后实际长度会慢慢地超出
原来的长度, 所以后面的就处理不到了. 不过卡号能有多长呢? 现在这样就能用了呀!
改进一下. 因为循环的长度不够了造成这种问题那么给他加长就完了呗, 具体加多少呢? 恭喜你, 答对了,
有多少个空格就加多少, 我们把卡号长度整除4就好了, 不过别忘了还需要减1, 不然如果正好是4的整数倍的话最后也会加上一个空格了
实现1的5~6行换成

1
2
3
4
5
6
let lengthOfCardNumber = cardNumber.lengthOfBytesUsingEncoding(NSUTF8StringEncoding)
let loopEdge = lengthOfCardNumber + lengthOfCardNumber / 4 - 1
for i in 0..<loopEdge {

// output
1234_1234_1234_1234_1234_1234_1234_1234_1234_1234_1234_1234_1234_1234_1234

这回真的好了!

本来感觉挺简单的一东西变成了这样, 所以有没有更好一点的方式呢?

重新思考这个过程

上面要命的处理那么多, 究其原因是因为加空格长度会变, 所以绕开这个长度的问题应该就好了, 但是
加空格肯定是会变长的呀! 怎么办呢? ** 从后面往前加! **
从后往前添加空格的过程中虽然字符串的长度一直在边长, 但是并不会影响到前面的索引位置所以改进如下

实现2

1
2
3
4
5
6
7
8
9
10
11
12
13
14
let cardNumber = "123412341234123412341234123412341234123412341234123412341234"
var cardNumberArray = Array<Character>(cardNumber.characters) // 指定Array的元素为 Character

let lengthOfCardNumber = cardNumber.lengthOfBytesUsingEncoding(NSUTF8StringEncoding)
var i = lengthOfCardNumber

while i > 0 {
if i % 4 == 0 && i != lengthOfCardNumber {
cardNumberArray.insert("_", atIndex: i)
}
i -= 1
}

print(cardNumberArray.map({String($0)}).joinWithSeparator(""))