手把手帶你實現Markdown編輯器語法高亮

JoeOKQE 7年前發布 | 13K 次閱讀 Markdown 正則表達式 iOS開發 移動開發

  • iOS開發如何使用正則表達式?
  • 使用正則表達式匹配Markdown
  • 配合YYTextView實現語法高亮

本文是作者在獨立開發一款Markdown編輯器App時所寫,讀完本文你將可以實現如下效果:

IMG_3528.PNG

什么是正則表達式?

正則表達式(regular expression)描述了一種字符串匹配的模式,可以用來檢查一個串是否含有某種子串、將匹配的子串做替換或者從某個串中取出符合某個條件的子串等。

如果有同學寫過爬蟲,應該對正則表達式很熟悉,強大的匹配功能讓很多問題引刃而解.運用正則表達式可以驗證用戶輸入(手機號,郵箱,密碼)提取特定規則字符串.

舉個最簡單的栗子:

" [\\u4e00-\\u9fa5]"   //匹配中文
" ^[A-Za-z0-9]+$"   //匹配由數字和26個英文字母組成的字符串

附上簡單的正則語法: NSRegularExpression-Cheatsheet.pdf

推薦一本好書:

精通正則表達式

作者也僅是看過一部分,書前半部分講原理,一共500多頁,略多。

正則匹配如何實現的呢?

通過正則引擎來實現,正則文法對應于有限狀態自動機,又分確定型有限狀態自動機(DFA)和非確定型有限狀態自動機(NFA),這兩種狀態機的能力是一樣的,都能識別正則語言。什么是DFA與NFA呢?這方面屬于編譯原理的知識,作者由于還沒有上過這門課,所以這方面就不誤人子弟了。

感興趣的同學可以看看下面這本書: Parsing Techniques 。這本書主要講前端,大家熟知的可能是龍書,但是龍書不太適合新手,所以就不推薦了。后端方面還有各種鯨書,虎書。

iOS開發如何使用正則匹配

iOS開發中,使用正則匹配的場景不是很多:

  • 注冊檢查帳號是是手機號,避免多次請求服務器
  • 密碼強度檢查
  • 驗證碼檢查

舉個栗子:檢查輸入的是否手機號

//匹配以1開頭,第二位為36578,后面還有九位數字的字符串;
NSString *pattern = @"^[1][36578]\\d{9}$"

//生成正則表達式 NSRegularExpression *regular = [NSRegularExpression regularExpressionWithPattern:pattern options:0 error:nil];

//匹配方法 / (void)enumerateMatchesInString:(NSString )string options:(NSMatchingOptions)options range:(NSRange)range usingBlock:(void (NS_NOESCAPE ^)(NSTextCheckingResult _Nullable result, NSMatchingFlags flags, BOOL stop))block;

  • (NSArray<NSTextCheckingResult > )matchesInString:(NSString *)string options:(NSMatchingOptions)options range:(NSRange)range;

  • (NSUInteger)numberOfMatchesInString:(NSString *)string options:(NSMatchingOptions)options range:(NSRange)range;

  • (nullable NSTextCheckingResult )firstMatchInString:(NSString )string options:(NSMatchingOptions)options range:(NSRange)range;

  • (NSRange)rangeOfFirstMatchInString:(NSString )string options:(NSMatchingOptions)options range:(NSRange)range;/

NSArray *array = [regular matchesInString:string options:0 range:NSMakeRange(0,string.length)];

//判斷數組元素個數是否為0 if (array.count == 0) {

self.loginButton.enabled = NO;

} else{

self.loginButton.enabled = YES;

}</code></pre>

上面僅僅是正則表達式的一個簡單應用。還可以使用正則表達式來進行實時文本搜索高亮,語法高亮,提取特定字符串。作者目前正在獨立開發一個簡單的Markdown編輯App,通過用正則表達式完成了語法高亮。

使用正則表達式匹配Markdown語法

作者在匹配Markdown語法時由于水平限制只匹配了一部分,另外一部分:公式,checkBox沒有匹配。如果哪位

朋友能夠完成希望指點一下。

我們匹配時使用的正則表達式如下:

//# 五級標題
@"^((\#{1,5}+\s+[^#].*))$"

//標題\n---- @"^[^-\n][^\n]*\n-+$"

