Skip to content

Commit

Permalink
Upgrade to more Swifty API.
Browse files Browse the repository at this point in the history
  • Loading branch information
RockfordWei committed Nov 21, 2017
1 parent 2dc91d4 commit 7fefdbe
Show file tree
Hide file tree
Showing 5 changed files with 188 additions and 59 deletions.
42 changes: 26 additions & 16 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,10 @@ Ensure you have installed and activated the latest Swift 4.0 tool chain.
Add this project as a dependency in your Package.swift file.

``` swift
.Package(url:"https://github.com/PerfectSideRepos/Perfect-RegEx.git", majorVersion: 3)
.package(url:"https://github.com/PerfectSideRepos/Perfect-RegEx.git", from: "3.1.0")

...
dependencies: ["RegEx"]
```

Then please add the following line to the beginning part of swift sources:
Expand All @@ -62,34 +65,41 @@ import Regex

## Quick Start

The following demo shows how to extract substrings with a pattern, for example, phone numbers:
The following demo shows how to extract substring ranges with a pattern:

``` swift
let text = "this is a long test. The correct number is 123-456-7890 or 647-237-8888 but please don't deal because it's not my number. 我的电话我可不告诉你是 416-970-8888 🇨🇳 🇨🇦 "
var source = "there is a bloody bad bread on my bed."

let ranges = source.match(pattern: "b[a-z]+d")
// it will figure out the range of `blood`, `bad` `bread` and `bed`

// you can do further operations, such as remove:
source.removeSubrange(ranges[0])

let found = text.matches()
print(found)
print(source)
// the result should be:
// there is a y bad bread on my bed.
```

## API Info

There is only one function in this library, called `matches()`:

``` swift
extension String {
public func matches(pattern: String = "[0-9]{3}-[0-9]{3}-[0-9]{4}", limitation: Int = 32)
-> [(rangeBegin: Int, rangeEnd: Int, extraction: String)]
/// test if the string contains certain pattern
/// - parameters:
/// - pattern: string to recognize
/// - return: true for found
public func contains(pattern: String) -> Bool

/// find string ranges
/// - parameters:
/// - pattern: string to recognize
/// - return: a string range array
public func match(pattern: String) -> [Range<String.Index>]
}
```

### Parameters:
- pattern: String, the regular expression; default value is a typical phone number, like 123-456-7890
- limitation: Int, the maximum number of matches allowed to find; default is 32, which means the first 32 needles would be found and save from the stack

### Returns:

`[(rangeBegin: Int, rangeEnd: Int, extraction: String)]` - a tuple array, each element is the range begin / end mark, with the extraction value; if nothing found or error happened, the result set will be empty.

## Issues

We are transitioning to using JIRA for all bugs and support related issues, therefore the GitHub issues has been disabled.
Expand Down
42 changes: 25 additions & 17 deletions README.zh_CN.md
Original file line number Diff line number Diff line change
Expand Up @@ -56,8 +56,10 @@
请在您的Package.swift 文件中增加如下依存关系:

``` swift
.package(url:"https://github.com/PerfectSideRepos/Perfect-RegEx.git", from: "3.1.0")

.Package(url:"https://github.com/PerfectSideRepos/Perfect-RegEx.git", majorVersion: 3)
...
dependencies: ["RegEx"]

```

Expand All @@ -71,34 +73,40 @@ import Regex

## 快速上手

以下示范代码展示了如何从字符串内提取电话号码
以下示范代码展示了如何从字符串内寻找特定模式的内容,并删除

``` swift
let text = "this is a long test. The correct number is 123-456-7890 or 647-237-8888 but please don't deal because it's not my number. 我的电话我可不告诉你是 416-970-8888 🇨🇳 🇨🇦 "
var source = "there is a bloody bad bread on my bed."

let found = text.matches()
print(found)
let ranges = source.match(pattern: "b[a-z]+d")
// 返回结果将会找到`blood`, `bad` `bread` and `bed`

// 同时进一步操作,比如删除选项
source.removeSubrange(ranges[0])

print(source)
// 结果字符串变成了
// there is a y bad bread on my bed.
```

