在Go中,可以通过简单的方式从标准输入读取一行输入,这也符合以下要求吗?
<form name="info_form" action="https://app.getresponse.com/add_subscriber.html" accept-charset="utf-8" method="post">
<input placeholder="Enter Your Name..." type="text" name="name" id="awf_field" class="elInputIEmail" data-type="extra"/>
<input placeholder="Enter Your Email..." type="text" name="email" id="awf_field" class="elInputIEmail" data-type="extra"/>
<!-- Campaign token -->
<!-- Get the token at: https://app.getresponse.com/campaign_list.html -->
<input type="hidden" name="campaign_token" value="18oKuC" />
<input type="hidden" name="thankyou_url" value="https://www.example.com/watch-video/"/>
<!-- Forward form data to your page (optional) -->
<input type="hidden" name="forward_data" value="" />
<!-- Add subscriber to the follow-up sequence with a specified day (optional) -->
<input type="hidden" name="start_day" value="0" />
<!-- Subscriber button -->
<input name="submit" id="af-submit-image-416906520" type="image" class="image" style="background: none; max-width: 100%;" alt="Submit Form" src="http://www.example.com/Images/button-400.jpg" />
</form>
)。我想修改现有的大型Go应用程序,该应用程序每次向用户询问输入行时都会创建一个<form name="info_form" action="https://app.getresponse.com/add_subscriber.html" accept-charset="utf-8" method="post" type="hidden">
实例。当标准输入来自终端时,多个实例正常工作,但是当标准输入来自另一个进程时,https://app.getresponse.com/add_subscriber.html
的调用仅在bufio.Scanner
的第一个实例上成功。来自所有其他实例的调用失败。
这里有一些演示问题的玩具代码:
bufio.Scanner
我将其构建为Scan
并以交互方式从bash shell运行:
bufio.Scanner
但如果我在文本中输入,第二个package main
import (
"bufio"
"fmt"
"os"
)
func main() {
// read with 1st scanner -> works for both piped stdin and terminal
scanner1 := readStdinLine(1)
// read with 2nd scanner -> fails for piped stdin, works for terminal
readStdinLine(2)
// read with 1st scanner -> prints line 2 for piped stdin, line 3 for terminal
readLine(scanner1, 3)
}
func readStdinLine(lineNum int64) (scanner *bufio.Scanner) {
scanner = readLine(bufio.NewScanner(os.Stdin), lineNum)
return
}
func readLine(scannerIn *bufio.Scanner, lineNum int64) (scanner *bufio.Scanner) {
scanner = scannerIn
scanned := scanner.Scan()
fmt.Printf("%d: ", lineNum)
if scanned {
fmt.Printf("Text=%s\n", scanner.Text())
return
}
if scanErr := scanner.Err(); scanErr != nil {
fmt.Printf("Error=%s\n", scanErr)
return
}
fmt.Println("EOF")
return
}
会失败:
print_stdin
答案 0 :(得分:1)
您的顺序是:
当你在管道中执行echo时,只存在一个正在读/写的stdin / stdout文件,但你试图使用两个。
UPDATE: echo的执行流程为:
按ENTER键可以看到这种情况。参数整体被发送到echo程序而不是行。
echo实用程序将其参数写入标准输出,然后是 一个 。如果没有参数,则只写入。
更多信息:http://pubs.opengroup.org/onlinepubs/9699919799/utilities/echo.html。
参见source code echo工作原理:
while (argc > 0)
{
fputs (argv[0], stdout);//<-- send args to the same stdout
argc--;
argv++;
if (argc > 0)
putchar (' ');
}
因此,您的代码可以正常使用:
$ (n=1; while sleep 1; do echo a$n; n=$((n+1)); done) | ./print_stdin
$ 1: Text=a1
$ 2: Text=a2
$ 3: Text=a3
如果你需要在不同的stdout中重复args,请使用&#34; yes&#34;计划或替代方案。 是程序在stdout中重复写入的args。更多内容: https://git.savannah.gnu.org/cgit/coreutils.git/tree/src/yes.c
示例:
$ yes a | ./print_stdin
$ 1: Text=a
$ 2: Text=a
$ 3: Text=a
答案 1 :(得分:0)
comment ThunderCat中的建议有效。
缓冲读取的替代方法是一次读取一个字节。读取单个字节,直到找到\ n或某个终结符,并将数据返回到该点。
这是我的实施,受到Scanner.Scan的启发:
package lineio
import (
"errors"
"io"
)
const startBufSize = 4 * 1024
const maxBufSize = 64 * 1024
const maxConsecutiveEmptyReads = 100
var ErrTooLong = errors.New("lineio: line too long")
func ReadLine(r io.Reader) (string, error) {
lb := &lineBuf {r:r, buf: make([]byte, startBufSize)}
for {
lb.ReadByte()
if lb.err != nil || lb.TrimCrlf() {
return lb.GetResult()
}
}
}
type lineBuf struct {
r io.Reader
buf []byte
end int
err error
}
func (lb *lineBuf) ReadByte() {
if lb.EnsureBufSpace(); lb.err != nil {
return
}
for empties := 0; ; {
n := 0
if n, lb.err = lb.r.Read(lb.buf[lb.end:lb.end+1]); lb.err != nil {
return
}
if n > 0 {
lb.end++
return
}
empties++
if empties > maxConsecutiveEmptyReads {
lb.err = io.ErrNoProgress
return
}
}
}
func (lb *lineBuf) TrimCrlf() bool {
if !lb.EndsLf() {
return false
}
lb.end--
if lb.end > 0 && lb.buf[lb.end-1] == '\r' {
lb.end--
}
return true
}
func (lb *lineBuf) GetResult() (string, error) {
if lb.err != nil && lb.err != io.EOF {
return "", lb.err
}
return string(lb.buf[0:lb.end]), nil
}
func (lb *lineBuf) EndsLf() bool {
return lb.err == nil && lb.end > 0 && (lb.buf[lb.end-1] == '\n')
}
func (lb *lineBuf) EnsureBufSpace() {
if lb.end < len(lb.buf) {
return
}
newSize := len(lb.buf) * 2
if newSize > maxBufSize {
lb.err = ErrTooLong
return
}
newBuf := make([]byte, newSize)
copy(newBuf, lb.buf[0:lb.end])
lb.buf = newBuf
return
}
使用go install
编译的lineio和使用go build -o read_each_byte
的main(见下文)。
经过测试的脚本输入:
$ seq 12 22 78 | ./read_each_byte
1: Text: "12"
2: Text: "34"
3: Text: "56"
来自交互式终端的测试输入:
$ ./read_each_byte
abc
1: Text: "abc"
123
2: Text: "123"
x\y"z
3: Text: "x\\y\"z"
这里的主要内容:
package main
import (
"fmt"
"lineio"
"os"
)
func main() {
for i := 1; i <= 3; i++ {
text, _ := lineio.ReadLine(os.Stdin)
fmt.Printf("%d: Text: %q\n", i, text)
}
}