phone.go 3.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146
  1. /*
  2. * Copyright (c) 2015, zheng-ji.info
  3. * */
  4. package gophone
  5. import (
  6. "bytes"
  7. "encoding/binary"
  8. "errors"
  9. "fmt"
  10. "strconv"
  11. "strings"
  12. )
  13. const (
  14. IntLen = 4
  15. CharLen = 1
  16. PhoneIndexLength = 9
  17. Chunk = 100
  18. )
  19. type GOPhone struct {
  20. phoneDat []byte
  21. }
  22. type PhoneRecord struct {
  23. PhoneNum string
  24. Province string
  25. City string
  26. ZipCode string
  27. AreaZone string
  28. CardType string
  29. }
  30. func New(phoneDat []byte) *GOPhone {
  31. if nil == phoneDat || len(phoneDat) == 0 {
  32. panic(errors.New("phoneDat is required"))
  33. }
  34. return &GOPhone{
  35. phoneDat: phoneDat,
  36. }
  37. }
  38. func (g *GOPhone) Display() {
  39. fmt.Println(g.getVersion())
  40. fmt.Println(g.getTotalRecord())
  41. fmt.Println(g.getFirstRecordOffset())
  42. }
  43. func (pr PhoneRecord) String() string {
  44. _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)
  45. return _str
  46. }
  47. func (g *GOPhone) getVersion() string {
  48. return string(g.phoneDat[0:IntLen])
  49. }
  50. func (g *GOPhone) getTotalRecord() int32 {
  51. total := (int32(len(g.phoneDat)) - g.getFirstRecordOffset()) / PhoneIndexLength
  52. return total
  53. }
  54. func (g *GOPhone) getFirstRecordOffset() int32 {
  55. var offset int32
  56. buffer := bytes.NewBuffer(g.phoneDat[IntLen : IntLen*2])
  57. _ = binary.Read(buffer, binary.LittleEndian, &offset)
  58. return offset
  59. }
  60. func (g *GOPhone) getIndexRecord(offset int32) (phonePrefix int32, recordOffset int32, cardType byte) {
  61. buffer := bytes.NewBuffer(g.phoneDat[offset : offset+IntLen])
  62. _ = binary.Read(buffer, binary.LittleEndian, &phonePrefix)
  63. buffer = bytes.NewBuffer(g.phoneDat[offset+IntLen : offset+IntLen*2])
  64. _ = binary.Read(buffer, binary.LittleEndian, &recordOffset)
  65. buffer = bytes.NewBuffer(g.phoneDat[offset+IntLen*2 : offset+IntLen*2+CharLen])
  66. _ = binary.Read(buffer, binary.LittleEndian, &cardType)
  67. return
  68. }
  69. func (g *GOPhone) getOpCompany(cardtype byte) string {
  70. var cardStr = ""
  71. switch cardtype {
  72. case '1':
  73. cardStr = "移动"
  74. case '2':
  75. cardStr = "联通"
  76. case '3':
  77. cardStr = "电信"
  78. case '4':
  79. cardStr = "电信虚拟运营商"
  80. case '5':
  81. cardStr = "联通虚拟运营商"
  82. default:
  83. cardStr = "移动虚拟运营商"
  84. }
  85. return cardStr
  86. }
  87. // BinarySearch
  88. func (g *GOPhone) Find(phoneNum string) (pr *PhoneRecord, err error) {
  89. err = nil
  90. if len(phoneNum) < 7 || len(phoneNum) > 11 {
  91. return nil, errors.New("illegal phone length")
  92. }
  93. var left int32 = 0
  94. phoneSevenInt, _ := strconv.ParseInt(phoneNum[0:7], 10, 32)
  95. phoneSevenInt32 := int32(phoneSevenInt)
  96. totalLen := int32(len(g.phoneDat))
  97. right := g.getTotalRecord()
  98. firstPhoneRecordOffset := g.getFirstRecordOffset()
  99. for {
  100. if left > right {
  101. break
  102. }
  103. mid := (left + right) / 2
  104. currentOffset := firstPhoneRecordOffset + mid*PhoneIndexLength
  105. if currentOffset >= totalLen {
  106. break
  107. }
  108. curPhone, recordOffset, cardType := g.getIndexRecord(currentOffset)
  109. if curPhone > phoneSevenInt32 {
  110. right = mid - 1
  111. } else if curPhone < phoneSevenInt32 {
  112. left = mid + 1
  113. } else {
  114. s := recordOffset
  115. e := recordOffset + int32(strings.Index(string(g.phoneDat[recordOffset:recordOffset+Chunk]), "\000"))
  116. recordContent := string(g.phoneDat[s:e])
  117. _tmp := strings.Split(recordContent, "|")
  118. cardStr := g.getOpCompany(cardType)
  119. pr = &PhoneRecord{
  120. PhoneNum: phoneNum,
  121. Province: _tmp[0],
  122. City: _tmp[1],
  123. ZipCode: _tmp[2],
  124. AreaZone: _tmp[3],
  125. CardType: cardStr,
  126. }
  127. return
  128. }
  129. }
  130. return nil, errors.New("num not found")
  131. }