## API 参考

本函数库内只有一个函数,名为`matches()`:

``` swift
extension String {
public func matches(pattern: String = "[0-9]{3}-[0-9]{3}-[0-9]{4}", limitation: Int = 32)
-> [(rangeBegin: Int, rangeEnd: Int, extraction: String)]
/// 检查字符串是否包含某种模式
/// - 参数:
/// - pattern: 待识别内容
/// - 返回值: 如果找到返回真值
public func contains(pattern: String) -> Bool

/// 找到所有符合条件的字串范围信息
/// - 参数:
/// - pattern: 待识别内容
/// - 返回值: 范围信息数组
public func match(pattern: String) -> [Range<String.Index>]
}
```

### 参数:
- pattern: String, 正则表达式,默认为一个电话号码,比如 123-456-7890
- limitation: Int, 允许检索的最大结果数量限制。默认为32,意味着只有前32个结果能够返回

### 返回值:

`[(rangeBegin: Int, rangeEnd: Int, extraction: String)]` - 一个元组数组,每个元素分别有三个单元,依次为检索结果的起点为止、终点位置,以及随后提取的匹配字符串。如果没找到或者出错,返回值为空数组。

### 问题报告、内容贡献和客户支持

我们目前正在过渡到使用JIRA来处理所有源代码资源合并申请、修复漏洞以及其它有关问题。因此,GitHub 的“issues”问题报告功能已经被禁用了。
Expand Down
96 changes: 96 additions & 0 deletions Sources/Regex/Regex.swift
Original file line number Diff line number Diff line change
Expand Up @@ -23,14 +23,110 @@ import SwiftGlibc
import Darwin
#endif

public class RegEx {
var reg = regex_t()
public init?(_ pattern: String) {
guard 0 == regcomp(&reg, pattern, REG_EXTENDED) else {
return nil
}
}
deinit {
regfree(&reg)
}

/// test if the current string contains a certain pattern
/// - parameters:
/// - string: string to search
/// - returns: true if found
public func exists( _ string: String) -> Bool {
return match(string).count > 0
}

/// using regular expression to extract substrings
/// - parameters:
/// - string: String to search
/// - limitation: Int, the maximum number of matches allowed to find
/// - returns:
/// [Range] - an array, each element is a range of match
public func match(_ string: String) -> [Range<String.Index>] {

// set up an empty result set
var found = [Range<String.Index>]()

// prepare pointers
guard let me = strdup(string) else {
return found
}

// string length
let sz = Int(string.count)
let limitation = sz

// cursor of the string buffer
var cursor = me

// allocate a buffer for the outcomes
let m = UnsafeMutablePointer<regmatch_t>.allocate(capacity: limitation)
defer {
m.deallocate(capacity: limitation)
free(me)
}

// loop until all matches were found
while 0 == regexec(&reg, cursor, limitation, m, 0) {

// retrieve each matches from the pointer buffer
for i in 0 ... limitation - 1 {

// if reach the end, the position marker will be -1
let p = m.advanced(by: i).pointee
guard p.rm_so > -1 else {
break
}//end guard

// append outcomes to return set
let start = String.Index.init(encodedOffset: Int(p.rm_so))
let end = String.Index.init(encodedOffset: Int(p.rm_eo))
found.append(start ..< end)
}//next i

cursor = cursor.advanced(by: Int(m.pointee.rm_eo))
}

return found
}
}

