197 lines
6.8 KiB
Go
197 lines
6.8 KiB
Go
package main
|
|
|
|
// input-scan-cheatsheet.go
|
|
// ACM/笔试 输入解析速查表(Go)。所有模板均经本机 go1.26.3 实测通过。
|
|
// 来源:掘金 ACM 模板(starine, 2022) + CSDN fmt/bufio 对比 + 本机实测。
|
|
//
|
|
// 本文件与 main.go 同属 package main,但所有模板均为独立函数,
|
|
// 不与 main.go 的 main() 冲突。直接在 main.go 里调用任一 demoX() 即可验证。
|
|
//
|
|
// ───────────────────────── 场景索引 ─────────────────────────
|
|
// demo1 EOF 多组,每组两个数求和 fmt.Scan
|
|
// demo2 先读组数 t,再 t 组每组两个数 fmt.Scan
|
|
// demo3 特殊值结束(0 0 停) fmt.Scan
|
|
// demo4 先读 n 再读 n 个元素(行布局无关) fmt.Scan ★最省心
|
|
// demo5 逐行读 + 行内不定个数求和 bufio.Scanner
|
|
// demo6 先 n 再 n 个元素(Scanner 鲁棒版) bufio.Scanner
|
|
// demo7 t 组,每组先 n 再 n 个数 bufio.Scanner
|
|
// demo8 大数据量:Scanner 读 + Writer 输出 bufio 双向缓冲
|
|
// ─────────────────────────────────────────────────────────────
|
|
|
|
import (
|
|
"bufio"
|
|
"fmt"
|
|
"os"
|
|
"strconv"
|
|
"strings"
|
|
)
|
|
|
|
// ──────────────────────── fmt.Scan 系列 ────────────────────────
|
|
|
|
// demo1: 多组数据,每组一行两个整数,读到 EOF 结束。
|
|
// 输入示例: "1 2\n3 4\n5 7\n" → 3 7 12
|
|
// 关键:fmt.Scan 把所有空白符(空格/换行/tab)当分隔符,无视行边界。
|
|
// n 是成功填入的变量个数;n==0 表示 EOF。
|
|
func demo1() {
|
|
var a, b int
|
|
for {
|
|
n, _ := fmt.Scan(&a, &b)
|
|
if n == 0 {
|
|
break
|
|
}
|
|
fmt.Println(a + b)
|
|
}
|
|
}
|
|
|
|
// demo2: 第一行组数 t,接下来 t 行每行两个整数。
|
|
// 输入示例: "2\n1 2\n3 4\n" → 3 7
|
|
func demo2() {
|
|
var t, a, b int
|
|
fmt.Scan(&t)
|
|
for i := 0; i < t; i++ {
|
|
fmt.Scan(&a, &b)
|
|
fmt.Println(a + b)
|
|
}
|
|
}
|
|
|
|
// demo3: 多组数据,读到 0 0 结束(特殊哨兵)。
|
|
// 输入示例: "1 2\n3 4\n0 0\n" → 3 7
|
|
// 关键:判停条件由题目给定,与 EOF 无关。仍用 fmt.Scan 读取。
|
|
func demo3() {
|
|
var a, b int
|
|
for {
|
|
fmt.Scan(&a, &b)
|
|
if a == 0 && b == 0 {
|
|
break
|
|
}
|
|
fmt.Println(a + b)
|
|
}
|
|
}
|
|
|
|
// demo4 ★最省心模板:先读 n,再读 n 个元素。行布局无关(挤一行/每元素一行/乱排都行)。
|
|
// 输入1 "5 1 2 3 4 5" 输入2 "5\n1 2 3 4 5" 输入3 "5\n1\n2\n3\n4\n5" 全部 → [1 2 3 4 5]
|
|
// 关键:fmt.Scan 无视行边界,只按空白切分。naive Scanner 写法在"n 与元素挤一行"时会崩,见 demo6 对比。
|
|
func demo4() {
|
|
var n int
|
|
fmt.Scan(&n)
|
|
a := make([]int, n)
|
|
for i := 0; i < n; i++ {
|
|
fmt.Scan(&a[i])
|
|
}
|
|
fmt.Println(a)
|
|
}
|
|
|
|
// ──────────────────────── bufio.Scanner 系列 ────────────────────────
|
|
|
|
// newScanner 返回带 1MB 缓冲的 Scanner,避免单行过长触发默认 64KB 上限。
|
|
func newScanner() *bufio.Scanner {
|
|
sc := bufio.NewScanner(os.Stdin)
|
|
sc.Buffer(make([]byte, 1024*1024), 1024*1024)
|
|
return sc
|
|
}
|
|
|
|
// demo5: 多组数据,每行不定个数整数求和。逐行读 + strings.Fields 切分。
|
|
// 输入示例: "1 2 3\n4 5\n6 7 8 9\n" → 6 9 30
|
|
// 关键:strings.Fields 按任意空白符切,自动跳过连续空白,不会产生空元素。
|
|
// 优先用 Fields 而非 strings.Split(s, " ")——后者遇连续空格会产生空字符串。
|
|
func demo5() {
|
|
sc := newScanner()
|
|
for sc.Scan() {
|
|
line := sc.Text()
|
|
if line == "" {
|
|
continue
|
|
}
|
|
var sum int
|
|
for _, s := range strings.Fields(line) {
|
|
v, _ := strconv.Atoi(s)
|
|
sum += v
|
|
}
|
|
fmt.Println(sum)
|
|
}
|
|
}
|
|
|
|
// demo6: 先读 n 再读 n 个元素(Scanner 鲁棒版)。兼容"n 与元素挤一行"的情况。
|
|
// 关键:naive 写法(sc.Scan 一行当 n 去解析)在 "5 1 2 3 4 5" 上会 Atoi 失败返回 0。
|
|
// 鲁棒做法:把第一行用 Fields 切开,第一个 token 是 n,余下的直接进数组,不够再读后续行。
|
|
func demo6() {
|
|
sc := newScanner()
|
|
sc.Scan()
|
|
tokens := strings.Fields(sc.Text())
|
|
n, _ := strconv.Atoi(tokens[0])
|
|
a := make([]int, 0, n)
|
|
for _, s := range tokens[1:] {
|
|
v, _ := strconv.Atoi(s)
|
|
a = append(a, v)
|
|
}
|
|
for len(a) < n && sc.Scan() {
|
|
for _, s := range strings.Fields(sc.Text()) {
|
|
v, _ := strconv.Atoi(s)
|
|
a = append(a, v)
|
|
}
|
|
}
|
|
fmt.Println(a)
|
|
}
|
|
|
|
// demo7: t 组,每组先读 n 再读 n 个整数求和。
|
|
// 输入示例: "2\n3 1 2 3\n2 4 5\n" → 6 9
|
|
// 组合模式:外层组数用 Scan,内层元素用 Scanner 按行 + Fields 收集。
|
|
func demo7() {
|
|
sc := newScanner()
|
|
sc.Scan()
|
|
t, _ := strconv.Atoi(strings.TrimSpace(sc.Text()))
|
|
for i := 0; i < t; i++ {
|
|
sc.Scan()
|
|
tokens := strings.Fields(sc.Text())
|
|
n, _ := strconv.Atoi(tokens[0])
|
|
a := make([]int, 0, n)
|
|
for _, s := range tokens[1:] {
|
|
v, _ := strconv.Atoi(s)
|
|
a = append(a, v)
|
|
}
|
|
for len(a) < n && sc.Scan() {
|
|
for _, s := range strings.Fields(sc.Text()) {
|
|
v, _ := strconv.Atoi(s)
|
|
a = append(a, v)
|
|
}
|
|
}
|
|
sum := 0
|
|
for _, v := range a {
|
|
sum += v
|
|
}
|
|
fmt.Println(sum)
|
|
}
|
|
}
|
|
|
|
// ──────────────────────── 大数据量模板 ────────────────────────
|
|
|
|
// demo8: 大数据量(>10^5 行)。Scanner 读 + Writer 输出,双向缓冲。
|
|
// 输入示例: 大量行,每行一个整数,输出每个的 2 倍。
|
|
// 关键:fmt.Print 系列在 10^5 级输出时会明显变慢;bufio.NewWriter + Flush 提速一个量级。
|
|
// defer w.Flush() 必须有,否则缓冲区内容不会写出。
|
|
func demo8() {
|
|
sc := newScanner()
|
|
w := bufio.NewWriter(os.Stdout)
|
|
defer w.Flush()
|
|
for sc.Scan() {
|
|
line := sc.Text()
|
|
if line == "" {
|
|
continue
|
|
}
|
|
v, _ := strconv.Atoi(strings.TrimSpace(line))
|
|
fmt.Fprintln(w, v*2)
|
|
}
|
|
}
|
|
|
|
// ──────────────────────── 坑速查 ────────────────────────
|
|
// 1. fmt.Scan 读不全时,未填的变量保留上轮值(不会清零)。
|
|
// → 判停用 n < 预期个数,别信任未读到的变量。
|
|
// 2. naive Scanner(整行 Atoi 当 n)在"n 与元素挤一行"时返回 n=0。
|
|
// → 用 strings.Fields 切开第一行(见 demo6)。
|
|
// 3. strings.Split(s, " ") 遇连续空格产生空字符串元素。
|
|
// → 优先 strings.Fields(s)。
|
|
// 4. bufio.Scanner 默认 64KB 上限,超长行会读不全。
|
|
// → sc.Buffer(make([]byte, 1024*1024), 1024*1024)。
|
|
// 5. bufio.NewWriter 必须 Flush,否则输出丢失。
|
|
// → defer w.Flush()。
|
|
// 6. fmt.Scanln 遇换行即停,不跨行;fmt.Scan 无视行边界跨行连读。
|
|
// → ACM 默认用 fmt.Scan。 |