u
This commit is contained in:
@@ -0,0 +1,197 @@
|
||||
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。
|
||||
Reference in New Issue
Block a user