extension String {

/// test if the string contains certain pattern
/// - parameters:
/// - pattern: string to recognize
/// - return: true for found
public func contains(pattern: String) -> Bool {
guard let reg = RegEx(pattern) else {
return false
}
return reg.exists(self)
}

/// find string ranges
/// - parameters:
/// - pattern: string to recognize
/// - return: a string range array
public func match(pattern: String) -> [Range<String.Index>] {
guard let reg = RegEx(pattern) else {
return []
}
return reg.match(self)
}
/// using regular expression to extract substrings
/// - parameters:
/// - pattern: String, the regular expression; default value is a typical phone number, like 123-456-7890
/// - limitation: Int, the maximum number of matches allowed to find; default is 32, which means the first 32 needles would be found and save from the stack
/// - returns:
/// [(Int, Int, String)] - a turple array, each element is the range begin / end mark, with the extraction value; if nothing found or error happened, the result set will be empty.
@available(*, deprecated)
public func matches(pattern: String = "[0-9]{3}-[0-9]{3}-[0-9]{4}", limitation: Int = 32) -> [(rangeBegin: Int, rangeEnd: Int, extraction: String)] {

// set up an empty result set
Expand Down
46 changes: 30 additions & 16 deletions Tests/RegexTests/RegexTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -21,28 +21,42 @@ import XCTest
@testable import Regex

class RegexTests: XCTestCase {
func testExample() {
let test = "this is a long test. The correct number is 123-456-7890 or 647-237-8888 but please don't deal because it's not my number. 我的电话我可不告诉你是 416-970-8888"

let outcome = test.matches()
print(outcome)
XCTAssertEqual(outcome.count, 3)
XCTAssertEqual(outcome[0].2, "123-456-7890")
XCTAssertEqual(outcome[1].2, "647-237-8888")
XCTAssertEqual(outcome[2].2, "416-970-8888")
}
static var allTests : [(String, (RegexTests) -> () throws -> Void)] {
return [
("testRanges", testRanges),
("testSlack", testSlack),
("testExample", testExample)
]
}

func testRanges() {
XCTAssertTrue("I have a dream.".contains(pattern: "d(.*)m"))
var source = "there is a bloody bad bread on my bed."
let ranges = source.match(pattern: "b[a-z]+d")
XCTAssertEqual(ranges.count, 4)
print("ranges", ranges)
source.removeSubrange(ranges[0])
print("remove bloody", source)
}

@available(*, deprecated)
func testExample() {
let test = "this is a long test. The correct number is 123-456-7890 or 647-237-8888 but please don't deal because it's not my number. 我的电话我可不告诉你是 416-970-8888"

let outcome = test.matches()
print(outcome)
XCTAssertEqual(outcome.count, 3)
XCTAssertEqual(outcome[0].2, "123-456-7890")
XCTAssertEqual(outcome[1].2, "647-237-8888")
XCTAssertEqual(outcome[2].2, "416-970-8888")
}

@available(*, deprecated)
func testSlack() {
let source = "send you <@U4MNSLWMD> :cookie: <@hello> <@myboy123>"
let u = source.matches(pattern: "<@[^>]+>")
print(u)
XCTAssertEqual(u.count, 3)
}

static var allTests : [(String, (RegexTests) -> () throws -> Void)] {
return [
("testExample", testExample),
("testSlack", testSlack)
]
}
}
21 changes: 11 additions & 10 deletions test.sh
Original file line number Diff line number Diff line change
@@ -1,13 +1,14 @@
SRCPACK=/tmp/pr.tgz
tar czf $SRCPACK Sources Tests Package.swift
REPO=/tmp/reg.linux
mkdir -p $REPO
pushd .
echo "-------------- LINUX SWIFT 4.0 ----------------"
cd $REPO
tar xzf $SRCPACK
docker pull rockywei/swift:4.0
docker run -it -v $REPO:/home -w /home rockywei/swift:4.0 /bin/bash -c "swift build -c release && swift test"
echo "-------------- OS X / Xcode ----------------"
rm -rf .build
rm -rf Package.pins
rm -rf Package.resolved
swift build
popd
swift build -c release
swift test
echo "-------------- LINUX SWIFT 4.0 ----------------"
rm -rf .build_linux
rm -rf Package.resolved
docker pull rockywei/swift:4.0
docker run -it -v $PWD:/home rockywei/swift:4.0 /bin/bash -c "cd /home;swift build --build-path=.build_linux; swift build -c release --build-path=.build_linux;swift test --build-path=.build_linux"

0 comments on commit 7fefdbe

Please sign in to comment.