main.go 2.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990
  1. package main
  2. import (
  3. "bufio"
  4. "errors"
  5. "flag"
  6. "fmt"
  7. "io"
  8. "os"
  9. "strconv"
  10. "strings"
  11. )
  12. // run 接收参数、输入和输出,方便测试时不依赖真实终端。
  13. // 参数不包含程序名,通常由 main 传入 os.Args[1:]。
  14. func run(args []string, input io.Reader, output io.Writer) error {
  15. // ContinueOnError 让解析失败返回错误,而不是直接结束整个进程。
  16. flags := flag.NewFlagSet("study", flag.ContinueOnError)
  17. flags.SetOutput(output) // 帮助信息和参数解析错误写到这里。
  18. name := flags.String("name", "", "学习者姓名;交互模式可留空")
  19. minutes := flags.Int("minutes", 0, "学习时长(分钟,1~1440)")
  20. interactive := flags.Bool("interactive", false, "从标准输入读取姓名和时长")
  21. if err := flags.Parse(args); err != nil {
  22. return err
  23. }
  24. if flags.NArg() != 0 {
  25. return fmt.Errorf("不支持的位置参数:%s", strings.Join(flags.Args(), " "))
  26. }
  27. if *interactive {
  28. // 明确禁止混用两种输入来源,避免覆盖用户提供的参数。
  29. mixed := false
  30. flags.Visit(func(item *flag.Flag) {
  31. if item.Name == "name" || item.Name == "minutes" {
  32. mixed = true
  33. }
  34. })
  35. if mixed {
  36. return errors.New("交互模式不能同时指定 name 或 minutes")
  37. }
  38. scanner := bufio.NewScanner(input)
  39. if _, err := fmt.Fprint(output, "请输入姓名:"); err != nil {
  40. return err
  41. }
  42. if !scanner.Scan() {
  43. if err := scanner.Err(); err != nil {
  44. return err
  45. }
  46. return errors.New("没有读取到姓名")
  47. }
  48. *name = scanner.Text()
  49. if _, err := fmt.Fprint(output, "请输入学习分钟数:"); err != nil {
  50. return err
  51. }
  52. if !scanner.Scan() {
  53. if err := scanner.Err(); err != nil {
  54. return err
  55. }
  56. return errors.New("没有读取到分钟数")
  57. }
  58. value, err := strconv.Atoi(strings.TrimSpace(scanner.Text()))
  59. if err != nil {
  60. return fmt.Errorf("分钟数必须是整数:%w", err)
  61. }
  62. *minutes = value
  63. }
  64. cleanName := strings.TrimSpace(*name)
  65. if cleanName == "" {
  66. return errors.New("姓名不能为空,请指定 -name 或使用 -interactive")
  67. }
  68. if *minutes < 1 || *minutes > 1440 {
  69. return errors.New("学习分钟数必须在 1~1440 之间")
  70. }
  71. _, err := fmt.Fprintf(output, "学习者:%s,学习时长:%d 分钟\n", cleanName, *minutes)
  72. return err
  73. }
  74. func main() {
  75. // Args[0] 通常是程序名,Args[1:] 是用户提供的参数。
  76. if err := run(os.Args[1:], os.Stdin, os.Stdout); err != nil {
  77. if errors.Is(err, flag.ErrHelp) {
  78. return // -h 是查看帮助,不属于失败。
  79. }
  80. fmt.Fprintln(os.Stderr, "错误:", err)
  81. os.Exit(1)
  82. }
  83. }