//標題/n== @"^[^=\n][^\n]*\n=+$"

//行內代碼 @"(?<!)({1,3})([^\n]+?)\\1(?!)"

//多行代碼 @ "\`([\\s\\S]*?)`[\s]?"

//縮進型代碼 @"(^\s$\n)((( {4}|\t).(\n|\z))|(^\s*$\n))+"

//強調 強調 @"((?<!\)\(?=[^ \t])(.+?)(?<=[^ \t])\(?!\)|(?<!)(?=[^ \t])(.+?)(?<=[^ \t])(?!))"

// 強調 強調 @"((?<!\)\{3}(?=[^ \t])(.+?)(?<=[^ \t])\{3}(?!\)|(?<!){3}(?=[^ \t])(.+?)(?<=[^ \t]){3}(?!))"

//text @"(?<!\)\{2}(?=[^ \t])(.+?)(?<=[^ \t])\{2}(?!\)"

// 強調 @"(?<!)__(?=[^ \t])(.+?)(?<=[^ \t])\__(?!)"

// 刪除 @"(?<!~)(?=[^ \t~])(.+?)(?<=[^ \t~])\(?!~)"

//!圖片 @"!?\[(^\[\]]+)\]+)\)|\[([^\[\]]+)\])"

//[鏈接]: @"^[ \t]*\[[^\[\]]\]:"

//1.列表 2.列表 3.列表 @"^[ \t]([+-]|\d+[.])[ \t]+"

//**分割線 @"^[ \t]([-])[ \t]((\1)[ \t]){2,}[ \t]*$"</code></pre>

如何使用?

說一種最簡單但效率最低的方法

  • 使用TextView代理方法,每次文本更改都進行匹配
  • 使用TextKit進行富文本的生成,需要用到匹配結果得到的 range ,TextKit教程請自行搜索;

或者使用更方便的YYTextView;

這種每次更改都要匹配的顯然很低效,但在這個基礎上,我們仍然可以進行一些優化:

  • 匹配空字符串,如果輸入的是空字符串,不再繼續匹配其他語法
  • 如果用戶粘貼文段時,不匹配。

如果使用編譯原理知識來進行語法高亮就可以提高很多性能。但作者學識尚淺,未能完成相關的工作。

性能劣勢

性能問題在上文已經說了,經過測試,當文字超過7000字時,就會出現0.4秒左右的延遲,內存占用也會逐漸變高。使用YYTextView以后內存急劇增加,通常7000字時就會達到100M。但是YYTextView提供了很多方便。考慮到實用性還是選擇了YYTextView;

配合YYTextView實現語法高亮

YYTextView擁有Parser的協議,只需要遵守該協議就可以實現一個Parser。同時還需要設置Parser屬性;

//該方法會傳入一個富文本,在這個方法里寫入我們需要匹配的代碼,然后調用相關方法就可以進行實時語法高亮

  • (BOOL)parseText:(NSMutableAttributedString *)text selectedRange:(NSRangePointer)range {

//舉個栗子:高亮標題 NSRegularExpression headerRegex = [NSRegularExpression regularExpressionWithPattern:@"^((\#{1,5}+\s+[^#].))$" options:0 error:nil]; [headerRegex enumerateMatchesInString:str options:0 range:NSMakeRange(0, str.length) usingBlock:^(NSTextCheckingResult result, NSMatchingFlags flags, BOOL stop) {

        [text yy_setColor:self.headerColor range:result.range];
        [text yy_setFont:self.headerFont range:result.range];

    }];



}</code></pre>

至此,我們的Markdown編輯器語法高亮就實現了,使用同樣的方法我們還可以實現搜索時的文本實時高亮。正則表達式實在太強大,熟悉掌握可以給我們減去很多麻煩。如果有想跟我探討的相關問題的同學可以聯系我,這個App尚在開發中,如果有美工愿意同我一起開發請給我發郵件:)lztuna04@gmail.com

 

 

 

 本文由用戶 JoeOKQE 自行上傳分享,僅供網友學習交流。所有權歸原作者,若您的權利被侵害,請聯系管理員。
 轉載本站原創文章,請注明出處,并保留原始鏈接、圖片水印。
 本站是一個以用戶分享為主的開源技術平臺,歡迎各類分享!