|
@@ -0,0 +1,146 @@
|
|
|
+/*
|
|
|
+* Copyright (c) 2015, zheng-ji.info
|
|
|
+* */
|
|
|
+package gophone
|
|
|
+
|
|
|
+import (
|
|
|
+ "bytes"
|
|
|
+ "encoding/binary"
|
|
|
+ "errors"
|
|
|
+ "fmt"
|
|
|
+ "strconv"
|
|
|
+ "strings"
|
|
|
+)
|
|
|
+
|
|
|
+const (
|
|
|
+ IntLen = 4
|
|
|
+ CharLen = 1
|
|
|
+ PhoneIndexLength = 9
|
|
|
+ Chunk = 100
|
|
|
+)
|
|
|
+
|
|
|
+type GOPhone struct {
|
|
|
+ phoneDat []byte
|
|
|
+}
|
|
|
+
|
|
|
+type PhoneRecord struct {
|
|
|
+ PhoneNum string
|
|
|
+ Province string
|
|
|
+ City string
|
|
|
+ ZipCode string
|
|
|
+ AreaZone string
|
|
|
+ CardType string
|
|
|
+}
|
|
|
+
|
|
|
+func New(phoneDat []byte) *GOPhone {
|
|
|
+ if nil == phoneDat || len(phoneDat) == 0 {
|
|
|
+ panic(errors.New("phoneDat is required"))
|
|
|
+ }
|
|
|
+ return &GOPhone{
|
|
|
+ phoneDat: phoneDat,
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+func (g *GOPhone) Display() {
|
|
|
+ fmt.Println(g.getVersion())
|
|
|
+ fmt.Println(g.getTotalRecord())
|
|
|
+ fmt.Println(g.getFirstRecordOffset())
|
|
|
+}
|
|
|
+
|
|
|
+func (pr PhoneRecord) String() string {
|
|
|
+ _str := fmt.Sprintf("PhoneNum: %s\nAreaZone: %s\nCardType: %s\nCity: %s\nZipCode: %s\nProvince: %s\n", pr.PhoneNum, pr.AreaZone, pr.CardType, pr.City, pr.ZipCode, pr.Province)
|
|
|
+ return _str
|
|
|
+}
|
|
|
+
|
|
|
+func (g *GOPhone) getVersion() string {
|
|
|
+ return string(g.phoneDat[0:IntLen])
|
|
|
+}
|
|
|
+
|
|
|
+func (g *GOPhone) getTotalRecord() int32 {
|
|
|
+ total := (int32(len(g.phoneDat)) - g.getFirstRecordOffset()) / PhoneIndexLength
|
|
|
+ return total
|
|
|
+}
|
|
|
+
|
|
|
+func (g *GOPhone) getFirstRecordOffset() int32 {
|
|
|
+ var offset int32
|
|
|
+ buffer := bytes.NewBuffer(g.phoneDat[IntLen : IntLen*2])
|
|
|
+ _ = binary.Read(buffer, binary.LittleEndian, &offset)
|
|
|
+ return offset
|
|
|
+}
|
|
|
+
|
|
|
+func (g *GOPhone) getIndexRecord(offset int32) (phonePrefix int32, recordOffset int32, cardType byte) {
|
|
|
+ buffer := bytes.NewBuffer(g.phoneDat[offset : offset+IntLen])
|
|
|
+ _ = binary.Read(buffer, binary.LittleEndian, &phonePrefix)
|
|
|
+ buffer = bytes.NewBuffer(g.phoneDat[offset+IntLen : offset+IntLen*2])
|
|
|
+ _ = binary.Read(buffer, binary.LittleEndian, &recordOffset)
|
|
|
+ buffer = bytes.NewBuffer(g.phoneDat[offset+IntLen*2 : offset+IntLen*2+CharLen])
|
|
|
+ _ = binary.Read(buffer, binary.LittleEndian, &cardType)
|
|
|
+ return
|
|
|
+}
|
|
|
+
|
|
|
+func (g *GOPhone) getOpCompany(cardtype byte) string {
|
|
|
+ var cardStr = ""
|
|
|
+ switch cardtype {
|
|
|
+ case '1':
|
|
|
+ cardStr = "移动"
|
|
|
+ case '2':
|
|
|
+ cardStr = "联通"
|
|
|
+ case '3':
|
|
|
+ cardStr = "电信"
|
|
|
+ case '4':
|
|
|
+ cardStr = "电信虚拟运营商"
|
|
|
+ case '5':
|
|
|
+ cardStr = "联通虚拟运营商"
|
|
|
+ default:
|
|
|
+ cardStr = "移动虚拟运营商"
|
|
|
+ }
|
|
|
+ return cardStr
|
|
|
+}
|
|
|
+
|
|
|
+// BinarySearch
|
|
|
+func (g *GOPhone) Find(phoneNum string) (pr *PhoneRecord, err error) {
|
|
|
+ err = nil
|
|
|
+ if len(phoneNum) < 7 || len(phoneNum) > 11 {
|
|
|
+ return nil, errors.New("illegal phone length")
|
|
|
+ }
|
|
|
+
|
|
|
+ var left int32 = 0
|
|
|
+ phoneSevenInt, _ := strconv.ParseInt(phoneNum[0:7], 10, 32)
|
|
|
+ phoneSevenInt32 := int32(phoneSevenInt)
|
|
|
+ totalLen := int32(len(g.phoneDat))
|
|
|
+ right := g.getTotalRecord()
|
|
|
+ firstPhoneRecordOffset := g.getFirstRecordOffset()
|
|
|
+ for {
|
|
|
+ if left > right {
|
|
|
+ break
|
|
|
+ }
|
|
|
+ mid := (left + right) / 2
|
|
|
+ currentOffset := firstPhoneRecordOffset + mid*PhoneIndexLength
|
|
|
+
|
|
|
+ if currentOffset >= totalLen {
|
|
|
+ break
|
|
|
+ }
|
|
|
+ curPhone, recordOffset, cardType := g.getIndexRecord(currentOffset)
|
|
|
+ if curPhone > phoneSevenInt32 {
|
|
|
+ right = mid - 1
|
|
|
+ } else if curPhone < phoneSevenInt32 {
|
|
|
+ left = mid + 1
|
|
|
+ } else {
|
|
|
+ s := recordOffset
|
|
|
+ e := recordOffset + int32(strings.Index(string(g.phoneDat[recordOffset:recordOffset+Chunk]), "\000"))
|
|
|
+ recordContent := string(g.phoneDat[s:e])
|
|
|
+ _tmp := strings.Split(recordContent, "|")
|
|
|
+ cardStr := g.getOpCompany(cardType)
|
|
|
+ pr = &PhoneRecord{
|
|
|
+ PhoneNum: phoneNum,
|
|
|
+ Province: _tmp[0],
|
|
|
+ City: _tmp[1],
|
|
|
+ ZipCode: _tmp[2],
|
|
|
+ AreaZone: _tmp[3],
|
|
|
+ CardType: cardStr,
|
|
|
+ }
|
|
|
+ return
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return nil, errors.New("num not found")
|
|
|
+}
|