Golang如何在Windows终端中覆盖任意前一行内容

Golang如何在Windows终端中覆盖任意前一行内容 大家好,我正在尝试在Windows终端中重写任何先前已打印的行。为了实现这一点,我考虑将光标移动到任何先前的行,然后在其上方重新打印。

package main
 
import (
    "bytes"
    "fmt"
    "os"
    "os/exec"
)
 
func main() {
     fmt.Println("line01")
     fmt.Println("line02")
     fmt.Println("line03")
     fmt.Println("line04")
     fmt.Println("line05")
     fmt.Println("line06")
     fmt.Println("line07")
     fmt.Println("line08")
    cmd := exec.Command("tput", "-S")
    cmd.Stdin = bytes.NewBufferString("clear\ncup 5 2")
    cmd.Stdout = os.Stdout
    cmd.Run()
    fmt.Println("Hello")
}

然而,看起来这段代码没有起到任何作用。有人知道如何实现吗? 这段代码的目标是:将光标移动到第6行、第3列,并显示单词“Hello”。


更多关于Golang如何在Windows终端中覆盖任意前一行内容的实战教程也可以访问 https://www.itying.com/category-94-b0.html

1 回复

更多关于Golang如何在Windows终端中覆盖任意前一行内容的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html


在Windows终端中覆盖任意前一行内容需要使用Windows控制台API或ANSI转义序列。你的代码使用了tput命令,这是Unix/Linux系统的工具,在Windows上通常不可用。

以下是两种在Windows终端中实现光标移动和内容覆盖的方法:

方法1:使用ANSI转义序列(推荐)

package main

import (
    "fmt"
    "os"
    "syscall"
    "unsafe"
)

var (
    kernel32 = syscall.NewLazyDLL("kernel32.dll")
    procGetStdHandle = kernel32.NewProc("GetStdHandle")
    procGetConsoleMode = kernel32.NewProc("GetConsoleMode")
    procSetConsoleMode = kernel32.NewProc("SetConsoleMode")
)

const (
    STD_OUTPUT_HANDLE = uintptr(4294967285) // -11
    ENABLE_VIRTUAL_TERMINAL_PROCESSING = 0x0004
)

func enableVTMode() bool {
    h, _, _ := procGetStdHandle.Call(STD_OUTPUT_HANDLE)
    
    var mode uint32
    procGetConsoleMode.Call(h, uintptr(unsafe.Pointer(&mode)))
    
    mode |= ENABLE_VIRTUAL_TERMINAL_PROCESSING
    ret, _, _ := procSetConsoleMode.Call(h, uintptr(mode))
    
    return ret != 0
}

func main() {
    // 启用ANSI转义序列支持
    if !enableVTMode() {
        fmt.Println("无法启用ANSI转义序列支持")
        os.Exit(1)
    }
    
    fmt.Println("line01")
    fmt.Println("line02")
    fmt.Println("line03")
    fmt.Println("line04")
    fmt.Println("line05")
    fmt.Println("line06")
    fmt.Println("line07")
    fmt.Println("line08")
    
    // 移动光标到第6行第3列(行和列都是从1开始计数)
    // \033[6;3H 移动光标到第6行第3列
    // \033[2K 清除当前行
    fmt.Print("\033[6;3H\033[2KHello")
    
    // 移动光标到最后,避免后续输出位置错乱
    fmt.Print("\033[9;1H")
}

方法2:使用Windows控制台API

package main

import (
    "fmt"
    "syscall"
    "unsafe"
)

var (
    kernel32 = syscall.NewLazyDLL("kernel32.dll")
    procGetStdHandle = kernel32.NewProc("GetStdHandle")
    procSetConsoleCursorPosition = kernel32.NewProc("SetConsoleCursorPosition")
)

type COORD struct {
    X int16
    Y int16
}

func main() {
    fmt.Println("line01")
    fmt.Println("line02")
    fmt.Println("line03")
    fmt.Println("line04")
    fmt.Println("line05")
    fmt.Println("line06")
    fmt.Println("line07")
    fmt.Println("line08")
    
    // 获取标准输出句柄
    h, _, _ := procGetStdHandle.Call(uintptr(4294967285)) // STD_OUTPUT_HANDLE
    
    // 设置光标位置(第6行第3列,注意行和列都是从0开始)
    var pos COORD
    pos.X = 2  // 第3列(0-based)
    pos.Y = 5  // 第6行(0-based)
    
    procSetConsoleCursorPosition.Call(h, uintptr(*(*int32)(unsafe.Pointer(&pos))))
    
    fmt.Print("Hello")
    
    // 移动光标到最后一行
    pos.X = 0
    pos.Y = 8
    procSetConsoleCursorPosition.Call(h, uintptr(*(*int32)(unsafe.Pointer(&pos))))
}

方法3:使用第三方库(简化实现)

package main

import (
    "fmt"
    "github.com/mattn/go-colorable"
)

func main() {
    // 使用支持ANSI的writer
    stdout := colorable.NewColorableStdout()
    
    fmt.Fprintln(stdout, "line01")
    fmt.Fprintln(stdout, "line02")
    fmt.Fprintln(stdout, "line03")
    fmt.Fprintln(stdout, "line04")
    fmt.Fprintln(stdout, "line05")
    fmt.Fprintln(stdout, "line06")
    fmt.Fprintln(stdout, "line07")
    fmt.Fprintln(stdout, "line08")
    
    // 使用ANSI转义序列移动光标
    // \033[6;3H 移动到第6行第3列
    fmt.Fprint(stdout, "\033[6;3HHello")
    
    // 移动光标到最后
    fmt.Fprint(stdout, "\033[9;1H")
}

要使用第三方库,需要先安装:

go get github.com/mattn/go-colorable

简单的ANSI转义序列示例

package main

import "fmt"

func main() {
    // 打印多行内容
    for i := 1; i <= 10; i++ {
        fmt.Printf("Line %02d: Initial content\n", i)
    }
    
    // 移动光标到第5行第10列并覆盖内容
    fmt.Print("\033[5;10H")      // 移动到第5行第10列
    fmt.Print("Overwritten!")    // 覆盖内容
    
    // 移动光标到第8行并清除整行后写入新内容
    fmt.Print("\033[8;1H")       // 移动到第8行第1列
    fmt.Print("\033[2K")         // 清除整行
    fmt.Print("Completely new line content")
    
    // 确保光标在最后
    fmt.Print("\033[11;1H")
}

ANSI转义序列说明:

  • \033[n;mH:移动光标到第n行第m列
  • \033[nA:光标上移n行
  • \033[nB:光标下移n行
  • \033[nC:光标右移n列
  • \033[nD:光标左移n列
  • \033[2K:清除当前行
  • \033[s:保存光标位置
  • \033[u:恢复光标位置

推荐使用方法1或方法3,它们使用ANSI转义序列,代码更简洁且跨平台兼容性更好(在支持ANSI的终端中都能工作)。

回到顶部