項目裏面有一個需求,要對sql進行簡單的語法分析java
爲了不sql裏面的字符串和註釋對語法分析作干擾,我寫了一個java函數,對sql進行修剪,刪除裏面字符串和註釋,用空格代替
週末閒着沒事,我用go從新實現了這個功能,感受應該會有後來人能夠用上git
說明:
sql裏面的註釋有兩種單行註釋和多行註釋,其中單行註釋以--開頭,以\n結尾,多行註釋以/開頭,以/結尾
sql字符串是以'開頭,'結尾,但特別的地方是連續兩個單引號是表明一個單引號而不是字符串結束標誌github
關鍵函數以下:sql
`
/**數組
將字節數組裏面註釋和字符串,用空格替換 rangeBeg和rangeEnd是數組元素起始位置 左閉右開
*/
func TrimSqlByteArray(sql []byte, rangeBeg int, rangeEnd int) []byte {
sqlLength := rangeEnd - rangeBeg - 1;
//刪除註釋或者字符串後 用空格填充 必免因刪除致使粘連改變sql語義
const chPad = ' 'app
//結果切片,預分配空間爲入參sql長度一半
result := make([] byte, 0, sqlLength / 2)函數
//本字符類型
var charType int = NORMAL;
for i := rangeBeg; i < rangeEnd; i++ {
/*
*utf8編碼不影響判斷
//跳過非英文字符
if sql[i] & 0x80 != 0 {
//utf8編碼:UTF-8是一種變長字節編碼方式。對於某一個字符的UTF-8編碼,若是隻有一個字節則其最高二進制位爲0;
//若是是多字節,其第一個字節從最高位開始,連續的二進制位值爲1的個數決定了其編碼的位數,其他各字節均以10開頭。
//UTF-8最多可用到6個字節。 這裏不考慮異常,由於go的字符串基本都是標準utf8編碼
i += getPreNotZeroCount(sql[i]) - 1
continue;
}
*/單元測試
//本字符類型 預設爲普通字符 charType = NORMAL ch := sql[i] //下一個字符 var chNext byte; chNext = getCharSafe(sql, rangeEnd, i + 1) //非有效sql內容結束位置 endPos := 0 if ch == '-' && chNext == '-' { //單行註釋 charType = LINE //下標移到非有效字符的最後 endPos = seekToNext(sql, i + 2, rangeEnd, charType) } else if ch == '/' && chNext == '*' { //多行註釋 charType = MULTI //下標移到非有效字符的最後 endPos = seekToNext(sql, i + 2, rangeEnd, charType) } else if ch == '\'' { //字符串 charType = STRING //下標移到非有效字符的最後 endPos = seekToNext(sql, i + 1, rangeEnd, charType) } //若是字符是非有效字符 則用空格代替 不然保持原樣 if charType == NORMAL { result = append(result, ch) } else { result = append(result, chPad) i = endPos - 1 }
}測試
return result;
}編碼
/**
獲取字符串或者註釋的右邊界位置(不包含)
rangeEnd是數組邊界
*/
func seekToNext(sql []byte, begPos int, rangeEnd int, charType int) int {
result := begPos;
switch charType {
case MULTI:
for ; result < rangeEnd; result++ {
ch := sql[result]
chNext := getCharSafe(sql, rangeEnd, result+ 1)
if ch == '*' && chNext == '/' { result = result + 1; break; } } break
case LINE:
for ; result < rangeEnd; result++ {
ch := sql[result]
if ch == '\n' { break; } } break
case STRING:
for ; result < rangeEnd; result++ {
ch := sql[result]
chNext := getCharSafe(sql, rangeEnd, result + 1)
//sql字符串裏面連續的單引號被認爲是' 則不是字符串結束標誌 if ch == '\'' && chNext == '\'' { result = result + 1; continue; } else if ch == '\'' { break; } } break
default:
break;
}
result++;
return result;
}
`
完整代碼及單元測試已上傳 https://github.com/kingstarer/kingstarer.git