一個人下定決心去做一件事的話,一定會表現出極大的熱情來。她的確是下定決心要學好寫程序了,這從她表現出來的超高熱情就能看出來。熱情的表現之一就是下班後不急着回家了,而是拿着她的筆記本電腦到我的工位讓我給她講解寫程序;熱情的表現之二就是有時大半夜的會給我發微信,問我書上的某些練習題該怎麼做。而我也很樂意在下班後和她呆在一起,因爲我回家也是一個人待着,相較之下,和她一起待在辦公室,對我來說真的是莫大的幸福了。
“戴老師,你看看我寫得對不對?”那天晚上都12點了,她發來她寫的那個我給她留的課後作業。
“寫得還行。“看了她發過來的代碼截圖,我說,“這麼晚了,你還在學習啊。”
“是呀。”她說,“要多練習嘛。”
“嗯,早點休息吧。”我打着哈欠,在微信上給她發出這幾個字。
我確實是很困了,以致發出那幾個字以後,就不知不覺睡着了,第二天早上纔看到她發過來的“好”和晚安的動畫表情。
由於晚上太困,沒有仔細看她發過來的代碼截圖,今天仔細看了,才發現了一些問題,於是在下班後,開始學習新內容之前,我就和她說起我發現的問題。
“你昨晚寫的代碼,有些問題。”我說。
“是呀。”還沒等我說出問題在哪裏,她就接過我的話說,“本來想請教你來着,可是猜想你睡了,所以就沒來得及問你。”
於是,她打開她的筆記本電腦,翻出她昨晚寫的程序來,然後說出她自己早已發現的問題:“我這個程序只能判斷出5位數,如果更多位怎麼辦,不可能把這個else if一直寫下去吧。”
“那你有什麼想法沒?”我問。
“沒有,昨晚我想着想着就睡着了,才發現寫代碼真的挺廢腦力的。”
“那看到計算結果,你能數出來多少位嗎?”
“這個肯定能啊,我好歹是上過幼兒園的啊。”
當然,上過幼兒園就會數數了,我不禁笑了起來。
”哈哈,知道你上過幼兒園。“我笑着說,”那你發現沒有,一個只有一位的整數,每乘以10,位數就增加一位。”
“是這樣,可是這和寫程序有關係嗎?”她有些不解地看向我。
“你仔細想想。”我不打算直接給她答案。
“嗯,我想想。”她陷入了深深的思考。
都說認真做事的男人最帥,但我發現認真思考的女人,也真的很美。
她在認真思考,而我一時沒事可做,索性就認真地欣賞起她的這種美來,於是就出現這樣一幅定格畫面:她用手撐着頭,眼睛有些發直,在她旁邊,我側身看着她,像雕塑一樣一動不動。
“哈,我知道了。”她突然地發出聲音,把我下了一跳。
”你怎麼了?“她大笑着說,顯然我一激靈的反應將她逗笑了。
”沒怎麼。“我有些不好意思,希望她沒有發現我一直盯着她看吧。
”你說說你想到什麼了。“我接着說。
於是她認真地說起她的想法來:”把一個整數循環除以10,直到爲0爲止,計算除了多少次,就知道這個整數是多少位了。“
”哈哈,沒錯。“我說。
”等我改下代碼。“她說着,就去修改代碼了。
編譯運行:
編譯後,她反覆運行程序,輸入各種整數,然後再去數結果的位數和計算機輸出的位數是不是相同。
”哈哈,全正確,太有成就感了。“在把程序運行了不知多少次後,她滿足地說。
“其實,電腦也是上過幼兒園哦。”等她享受完她的成就感後,我說。
我這一句沒頭沒腦的話,讓她愣了一下,隨即她就明白過來,我是在揶揄她那句上過幼兒園的話。
“你是想說電腦像我一樣聰明呢,還是想說我和電腦在一個智商水平啊?”她佯裝慍怒地說。
“不不,我是說電腦也會數數的。”我連忙求饒道。
“怎麼說?”
“那就該今天的主角出場了,你猜是什麼?”我故作神祕地說。
“快說是什麼?”
“字符串。”
“這個我知道,就是一串字符串起來,對不對?”
她顯然已經看過書上關於字符串這一章節了,所以纔會對字符串這個概念不陌生,並能說出她對字符串的理解。
“對,其實,我們一直在用字符串。你剛剛程序裏面那個‘請輸入一個整數:’就是一個字符串。”
“那確實是一串字符,可是這和讓電腦數數有什麼關係呢?”
“既然字符串是一串字符,那是不是有長度呢?”我問。
“對,好像字符串有一個計算長度的操作來着。”說着,她拿出書來翻着。
“所以,將1到n相加的結果轉換成字符串,然後用字符串的計算長度的操作來計算出長度就是這個整數的位數了。”我在她翻書之際接着說。
“找到了。”她翻到了講字符串操作的那一頁說。
“那麼試試這個方法唄。”
“可是怎麼將一個整數轉換成字符串呢?”她有些犯難。
“這個簡單啊,用加號加上一個空字符串就好了,就像你剛剛寫的程序裏面那樣。”我指着她電腦上那句輸出結果的代碼說。
“哦哦。”她說着改起代碼來。
編譯運行:
“哈哈,這樣簡單多了。”她高興地說。
“字符串還有其他一些操作,你知道嗎?”我問。
“知道一些,你再講講吧。”
“那我們還是來看幾個例子吧。”我說,因爲從示例中學習無疑是最高效的。
於是,經過短暫的一個思考後,我說:“假如給你一個郵箱地址,你怎麼判斷它是不是格式正確的郵箱地址?”
“我想想。”她說着,拿出紙筆來,一邊寫,一邊唸叨着:“郵箱地址的格式是前面一串字符,接着跟一個@,最後是一個網址。”
“是這樣的一個字符串就是郵箱地址。”她指着紙上她寫出的一個郵箱地址,擡起頭來看着我說,意思是問我她說得對不對。
“沒錯,郵箱的一般格式爲:用戶名@郵箱服務器域名。我們可以從4個方面來驗證:1是否包含@字符,2@字符是不是隻有一個,3字符.是不是在@字符後面,4是不是以.com結尾。”我肯定了她的想法,並進行了擴展。
”嗯,好像是這樣。“她說。
”那我們來寫程序吧。“我說。
“好啊。”她說,然後新建了一個文件後,看着我不說話。
“寫啊。”我說。
“怎麼寫?”她說。
我明白過來,她在等着我給她說怎麼寫。
“好吧,我來寫,你來看。”我說,我終於想起來是我要給她講字符串的一些用法的。於是,我從她手上接過筆記本電腦,寫起這個驗證郵箱地址的程序來:
“你看,寫起來很簡單的。”寫完後我說。
這句話的背後意思是,這麼簡單的事情其實不需要我出手的,她顯然聽出來我話裏的意思,臉上有了一些委屈和不悅。
”對於你來說是簡單,可我是什麼都不懂的小白啊。“她說。
”對對,我應該站在你的角度來看問題纔對。“我連忙認錯,然後又說:”那你看看這個程序,有什麼不懂的,你說出來,我給你解答。“
”嗯,我看看。“她臉上的不悅已經煙消雲散,這自然讓接下來的一問一答在友好和輕鬆的氛圍中順暢進行着。
“咦,那個雙斜槓那行是幹什麼的?”
“哦,雙斜槓後面的都是註釋。註釋就是在源碼中添加的一些備註說明,不會被編譯到程序裏面的。嗯……,這個註釋的作用呢,主要是方便別人在看代碼的時候容易理解代碼的意思。其實更大的用處是留給自己看的,因爲寫程序的人,在時間久了的時候會經常忘了當時寫這些代碼是幹嘛的。”
“誒,還真是,有這個註釋,一看就看明白了這些代碼是在幹嘛了。不過,爲什麼atIdx等於-1就是沒有找到@呢?”
“這個indexOf這個方法是從前向後查找指定的字符或者字符串,如果找到了就會把指定的字符或字符串在被找的字符串中的位置返回回來,如果沒找到就返回-1,所以這裏判斷一下返回結果是不是-1就知道字符串中是不是有@這個字符了。對應的lastIndexOf是從後向前面查找,這個不用我解釋了吧?”
“嗯,這個我懂了,從前向後查和從後向前查,查到的@的位置相同,說明整個字符串中只有一個@,因爲如果有多個的話,這兩種查法結果肯定不一樣。不過,那個return是什麼意思?”
“return是返回的意思。返回的意思呢,就是退出當前正在執行的方法,返回到調用它的地方繼續執行。這裏因爲是在main方法中,而調用main方法的是系統,所以整個程序就退出了,返回到系統了。至於方法的一些東西,後面會專門學習的。”
“嗯,我沒什麼問題了。”
“那toLowerCase和endsWith是幹什麼的?”這回換成我問問題了。
“這個看字面意思就知道了啊,toLowerCase是將字符串中字符全部換成小寫,endsWith是判斷是不是以某個字符串結尾。”
“完全正確。”我說,“這個例子只用到這些字符串操作的方法,還有很多其他的你看看書就知道了,書上應該有一個表格列出這些方法。”
“書上是有這麼一個表格。”在我說話的時候,她已經從書上找到了那個表格,但隨即臉上就流露愁容,“不過,這麼多內容我怎麼記得住啊。”
“記這些幹什麼?”
“不記嗎?那要用到這些怎麼辦?”
“你傻啊,要用的時候,百度一下不就好了。”說着我打開百度,在輸入框中輸入“java 截取字符串”,然後就出來一大堆截取字符串的內容。
“原來如此。”她一副發現天大祕密的表情。
“什麼原來如此?”
“我原以爲你們程序員都好厲害,這麼厚一本書的內容都記得清清楚楚,原來是靠百度的啊。”
“所以啊,不要覺得寫程序有多難。”我乘機鼓勵她,並厚着臉皮誇了下我自己,“不過我還是很厲害的,我知道怎麼百度。”
“嗯,你厲害,臉皮也厚得厲害。哈哈。”她說完忍不住笑出聲來。
我也笑了起來,我是有點不好意思的笑。
“驗證郵箱地址還有一個很簡單的方法,你知道是什麼嗎?”等她笑完了,我說,轉移了關於我臉皮厚的話題。
“不知道。”她一副虛心求教的樣子看着我說。
“用正則表達式。”我說,同時開始修改剛剛那個驗證郵箱地址的程序:
”是不是比剛剛那個簡潔很多。“寫完後我說。
”就這麼一兩行代碼就搞定了?“她看了代碼,覺得不可思議。
”對啊,這就是正則表達式的魅力所在。“
”那快給我講講這個正則表達式。“
”正則表達式是一個用於描述字符串規則的工具,或者說是記錄文本規則的代碼。“我試圖給正則表達式是什麼下一個定義,但好像不太成功,這從她那一臉茫然就能看出來。
所以我不得不回到剛剛驗證郵箱地址的例子中來,於是我接着說:”還是來看剛剛那個例子吧。在這個例子中,郵箱地址的規則是這樣的:1、以1個或多個字類字符(字母、數字和下劃線)開頭;2、緊接着是一個@;3、再後面是多個字母數字加點的組合;最後是一個com結尾。“
說到這,我停了下來,給她一些思考的時間。
”嗯,郵箱地址的格式的確是這樣的。“她思考了以後說。
”既然規則是固定的,那麼我們就用一個字符串來表達出這麼一個規則,然後去驗證輸入的字符串是不是符合這個規則,符合就是郵箱地址,不符合就不是郵箱地址。這個表達規則的字符串就是正則表達式。關於表達式的常用語法我們還是來看看書吧。“說着,我拿過書,翻到講正則表達式那個章節,遞給她看。
字符的取值範圍
1.[abc] : 表示可能是a,可能是b,也可能是c。
2.[^abc]: 表示不是a,b,c中的任意一個
3.[a-zA-Z]: 表示是英文字母
4.[0-9]:表示是數字
簡潔的字符表示
^:匹配輸入字符串開始的位置
$:匹配輸入字符串結尾的位置
.:匹配任意的字符
\d:表示數字
\D:表示非數字
\s:表示由空字符組成,[ \t\n\r\x\f]
\S:表示由非空字符組成,[^\s]
\w:表示字母、數字、下劃線,[a-zA-Z0-9_]
\W:表示不是由字母、數字、下劃線組成
數量表達式
1.?: 表示出現0次或1次
2.+: 表示出現1次或多次
3.*: 表示出現0次、1次或多次
4.{n}:表示出現n次
5.{n,m}:表示出現n~m次
6.{n,}:表示出現n次或n次以上
邏輯表達式
1.XY: 表示X後面跟着Y,這裏X和Y分別是正則表達式的一部分
2.X|Y:表示X或Y,比如"food|f"匹配的是foo(d或f),而"(food)|f"匹配的是food或f
3.(X):子表達式,將X看做是一個整體
她接過書,一會兒看看書,一會兒看看電腦屏幕,再歪着頭思考一會兒。過了幾分鐘的時間,她終於像是恍然大悟了。
”哈哈,我明白了。“她說,”這個正則表達式還真是個好東西,用它來驗證字符串真是太方便了。“
”你真的這麼快就弄懂了?“我有些懷疑,因爲當初我學這個的時候就花了很長時間才搞懂的,所以我是準備好好給她解釋一番的。
”嗯,懂了。“她很肯定的回答。
”那我出幾個題目考考你。“
”好啊,你出。“
”電話號碼的正則表達式怎麼寫?“
“嗯,電話號碼是一個3位或4位的區號,再加這個橫槓,再加一個8位的號碼。”她一邊唸叨着,一邊在紙上寫。
”寫好了。“她說,然後我看到了她寫的正則表達式:
^(\d{3}|\d{4})-\d{8}$
"不錯,很對。”我說。
看來她真的掌握了,但我還是有點懷疑,她的學習能力如此強嗎?我決定讓她再多寫幾個來看看。
“網址的正則表達式怎麼寫?”我說。
她寫:
^http:\/\/[A-Za-z0-9]+\.[A-Za-z0-9]+[\/=\?%\-&_~`@[\]\':+!]*([^<>\"\"])*$
“Ip地址的正則表達式怎麼寫?”我說。
她寫:
(\d+)\.(\d+)\.(\d+)\.(\d+)
“郵政編碼的正則表達式怎麼寫?”我說。
她寫:
^[1-9]\d{5}$
“好吧,我真是佩服你的學習能力了,這麼快就掌握了正則表達式。”我說。
“哈哈,承蒙老師誇獎了,還是老師教得好。”她笑着說,臉上有一些自豪。
“好了,我們就不要再互相吹捧了吧。既然你學得這麼快,那麼我們繼續後面的內容吧。”我覺得每次我們這樣互相吹捧都挺搞笑的。
“我還有一個問題,”她突然說道,“^(匹配輸入字符串開始的位置)和$(匹配輸入字符串結尾的位置)這兩個符號有什麼意義,好像寫不寫也不影響字符串的驗證啊。”
“嗯,光是做字符串格式的驗證加不加這兩個字符似乎沒什麼影響,但是用來在一個很長的字符串找出特定格式的字符串就有區別了。例如,要從一個很長的字符串中找出裏面的所有電話號碼,你想想怎麼寫程序?”這也是正則表達式經常用到的場景,正好給她講講。
“哎呀,你就別賣關子了,就直接給我講吧。”
“好吧,就剛剛那個找電話號碼的例子,我寫個程序,你看了就會明白加不加^和$這兩個字符的區別了。”
於是,我拿過電腦,一通敲擊鍵盤,程序已經寫好:
編譯運行:
“看出來不同沒?”程序運行後,我問她。
“我想想。“她回答。
我不再說話,她需要一些時間自己去領悟,學習終究還是自己思考後得來的才深刻。她就一直盯着電腦屏幕,而且保持一個姿勢不動,這讓我產生了時間停止的錯覺。
“哈,我懂了。”隨着她的聲音傳入我的耳朵,時間又開始流動起來。
“那你說說。”我說。
“匹配輸入字符串的開始位置,就是匹配找到的字符串必須在被查找字符串的開始位置。匹配輸入字符串的結尾位置,意思是匹配找到的字符串必須在被查找字符串的結尾位置。所以你剛寫那個例子,第二個表達式只找到開頭的電話號碼,第三個表達式只找到最後一個電話號碼,而最後一個表達式一個電話號碼都沒找到,因爲沒有一個電話號碼是既在開始位置又在結尾位置的。”
“你這樣理解也沒有錯,不過...”我本想給她講講我的理解的,但轉念一想,她已經有自己的理解了,我再給她說我的理解,這會攪亂她腦海中建立起來的概念,於是我止住,沒有往下說了。
“不過什麼?”
“沒什麼。”
“沒什麼是什麼?”
“沒什麼就是你太聰明瞭,不用我講你就能領悟這麼深奧的東西。”我笑着說。
“這話我愛聽,不過我本來就聰明啊。”她也笑。
“那你這麼聰明的話,我們來做一個有挑戰的例子吧。”
“好啊,什麼例子?”她表現出來很大的興趣來。
“假如有一段文字,裏面包含很多數字,現在要計算這些數字的總和是多少,該怎麼做。”
“這叫有挑戰嗎?用正則表達式找出這些數字,然後加起來不就好了。”
“那就寫一下看看了。”
“看着。”她說完就開始敲起代碼來。
她寫的代碼:
編譯運行:
“咦,怎麼不對呢?”看到運行結果,她大惑不解地看向我。
“哈哈,你不是說沒有挑戰的嗎?”我笑了起來。
“你就別笑我了,快看看是怎麼回事。”
“好呢,且讓我給你改上一改。”
我改過的代碼:
編譯運行:
“正確了,還是你厲害。”看到正確的運行結果,她有些悻悻地說。
“其實你沒寫出來是很正常的,因爲這裏有一個知識點你沒學到。”我安慰她。
“我想也是,像我這麼冰雪聰明的人,要不是有知識盲區,怎麼可能難到我。”她復又自負起來。
“哈哈,其實你的臉皮也不薄啊。”我笑到。
“你才臉皮厚。”她佯裝慍怒到。
“是是,我們還是來看看這個讓你打臉的知識點吧。”我也不知怎麼應對她的“怒火”,於是乖乖求饒到。
“還記得前面講的變量的類型嗎?”我問她,得到肯定的回答後我繼續說,“字符串這個類型在做加法運算的時候它是把兩個字符串拼接成一個字符串,所以你那樣直接用字符串相加自然會得到一個更長的字符串而已。只有double、int這樣的數字類型的變量相加纔會進行算數運算,所以我們要先將數字字符串轉換成數值變量再進行相加。”
“所以那個Double.parseDouble就是把字符串轉換成double類型?”
“對,你說得沒錯,除了這個,還有Integer.parseInt用於將字符串轉成int類型,Long.parseLong轉成long等等,這個你也不用強記,知道就行了,需要用的時候直接百度就出來了。”
“我懂,不記得就百度嘛。”她不懷好意地笑着。
我知道她又在嘲笑我凡事就知道百度,不過這就是程序員的做事方式嘛,所以我也不在意的笑笑。
“嗯,類型轉換,我記着了。”她認真地說,不待我說什麼,她又說:“那個%.2f是幹嘛的?”
“那是字符串的格式化。”我說,“又一個知識盲區,對吧?”
“知道還不快講。”她說。
“字符串的格式化就是…”我突然不知道該怎麼講了,有很多東西自己心裏明白是什麼,可就是表達不出來,我似乎有嚴重的語言表達障礙。
“是什麼?”她撲閃着那雙靈動的大眼睛看着我說。
“嗯,就是一些數據按照一定格式轉換成字符串。”我整理了一下思緒說,“就像這裏,我們要讓金額顯示兩位小數,所以就用%.2f這個格式字符來代表一個兩位小數的數字。然後在執行String.format這個方法的時候就用後面的變量去替換%.2f,並且是按照它的格式進行替換,最終得到一個格式化後的字符串。”
她茫然地搖頭:“你在說些什麼呢?一點都不懂。”
“這個怎麼解釋呢?”我不自覺地撓着頭,不知道是我表達有問題,還是她理解有問題,我想還是我表達的問題吧,所以我的大腦高速運轉,只爲找出一個能讓她明白的表達方式。
最終我說:“這麼說吧,你現在要定義一個字符串,但是這個字符串有些地方你暫時不知道是什麼內容,於是你就用一些特殊的符號去把位置占上,再然後用String.format這個方法把那些特殊的符號換成具體的內容。”
“這麼說的話,我應該明白了,但是這個地方直接用加號將金額拼接進去就好了啊,幹嘛還這麼複雜,弄一個%.2f去佔着位置,後面又去替換?”她終於說她明白了,這讓我舒了一口氣。對於新問題,以她現在的好學,也是能理解的。
“這就是格式化的意義所在了,假設金額是一個整數的話,直接拼接就沒有後面的小數位了。用格式化的話,因爲我們設置的格式是兩位小數,所以不管金額變量是不是整數,也不管小數的位數是多少位,都會轉成兩位小數的字符串,然後再替換。”
“哦,這樣啊。”她不太相信地說。
“不然你試試。”我說,我想讓她自己改程序驗證。
於是在她把程序改動並執行了幾次後,她說:“好吧,你說得一點沒錯。”
“那是,我怎麼會錯呢。”我有些得意地笑着說。
“是是,你是老師,我是學生嘛。”她說,頓了一下她又說:“今天學太多了,要好好消化一下。”
“是要好好消化一下,回去多做一些練習。”我附和着。
“啊,都這麼晚了。”她看了看時間說,“我們回家吧。”
“好,走,下班回家。”我說。
於是,我們一道回家,這是第一次有機會和她一起回家,雖然我就住她家附近,回家路徑也一樣,但以前從沒一道回家過,即便看到她在前面,我也沒有勇氣追上去。但今天她卻和我一起走在回家的路上,而且我預感以後會一直這樣,這或許是對我教她寫程序的回饋吧。不管怎樣,我依然是無比開心的,臉上卻極力裝着波瀾不驚的樣子。