/* * 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") }