Безопасное прерывание чтения и записи csv

#csv #go

#csv #Вперед

Вопрос:

Я обрабатываю файлы csv, и когда я прерываю процесс, я хочу сохранить необработанные данные в другой файл.

Это то, что я сделал

 csvFile, err := os.Open(csvPath)
r := csv.NewReader(csvFile)

sigc := make(chan os.Signal, 1)
signal.Notify(sigc,
    syscall.SIGHUP,
    syscall.SIGINT,
    syscall.SIGTERM,
    syscall.SIGQUIT)
go func() {
    <-sigc

    savePending(r)
}()

for {
    record, err := r.Read()
    if err == io.EOF {
        break
    }

    if err != nil {
        log.Println(record, err)
        continue
    }

    
    doSomethingWithRecord(record)
}
  

Функция сохранения

 func savePending(r *csv.Reader)  {

    pendingFileName := fmt.Sprintf("%s_pending.csv", fileBaseName)
    csvPendingPath := path.Join(dirname, pendingFileName)
    pendingFile, err := os.Create(csvPendingPath)
    if err != nil {
        log.Fatalln("Couldn't open the csv file", csvPendingPath, err)
    }
    defer pendingFile.Close()
    pendR := csv.NewWriter(pendingFile)

    records, err := r.ReadAll()
    if err == io.EOF {
        log.Println("no pending records")
    }
    err = pendR.WriteAll(records)
    if err != nil {
        log.Println("error writing pending file")
    }
}
  

Но когда я запускаю код, а затем прерываю скрипт, нажимая CTRL C, у меня всегда возникает паника

 panic: runtime error: slice bounds out of range [:7887] with capacity 4096

goroutine 82 [running]:
bufio.(*Reader).ReadSlice(0xc0000c2ea0, 0x105930a, 0x88, 0x90, 0xc00090cab0, 0x0, 0x0)
        /usr/local/Cellar/go/1.13.3/libexec/src/bufio/bufio.go:334  0x232
encoding/csv.(*Reader).readLine(0xc00015c1b0, 0x9, 0x9, 0xc00090cab0, 0xc00090f680, 0x20e)
        /usr/local/Cellar/go/1.13.3/libexec/src/encoding/csv/reader.go:218  0x49
encoding/csv.(*Reader).readRecord(0xc00015c1b0, 0x0, 0x0, 0x0, 0xc00090cab0, 0x9, 0x9, 0x0, 0x0)
        /usr/local/Cellar/go/1.13.3/libexec/src/encoding/csv/reader.go:266  0x115
encoding/csv.(*Reader).ReadAll(0xc00015c1b0, 0xc0005af2c0, 0x1000, 0xc0006fc000, 0xc0001da608, 0x0)
        /usr/local/Cellar/go/1.13.3/libexec/src/encoding/csv/reader.go:202  0x74
main.savePending(0xc00015c1b0, 0x0, 0x0, 0x0)
  

В чем может быть проблема?

Комментарии:

1. Вы используете csv.Reader type одновременно (из основного потока и подпрограммы). Являются ли его методы потокобезопасными?

2. ну правильно, думаю, его не сохранить

Ответ №1:

Во время запуска savePending функции основная подпрограмма продолжает считывать данные с устройства чтения.

Как насчет прерывания цикла for <-sigc и сохранения остальных:

 csvFile, err := os.Open(csvPath)
r := csv.NewReader(csvFile)

sigc := make(chan os.Signal, 1)
signal.Notify(sigc,
    syscall.SIGHUP,
    syscall.SIGINT,
    syscall.SIGTERM,
    syscall.SIGQUIT)

for {
    select {
    case <-sigc:
        savePending(r)
        return
    default:
    }


    record, err := r.Read()
    if err == io.EOF {
        break
    }

    if err != nil {
        log.Println(record, err)
        continue
    }

    
    doSomethingWithRecord(record)
}
  

Комментарии:

1. смотрите также github.com/golang/go/issues/20280 и github.com/jbenet/go-context/blob/master/io/ctxio.go