Как изменить строку в файле

Рейтинг: 0Ответов: 1Опубликовано: 01.05.2023

У меня есть файл с таким содержанием:

str1
str2
str3

Мне нужно полностью изменить одну из строк. Например вторую:

str1
adc
str3

Я знаю только как добавить строку в файл:

f, _ := os.OpenFile("FileName", os.O_APPEND|os.O_WRONLY|os.O_CREATE, 0600)
defer f.Close()
f.WriteString("\nString")

Ответы

▲ 0

Вот простая неоптимизированная программа

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)
    }
}

Эта программа читает весь файл, находит начало и конец искомой строки, затем стирает файл и выводит в него начало файла, заменённую строку, остальные строки файла.

В чём проблемы этой программа?

  1. Надёжно она будет работать только в линуксе, так как за конец строки принимается символ \n.
    • В Windows нужно искать пару \r\n, поэтому в Windows вывод, вообще говоря, будет некорректным - в заменённой строке будет отсутствовать символ \r. Да и вообще можно сконструировать такой файл, в котором символы \n будут встречаться посреди строки. Программа замены на них среагирует и заменит текст между \n, и совсем необязательно это будет целая строка.
  • в макоси эта программа может вообще не работать, так как там по умолчанию используется \r как конец строки.
  1. Для больших (ОЧЕНЬ больших) файлов чтение всего файла может завершиться фатально из-за того, что файл просто не влезет в память. Либо же может сильно замедлить работу системы, которой придётся выпихивать спящие процессы в своп, дабы освободить память для чтения файла.