.eg 1. 日志文件
从头开始读取文件时,我就能获得Line1的搜索位置。
func getSeekLocation() int64 {
start := int64(0)
input, err := os.Open(logFile)
if err != nil {
fmt.Println(err)
}
if _, err := input.Seek(start, io.SeekStart); err != nil {
fmt.Println(err)
}
scanner := bufio.NewScanner(input)
pos := start
scanLines := func(data []byte, atEOF bool) (advance int, token []byte,
err error) {
advance, token, err = bufio.ScanLines(data, atEOF)
pos += int64(advance)
return
}
scanner.Split(scanLines)
for scanner.Scan() {
if strings.Contains(scanner.Text(), "Line1") {
break
}
}
size, err := getFileSize()
if err != nil {
fmt.Println(err)
}
return size - pos
}
但这不是解决问题的有效方法,因为随着文件大小的增加,获取位置的时间也会增加。我想从EOF位置获取线的位置,我认为这样会更有效。
注意:
我优化并改进了以下解决方案,并将其作为库发布在这里:github.com/icza/backscanner
bufio.Scanner
使用a
io.Reader
作为其源,它不支持从任意位置进行查找和/或读取,因此它无法从头开始扫描线。bufio.Scanner
只有在读取了输入之前的所有数据之后,它才能读取输入的任何部分(也就是说,如果先读取所有文件内容,则只能读取文件的结尾)。
因此,我们需要定制的解决方案来实现这种功能。幸运的os.File
是,它在实现io.Seeker
和时都支持从任意位置读取io.ReaderAt
(它们中的任何一个都足以满足我们的需要)。
让我们构造一个从最后一行开始
向后Scanner
扫描行的。为此,我们将使用。下面的实现使用一个内部缓冲区,从输入的末尾开始,按块将数据读取到该缓冲区中。输入的大小也必须传递(这基本上是我们要从其开始读取的位置,不一定必须是结束位置)。
__io.ReaderAt
type Scanner struct {
r io.ReaderAt
pos int
err error
buf []byte
}
func NewScanner(r io.ReaderAt, pos int) *Scanner {
return &Scanner{r: r, pos: pos}
}
func (s *Scanner) readMore() {
if s.pos == 0 {
s.err = io.EOF
return
}
size := 1024
if size > s.pos {
size = s.pos
}
s.pos -= size
buf2 := make([]byte, size, size+len(s.buf))
// ReadAt attempts to read full buff!
_, s.err = s.r.ReadAt(buf2, int64(s.pos))
if s.err == nil {
s.buf = append(buf2, s.buf...)
}
}
func (s *Scanner) Line() (line string, start int, err error) {
if s.err != nil {
return "", 0, s.err
}
for {
lineStart := bytes.LastIndexByte(s.buf, '\n')
if lineStart >= 0 {
// We have a complete line:
var line string
line, s.buf = string(dropCR(s.buf[lineStart+1:])), s.buf[:lineStart]
return line, s.pos + lineStart + 1, nil
}
// Need more data:
s.readMore()
if s.err != nil {
if s.err == io.EOF {
if len(s.buf) > 0 {
return string(dropCR(s.buf)), 0, nil
}
}
return "", 0, s.err
}
}
}
// dropCR drops a terminal \r from the data.
func dropCR(data []byte) []byte {
if len(data) > 0 && data[len(data)-1] == '\r' {
return data[0 : len(data)-1]
}
return data
}
使用它的示例:
func main() {
scanner := NewScanner(strings.NewReader(src), len(src))
for {
line, pos, err := scanner.Line()
if err != nil {
fmt.Println("Error:", err)
break
}
fmt.Printf("Line start: %2d, line: %s\n", pos, line)
}
}
const src = `Start
Line1
Line2
Line3
End`
输出(在Go Playground上尝试):
Line start: 24, line: End
Line start: 18, line: Line3
Line start: 12, line: Line2
Line start: 6, line: Line1
Line start: 0, line: Start
Error: EOF
笔记:
Scanner
没有限制最大行长,它可以处理所有行。Scanner
处理行尾\n
和\r\n
行尾(由dropCR()
函数确保)。Scanner
没有重用缓冲区,总是在需要时创建新的缓冲区。(预)分配2个缓冲区并明智地使用它们就足够了。实现将变得更加复杂,并且将引入最大行长度限制。要将其Scanner
与文件一起使用,您可以使用os.Open()
打开文件。注意*File
实现io.ReaderAt()
。然后,您可以File.Stat()
用来获取有关文件(os.FileInfo
)的信息,包括文件的大小(长度):
f, err := os.Open("a.txt")
if err != nil {
panic(err)
}
fi, err := f.Stat()
if err != nil {
panic(err)
}
defer f.Close()
scanner := NewScanner(f, int(fi.Size()))
如果要在一行中查找子字符串,则只需使用上面的代码即可Scanner
返回每行的起始位置,并从末尾读取行。
您可以使用来检查每行中的子字符串strings.Index()
,它返回该行内的子字符串位置,如果找到,则将行起始位置添加到该位置。
假设我们正在寻找"ine2"
子字符串(这是该"Line2"
行的一部分)。这是您可以执行的操作:
scanner := NewScanner(strings.NewReader(src), len(src))
what := "ine2"
for {
line, pos, err := scanner.Line()
if err != nil {
fmt.Println("Error:", err)
break
}
fmt.Printf("Line start: %2d, line: %s\n", pos, line)
if i := strings.Index(line, what); i >= 0 {
fmt.Printf("Found %q at line position: %d, global position: %d\n",
what, i, pos+i)
break
}
}
输出(在Go Playground上尝试):
Line start: 24, line: End
Line start: 18, line: Line3
Line start: 12, line: Line2
Found "ine2" at line position: 1, global position: 13
问题内容: 嘿,我正在尝试打开文件,仅从偏移量读取一定长度!我阅读了以下主题: 如何使用Java中的文件中的特定行号读取特定行? 在那儿,它说在不读取之前就不可能读取某行,但是我想知道字节! 是否可以从已知偏移量读取某些字节? 问题答案: RandomAccessFile提供一个功能:
问题内容: 我需要执行以下操作以确保我的应用服务器是 尾随日志文件中的特定字符串 保持阻塞,直到打印出该字符串 但是,如果在大约20分钟内未打印字符串,则退出并抛出异常消息,例如“服务器花费了20分钟以上的时间” 如果在日志文件中打印了字符串,请退出循环并继续。 有没有办法在while循环中包括超时? 问题答案: 这将读取写入日志文件的所有内容,搜索some_word,如果一切正常,则回显ok,否
当我将XML文件中的特定字符读取到PHP文件时,我遇到了一个问题。 我使用像“ä”、“ü”和“ö”这样的字符。我得到以下错误: simplexml_load_string()[function.simplexml-load-String]:实体:第96行:解析器错误:输入不正确的UTF-8,指示编码!字节:0xFC 0x73 0x65 0x0C
问题内容: 我有一个字符串,我想仅使用Java从字符串的结尾删除行尾字符 我想成为 (此问题类似于但与问题593671不同) 问题答案: 您可以使用。这修剪和字符在字符串的结尾 正则表达式的解释如下: 是包含和的字符类 是一个或多个重复 是弦末锚 参考文献 regular-expressions.info/Anchors,角色类,重复 相关话题 您还可以使用修剪字符串 开头 和 结尾 的 所有 空
问题内容: 我们如何从文本文件读取数据并将其存储在String变量中? 是否有可能在方法中传递文件名,并且它将返回字符串,即文件中的文本。 我必须导入哪种工具?声明列表会很棒。 问题答案: 这些是必需的进口: 这是一种方法,通过将文件名作为参数传递给它,您可以从文件中进行读取,如下所示:
本文向大家介绍Oracle截取字符串去掉字段末尾指定长度的字符,包括了Oracle截取字符串去掉字段末尾指定长度的字符的使用技巧和注意事项,需要的朋友参考一下 lengthb(string)计算string所占的字节长度:返回字符串的长度,单位是字节 length(string)计算string所占的字符长度:返回字符串的长度,单位是字符 eg: 【备注】一个汉字在Oracle数据库里占多少字节跟