Вот простая неоптимизированная программа
package main
import (
"fmt"
"io"
"log"
"os"
"path/filepath"
"strconv"
)
// Call: ./replace file lineno newtext
// example: replace the first line
// ./replace some.txt 1 "The first line"
func main() {
args := os.Args
if len(args) != 4 {
log.Fatalf("Invalid arguments\n%s FILE LINE-NUMBER LINE-TEXT\n", filepath.Base(args[0]))
}
fname, linenoStr, lineTxt := args[1], args[2], args[3]
lineno, err := strconv.Atoi(linenoStr)
checkErrorf(err, "invalid line number: %s", linenoStr)
fileIn, err := os.Open(fname)
checkErrorf(err, "Failed to open file %s", fname)
fullText, err := io.ReadAll(fileIn)
fileIn.Close()
checkErrorf(err, "Failed to read file %s", fname)
found := false
var start, end int
var char byte
cnt := 0
for end, char = range fullText {
if char == '\n' {
cnt++
if cnt == lineno {
found = true
break
}
start = end + 1
}
}
if cnt == lineno-1 {
// replace last line
found = true
}
if !found {
log.Fatalln("line number is too big: wanted line #", lineno, ", got ", cnt, " lines")
}
info, err := os.Stat(fname)
checkErrorf(err, "failed to get stats of %s", fname)
fileOut, err := os.OpenFile(fname, os.O_TRUNC|os.O_WRONLY, info.Mode().Perm())
checkErrorf(err, "failed to open for writing %s", fname)
defer fileOut.Close()
_, err = fileOut.Write(fullText[:start])
checkErrorf(err, "failed to write to %s", fname)
_, err = fileOut.WriteString(lineTxt)
checkErrorf(err, "failed to write to %s", fname)
_, err = fileOut.Write(fullText[end:])
checkErrorf(err, "failed to write to %s", fname)
}
func checkErrorf(err error, format string, args ...any) {
if err != nil {
log.Fatalln(fmt.Sprintf(format, args...), ":", err)
}
}
Эта программа читает весь файл, находит начало и конец искомой строки, затем стирает файл и выводит в него начало файла, заменённую строку, остальные строки файла.
В чём проблемы этой программа?
- Надёжно она будет работать только в линуксе, так как за конец строки принимается символ
\n
.
- В Windows нужно искать пару
\r\n
, поэтому в Windows вывод, вообще говоря, будет некорректным - в заменённой строке будет отсутствовать символ \r
. Да и вообще можно сконструировать такой файл, в котором символы \n
будут встречаться посреди строки. Программа замены на них среагирует и заменит текст между \n
, и совсем необязательно это будет целая строка.
- в макоси эта программа может вообще не работать, так как там по умолчанию используется
\r
как конец строки.
- Для больших (ОЧЕНЬ больших) файлов чтение всего файла может завершиться фатально из-за того, что файл просто не влезет в память. Либо же может сильно замедлить работу системы, которой придётся выпихивать спящие процессы в своп, дабы освободить память для чтения файла.