2016-09-19 5 views
0

Я начинаю изучать Go, и я немного озадачен тем фактом, что он включает EOF при использовании функции ioutil.ReadFile. Я хочу, например, прочитать файл и проанализировать все его строки в разделителе полей.Предотвращение ReadFile или ReadAll от чтения EOF

Пример входного файла:

CZG;KCZG;some text 
EKY;KEKY;some text 
A50;KA50;some text 
UKY;UCFL;some text 
MIC;KMIC;some text 
K2M;K23M;some text 

Это то, что я делаю, чтобы прочитать и разобрать этот файл:

import(
    "fmt" 
    "log" 
    "io/ioutil" 
    "strings" 
    ) 

func main() { 
    /* Read file */ 
    airportsFile := "/path/to/file/ad_iata" 
    content, err := ioutil.ReadFile(airportsFile) 
    if err != nil { 
     log.Fatal(err) 
    } 

    /* split content on EOL */ 
    lines := strings.Split(string(content), "\n") 

    /* split line on field separator ; */ 
    for _, line := range lines { 
     lineSplit := strings.Split(line, ";") 
     fmt.Println(lineSplit) 
    } 
} 

string.Split функция добавляет пустой элемент в конце среза lineSplit, когда его видит EOF (ничего не анализировать). Поэтому, если я хочу получить второй индекс этого фрагмента (lineSplit[1]), я столкнулся с panic: runtime error: index out of range. Я должен ограничить диапазон, делая это

/* split line on field separator ; */ 
lenLines := len(lines) -1 
for _, line := range lines[:lenLines] { 
    lineSplit := strings.Split(line, ";") 
    fmt.Println(lineSplit[1]) 
} 

Есть ли лучший способ, если я хочу сохранить с помощью ReadFile для его краткости?

Та же проблема возникает при использовании ioutil.ReadAll

ответ

4

Там нет такого понятия, как «EOF байт» или «EOF характера». То, что вы видите, вероятно, вызвано символом прерывания строки ('\n') в самом конце файла.

Чтобы прочитать файл построчно, это более идиоматических использовать bufio.Scanner вместо:

file, err := os.Open(airportsFile) 
if err != nil { 
    log.Fatal(err) 
} 
defer file.Close() 

scanner := bufio.NewScanner(file) 
for scanner.Scan() { 
    line := scanner.Text() 
    // ... use line as you please ... 
} 

if err := scanner.Err(); err != nil { 
    log.Fatal(err) 
} 

И это на самом деле решает проблему, потому что Scanner будет читать окончательный перевод строки без запуска новой линии, о чем свидетельствует this playground example.

+0

В конце файла нет конца '\ n'. Ваше решение с 'bufio.Scanner' отлично работает, но я просто задался вопросом, есть ли возможность запретить ioutil.ReadFile добавлять пустой срез, когда он видит EOF. Если его нет, я буду использовать bufio. Благодарю. – ripat

+0

Как вы подтвердили, что нет конечной новой строки? Некоторые редакторы фактически не показывают их как пустую строку, но добавляют их молча в любом случае. Попробуйте 'od -tc/path/to/file/ad_iata' и посмотрите. – Thomas

+0

Я стою исправлены. В конце последней строки есть конечный '\ n'.Это делает поведение «ioutil.ReadFile» более логичным для меня. Я узнал что-то от вас. Еще раз спасибо. – ripat

1

Вы можете использовать scanner.Err(), чтобы проверить наличие ошибок при чтении файла.

// Err returns the first non-EOF error that was encountered by the Scanner. 
func (s *Scanner) Err() error { 
    if s.err == io.EOF { 
     return nil 
    } 
    return s.err 
} 

В целом в идти идиоматический способ чтения и разбор файла является использование bufio.NewScanner которые принимают в качестве входного параметра файл для чтения и возвращает новый Scanner.

Учитывая приведенные выше замечания здесь, как вы можете читать и анализировать файл:

package main 

import (
    "bufio" 
    "fmt" 
    "os" 
) 

func main() { 
    input, err := os.Open("example.txt") 

    if err != nil { 
     panic("Error happend during opening the file. Please check if file exists!") 
     os.Exit(1) 
    } 

    defer input.Close() 

    scanner := bufio.NewScanner(input) 
    for scanner.Scan() { 
     line := scanner.Text() 
     fmt.Printf("%v\n", line) 
    } 
    if err := scanner.Err(); err != nil { 
     fmt.Fprintln(os.Stderr, "reading input:", err) 
    }  
} 
2

Ваш входной файл seeems быть CSV файл, так что вы можете использовать encoding/csv

airportsFile := "/path/to/file/ad_iata" 
content, err := os.Open(airportsFile) 
    if err != nil { 
     log.Fatal(err) 
    } 
r := csv.NewReader(content) 
r.Comma = ';' 
records, err := r.ReadAll() /* split line on field separator ; */ 
if err != nil { 
    log.Fatal(err) 
} 
fmt.Println(records) 

который выглядит достаточно кратковременно для меня и обеспечить правильный вывод

[[CZG KCZG какой-то текст] [EKY KEKY какой-то текст] [A50 KA50 какой-нибудь текст] [UKY UCFL какой-нибудь текст] [MIC KMIC какой-нибудь текст] [K2 M K23M некоторый текст]]

Смежные вопросы