csv.go 5.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195
  1. package utils
  2. import (
  3. "encoding/csv"
  4. "fmt"
  5. "log"
  6. "net"
  7. "os"
  8. "strconv"
  9. "time"
  10. )
  11. const (
  12. defaultOutput = "result.csv"
  13. maxDelay = 9999 * time.Millisecond
  14. minDelay = 0 * time.Millisecond
  15. maxLossRate float32 = 1.0
  16. )
  17. var (
  18. InputMaxDelay = maxDelay
  19. InputMinDelay = minDelay
  20. InputMaxLossRate = maxLossRate
  21. Output = defaultOutput
  22. PrintNum = 10
  23. Debug = false // 是否开启调试模式
  24. )
  25. // 是否打印测试结果
  26. func NoPrintResult() bool {
  27. return PrintNum == 0
  28. }
  29. // 是否输出到文件
  30. func noOutput() bool {
  31. return Output == "" || Output == " "
  32. }
  33. type PingData struct {
  34. IP *net.IPAddr
  35. Sended int
  36. Received int
  37. Delay time.Duration
  38. Colo string
  39. }
  40. type CloudflareIPData struct {
  41. *PingData
  42. lossRate float32
  43. DownloadSpeed float64
  44. }
  45. // 计算丢包率
  46. func (cf *CloudflareIPData) getLossRate() float32 {
  47. if cf.lossRate == 0 {
  48. pingLost := cf.Sended - cf.Received
  49. cf.lossRate = float32(pingLost) / float32(cf.Sended)
  50. }
  51. return cf.lossRate
  52. }
  53. func (cf *CloudflareIPData) toString() []string {
  54. result := make([]string, 7)
  55. result[0] = cf.IP.String()
  56. result[1] = strconv.Itoa(cf.Sended)
  57. result[2] = strconv.Itoa(cf.Received)
  58. result[3] = strconv.FormatFloat(float64(cf.getLossRate()), 'f', 2, 32)
  59. result[4] = strconv.FormatFloat(cf.Delay.Seconds()*1000, 'f', 2, 32)
  60. result[5] = strconv.FormatFloat(cf.DownloadSpeed/1024/1024, 'f', 2, 32)
  61. // 如果 Colo 为空,则使用 "N/A" 表示
  62. if cf.Colo == "" {
  63. result[6] = "N/A"
  64. } else {
  65. result[6] = cf.Colo
  66. }
  67. return result
  68. }
  69. func ExportCsv(data []CloudflareIPData) {
  70. if noOutput() || len(data) == 0 {
  71. return
  72. }
  73. fp, err := os.Create(Output)
  74. if err != nil {
  75. log.Fatalf("创建文件[%s]失败:%v", Output, err)
  76. return
  77. }
  78. defer fp.Close()
  79. w := csv.NewWriter(fp) //创建一个新的写入文件流
  80. _ = w.Write([]string{"IP 地址", "已发送", "已接收", "丢包率", "平均延迟", "下载速度(MB/s)", "地区码"})
  81. _ = w.WriteAll(convertToString(data))
  82. w.Flush()
  83. }
  84. func convertToString(data []CloudflareIPData) [][]string {
  85. result := make([][]string, 0)
  86. for _, v := range data {
  87. result = append(result, v.toString())
  88. }
  89. return result
  90. }
  91. // 延迟丢包排序
  92. type PingDelaySet []CloudflareIPData
  93. // 延迟条件过滤
  94. func (s PingDelaySet) FilterDelay() (data PingDelaySet) {
  95. if InputMaxDelay > maxDelay || InputMinDelay < minDelay { // 当输入的延迟条件不在默认范围内时,不进行过滤
  96. return s
  97. }
  98. if InputMaxDelay == maxDelay && InputMinDelay == minDelay { // 当输入的延迟条件为默认值时,不进行过滤
  99. return s
  100. }
  101. for _, v := range s {
  102. if v.Delay > InputMaxDelay { // 平均延迟上限,延迟大于条件最大值时,后面的数据都不满足条件,直接跳出循环
  103. break
  104. }
  105. if v.Delay < InputMinDelay { // 平均延迟下限,延迟小于条件最小值时,不满足条件,跳过
  106. continue
  107. }
  108. data = append(data, v) // 延迟满足条件时,添加到新数组中
  109. }
  110. return
  111. }
  112. // 丢包条件过滤
  113. func (s PingDelaySet) FilterLossRate() (data PingDelaySet) {
  114. if InputMaxLossRate >= maxLossRate { // 当输入的丢包条件为默认值时,不进行过滤
  115. return s
  116. }
  117. for _, v := range s {
  118. if v.getLossRate() > InputMaxLossRate { // 丢包几率上限
  119. break
  120. }
  121. data = append(data, v) // 丢包率满足条件时,添加到新数组中
  122. }
  123. return
  124. }
  125. func (s PingDelaySet) Len() int {
  126. return len(s)
  127. }
  128. func (s PingDelaySet) Less(i, j int) bool {
  129. iRate, jRate := s[i].getLossRate(), s[j].getLossRate()
  130. if iRate != jRate {
  131. return iRate < jRate
  132. }
  133. return s[i].Delay < s[j].Delay
  134. }
  135. func (s PingDelaySet) Swap(i, j int) {
  136. s[i], s[j] = s[j], s[i]
  137. }
  138. // 下载速度排序
  139. type DownloadSpeedSet []CloudflareIPData
  140. func (s DownloadSpeedSet) Len() int {
  141. return len(s)
  142. }
  143. func (s DownloadSpeedSet) Less(i, j int) bool {
  144. return s[i].DownloadSpeed > s[j].DownloadSpeed
  145. }
  146. func (s DownloadSpeedSet) Swap(i, j int) {
  147. s[i], s[j] = s[j], s[i]
  148. }
  149. func (s DownloadSpeedSet) Print() {
  150. if NoPrintResult() {
  151. return
  152. }
  153. if len(s) <= 0 { // IP数组长度(IP数量) 大于 0 时继续
  154. fmt.Println("\n[信息] 完整测速结果 IP 数量为 0,跳过输出结果。")
  155. return
  156. }
  157. dateString := convertToString(s) // 转为多维数组 [][]String
  158. if len(dateString) < PrintNum { // 如果IP数组长度(IP数量) 小于 打印次数,则次数改为IP数量
  159. PrintNum = len(dateString)
  160. }
  161. headFormat := "%-16s%-5s%-5s%-5s%-6s%-12s%-5s\n"
  162. dataFormat := "%-18s%-8s%-8s%-8s%-10s%-16s%-8s\n"
  163. for i := 0; i < PrintNum; i++ { // 如果要输出的 IP 中包含 IPv6,那么就需要调整一下间隔
  164. if len(dateString[i][0]) > 15 {
  165. headFormat = "%-40s%-5s%-5s%-5s%-6s%-12s%-5s\n"
  166. dataFormat = "%-42s%-8s%-8s%-8s%-10s%-16s%-8s\n"
  167. break
  168. }
  169. }
  170. Cyan.Printf(headFormat, "IP 地址", "已发送", "已接收", "丢包率", "平均延迟", "下载速度(MB/s)", "地区码")
  171. for i := 0; i < PrintNum; i++ {
  172. fmt.Printf(dataFormat, dateString[i][0], dateString[i][1], dateString[i][2], dateString[i][3], dateString[i][4], dateString[i][5], dateString[i][6])
  173. }
  174. if !noOutput() {
  175. fmt.Printf("\n完整测速结果已写入 %v 文件,可使用记事本/表格软件查看。\n", Output)
  176. }
  177. }