折騰了幾天之后發(fā)現(xiàn)按照窮舉法去發(fā)現(xiàn)所有的異常case實在是太難了,因為我們的精力是有限的,一時半會無法想到所有的case,那有沒有什么辦法呢?
一、背景
有一個需求,需要將源碼提供出去,交予三方進行安全審核,為了減少代碼泄漏帶來的影響,要求將
- 自己的源碼中要有代碼注釋
- 對外提供的代碼中,將所有的代碼注釋移除,增加其他人的代碼閱讀難度
二、艱難的爬坑過程
1、整理java中的注釋情形
多行注釋:2、初步入坑
對上一步的情況分析后簡單的結(jié)論,
- 以 /* 開頭,以*/結(jié)尾
- 以 //開頭,以換行或者文件結(jié)束 結(jié)尾
3、核心代碼
re.findall("\/\*.*?\*\/", content, re.S)
- 按行遍歷文件,檢查是否包含 “//”,如果包含就將 “//”及以后的內(nèi)容替換為換行
re.findall("\/\/.*", line, re.S)
4、進入大坑
本以為這樣是一個簡單粗暴的方法,真正跑起來之后發(fā)現(xiàn)有較大問題
- 字符串中包含 多行和單行 注釋的話,就會導(dǎo)致字符串內(nèi)的內(nèi)容被修改
// 后面有一個單獨的 “ ,引號的排查優(yōu)先級高,導(dǎo)致這個引號被保留,導(dǎo)致整體移除異常
"select * from user where name = 'root'"
- 中間的引號不能作為字符串的開始和結(jié)尾
- 最后的雙引號需要算做字符串的結(jié)尾
- 這樣的不能當作雙引號,也就是這樣的不能作為字符串的開始和結(jié)尾
三、回頭是岸
有時候捷徑不是最快的路徑。
折騰了幾天之后發(fā)現(xiàn)按照窮舉法去發(fā)現(xiàn)所有的異常case實在是太難了,因為我們的精力是有限的,一時半會無法想到所有的case,那有沒有什么辦法呢?
這個時候記憶深處的一些內(nèi)容開始冒泡泡,程序員的三大浪漫之一的 編譯原理 開始出現(xiàn)了。
之前懵懵懂懂讀過的文本開始有一點點印象了。重新翻開經(jīng)典之作的內(nèi)容,看看他是怎么來處理詞法和語法的。
1、回顧編譯原理
- 詞法分析,程序中的單詞大體可以分成五類:

- 語法分析,比如,對于賦值語句?
?position = initial + 2 * 60?
?,經(jīng)過語法分析后生成的樹

- 語義分析,比如?
?position = initial + 2 * 60?
? 經(jīng)過語義分析后

2、著手處理
按照編譯原理中講的過程,要先一個的拆成詞,然后將詞串成語句,然后一個語句一個語句的處理。
整體的思路:
- 判斷是否已經(jīng)在不用關(guān)注的范圍內(nèi),例如在 雙引號中間的,在多行注釋中的,在單行注釋后面的
- 如果已經(jīng)開始了,就只用關(guān)注是否是雙引號、多行注釋、單行注釋的結(jié)尾
- 如果是結(jié)尾,就分別處理,
- 在雙引號、多行注釋、單行注釋開始的時候,把前一次的給保存到新文件中
3、代碼
# coding=utf-8
foler_path = "./java/test/"
def rewriteContent(dirpath, filename, content):
writefile = open(dirpath + "/" + filename, "w+")
# print content
writefile.write(content)
writefile.close()
def clean_all_note():
for dirpath, dirnanes, filenams in os.walk(foler_path):
for filename in filenams:
print dirpath + "/" + filename
clean_note(dirpath, filename)
#判斷是否是雙引號,需要排除 '"' 和 \" 的情況,
def is_available_quotes(ch, pre_ch,next_ch):
return ch == "\"" and pre_ch != "\\" and not (pre_ch == "'" and next_ch == "'")
#判斷是否是多行注釋的開頭 即 /*
def is_prefix_multiline_comment(ch, pre_ch):
return ch == "*" and pre_ch == "/"
#判斷是否是多行注釋的結(jié)尾,即 */
def is_suffix_multiline_comment(ch, pre_ch):
return ch == "/" and pre_ch == "*"
#判斷是否是單行注釋 //
def is_single_line_comment(ch, pre_ch):
return ch == "/" and pre_ch == "/"
# 判斷是否是換行
def is_line_feed(ch, pre_ch):
return ch == "\n"
def clean_note(dirpath, filename):
file = open(dirpath + "/" + filename, "r+")
content = file.read()
multiline_ing = False
single_line_ing = False
quotes_ing = False
pre_ch = ""
index = 0
lastPoi = 0
newContent = ""
for ch in content:
if multiline_ing:
if is_suffix_multiline_comment(ch,pre_ch):
# print "m l e:" + pre_ch + ch
lastPoi = index+1
multiline_ing = False
elif single_line_ing:
if is_line_feed(ch,pre_ch) or index == len(content)-1:
# print "s l e:" + content[lastPoi:index-1]
lastPoi = index
single_line_ing = False
elif quotes_ing:
#解決 "\\"
if ch == "\\" and pre_ch == "\\":
ch = ''
if is_available_quotes(ch, pre_ch,content[index+1]):
# print "yinhao e :" + pre_ch + ch
newContent = newContent + content[lastPoi:index]
lastPoi = index
quotes_ing = False
else:
if index == len(content)-1:
# print "e s :" + pre_ch + ch
newContent = newContent + content[lastPoi:index+1]
elif is_available_quotes(ch, pre_ch,content[index+1]):
# print "yinhao s :" + pre_ch + ch
# newContent = newContent + content[lastPoi:index]+"----"
quotes_ing = True
elif is_prefix_multiline_comment(ch, pre_ch):
# print "m l s :" + pre_ch + ch
newContent = newContent + content[lastPoi:index-1]
multiline_ing = True
elif is_single_line_comment(ch, pre_ch):
# print "s l s :" + pre_ch + ch
newContent = newContent + content[lastPoi:index-1]
single_line_ing = True
index = index+1
pre_ch = ch
rewriteContent(dirpath, filename, newContent)
if __name__ == '__main__':
for dirpath, dirnanes, filenams in os.walk(foler_path):
for filename in filenams:
clean_note(dirpath, filename)