गो में लाइन द्वारा एक फ़ाइल लाइन पढ़ना


334

मुझे file.ReadLineGo में फंक्शन नहीं मिल रहा है । मैं यह पता लगा सकता हूं कि किसी को कैसे लिखना है, लेकिन मैं सोच रहा हूं कि क्या मैं यहां कुछ देख रहा हूं। कोई एक फ़ाइल लाइन लाइन द्वारा कैसे पढ़ता है?


7
Go1.1 के रूप में, bufio.Scanner ऐसा करने का सबसे अच्छा तरीका है।
मैल्कम

जवाबों:


133

नोट: स्वीकृत उत्तर गो के शुरुआती संस्करणों में सही था। उच्चतम मत वाले उत्तर को देखें इसे प्राप्त करने के लिए अधिक हालिया मुहावरेदार तरीका है।

पैकेज में फ़ंक्शन रीडलाइन है bufio

कृपया ध्यान दें कि यदि रेखा रीड बफर में फिट नहीं होती है, तो फ़ंक्शन एक अधूरी रेखा वापस कर देगा। यदि आप किसी प्रोग्राम में हमेशा एक कॉल के द्वारा अपने प्रोग्राम में एक पूरी लाइन पढ़ना चाहते हैं, तो आपको ReadLineफंक्शन को अपने फंक्शन में एनकोड करना होगा, जो ReadLineफॉर-लूप में कॉल करता है।

bufio.ReadString('\n')पूरी तरह से बराबर नहीं है ReadLineक्योंकि ReadStringमामले को संभालने में असमर्थ है जब एक फ़ाइल की अंतिम पंक्ति न्यूलाइन वर्ण के साथ समाप्त नहीं होती है।


37
डॉक्स से: "रीडलाइन एक निम्न-स्तरीय लाइन-रीडिंग प्राइमिटिव है। अधिकांश कॉलर्स को रीडबाइट्स ('\ n') या रीडस्ट्रिंग ('\ n') का उपयोग करना चाहिए या स्कैनर का उपयोग करना चाहिए।"
mdwhatcott

12
@mdwhatcott यह क्यों मायने रखता है कि इसकी "निम्न-स्तरीय लाइन-रीडिंग आदिम" है? यह इस निष्कर्ष पर कैसे पहुँचता है कि "अधिकांश कॉलर्स ReadBytes ('\ n') या ReadString ('\ n') का उपयोग करें या स्कैनर का उपयोग करें?"
चार्ली पार्कर

12
@CharlieParker - यकीन नहीं, सिर्फ संदर्भ जोड़ने के लिए डॉक्स को उद्धृत कर रहा हूं।
mdwhatcott

11
उसी डॉक्स से .. "यदि रीडस्ट्रिंग एक सीमांकक खोजने से पहले एक त्रुटि का सामना करता है, तो यह त्रुटि और त्रुटि से पहले पढ़ा गया डेटा (अक्सर io.EOF) देता है।" तो आप बस io.EOF त्रुटि की जांच कर सकते हैं और जान सकते हैं कि आपका काम हो चुका है।
eduncan911

1
ध्यान दें कि एक बाधित प्रणाली कॉल के कारण एक रीड या राइट विफल हो सकता है, जिसके परिणामस्वरूप अपेक्षित बाइट्स पढ़ने या लिखे जाने की अपेक्षा कम होती है।
जस्टिन स्वानर्ट

598

1.1 में जाएं और ऐसा करने का सबसे सरल तरीका है bufio.Scanner। यहाँ एक सरल उदाहरण है जो एक फ़ाइल से लाइनें पढ़ता है:

package main

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

func main() {
    file, err := os.Open("/path/to/file.txt")
    if err != nil {
        log.Fatal(err)
    }
    defer file.Close()

    scanner := bufio.NewScanner(file)
    for scanner.Scan() {
        fmt.Println(scanner.Text())
    }

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

यह Readerलाइन से लाइन से पढ़ने का सबसे साफ तरीका है ।

वहाँ एक चेतावनी है: स्कैनर 65536 वर्णों की तुलना में लंबी लाइनों के साथ अच्छी तरह से व्यवहार नहीं करता है। यदि आपके लिए यह एक मुद्दा है तो आपको संभवतः अपने खुद के शीर्ष पर रोल करना चाहिए Reader.Read()


40
और जब से ओपी ने किसी फाइल को स्कैन करने के लिए कहा, तो यह पहले file, _ := os.Open("/path/to/file.csv")फाइल को हैंडल करने के लिए तुच्छ होगा और फिर फाइल हैंडल पर स्कैन करेगा:scanner := bufio.NewScanner(file)
इवान प्लमली

14
भूलना मत defer file.Close()
किरिल

13
समस्या Scanner.Scan () 4096 [] बाइट बफर आकार प्रति पंक्ति में सीमित है। आपको bufio.ErrTooLongत्रुटि मिलेगी , जो कि bufio.Scanner: token too longयदि लाइन बहुत लंबी है। किस स्थिति में, आपको bufio.ReaderLine () या ReadString () का उपयोग करना होगा।
eduncan911

5
बस मेरा $ 0.02 - यह पृष्ठ पर सबसे सही उत्तर है :)
sethvargo

5

78

उपयोग:

  • reader.ReadString('\n')
    • अगर आपको इस बात से ऐतराज नहीं है कि लाइन बहुत लंबी हो सकती है (यानी बहुत सारी रैम का इस्तेमाल करें)। यह \nलौटे स्ट्रिंग के अंत में रहता है ।
  • reader.ReadLine()
    • यदि आप रैम की खपत को सीमित करने के बारे में परवाह करते हैं और उस मामले को संभालने के अतिरिक्त काम पर ध्यान नहीं देते हैं जहां लाइन पाठक के बफर आकार से अधिक है।

मैंने परिदृश्यों का परीक्षण करने के लिए एक कार्यक्रम लिखकर सुझाए गए विभिन्न समाधानों का परीक्षण किया, जिन्हें अन्य उत्तरों में समस्याओं के रूप में पहचाना जाता है:

  • 4MB लाइन वाली फाइल।
  • एक फ़ाइल जो एक लाइन ब्रेक के साथ समाप्त नहीं होती है।

मैने पाया कि:

  • Scannerसमाधान लंबी लाइनों संभाल नहीं करता है।
  • ReadLineसमाधान को लागू करने के लिए जटिल है।
  • ReadStringसमाधान सरल है और लंबी लाइनों के लिए काम करता है।

यहाँ कोड है जो प्रत्येक समाधान को प्रदर्शित करता है, इसे इसके माध्यम से चलाया जा सकता है go run main.go:

package main

import (
    "bufio"
    "bytes"
    "fmt"
    "io"
    "os"
)

func readFileWithReadString(fn string) (err error) {
    fmt.Println("readFileWithReadString")

    file, err := os.Open(fn)
    defer file.Close()

    if err != nil {
        return err
    }

    // Start reading from the file with a reader.
    reader := bufio.NewReader(file)

    var line string
    for {
        line, err = reader.ReadString('\n')

        fmt.Printf(" > Read %d characters\n", len(line))

        // Process the line here.
        fmt.Println(" > > " + limitLength(line, 50))

        if err != nil {
            break
        }
    }

    if err != io.EOF {
        fmt.Printf(" > Failed!: %v\n", err)
    }

    return
}

func readFileWithScanner(fn string) (err error) {
    fmt.Println("readFileWithScanner - this will fail!")

    // Don't use this, it doesn't work with long lines...

    file, err := os.Open(fn)
    defer file.Close()

    if err != nil {
        return err
    }

    // Start reading from the file using a scanner.
    scanner := bufio.NewScanner(file)

    for scanner.Scan() {
        line := scanner.Text()

        fmt.Printf(" > Read %d characters\n", len(line))

        // Process the line here.
        fmt.Println(" > > " + limitLength(line, 50))
    }

    if scanner.Err() != nil {
        fmt.Printf(" > Failed!: %v\n", scanner.Err())
    }

    return
}

func readFileWithReadLine(fn string) (err error) {
    fmt.Println("readFileWithReadLine")

    file, err := os.Open(fn)
    defer file.Close()

    if err != nil {
        return err
    }

    // Start reading from the file with a reader.
    reader := bufio.NewReader(file)

    for {
        var buffer bytes.Buffer

        var l []byte
        var isPrefix bool
        for {
            l, isPrefix, err = reader.ReadLine()
            buffer.Write(l)

            // If we've reached the end of the line, stop reading.
            if !isPrefix {
                break
            }

            // If we're just at the EOF, break
            if err != nil {
                break
            }
        }

        if err == io.EOF {
            break
        }

        line := buffer.String()

        fmt.Printf(" > Read %d characters\n", len(line))

        // Process the line here.
        fmt.Println(" > > " + limitLength(line, 50))
    }

    if err != io.EOF {
        fmt.Printf(" > Failed!: %v\n", err)
    }

    return
}

func main() {
    testLongLines()
    testLinesThatDoNotFinishWithALinebreak()
}

func testLongLines() {
    fmt.Println("Long lines")
    fmt.Println()

    createFileWithLongLine("longline.txt")
    readFileWithReadString("longline.txt")
    fmt.Println()
    readFileWithScanner("longline.txt")
    fmt.Println()
    readFileWithReadLine("longline.txt")
    fmt.Println()
}

func testLinesThatDoNotFinishWithALinebreak() {
    fmt.Println("No linebreak")
    fmt.Println()

    createFileThatDoesNotEndWithALineBreak("nolinebreak.txt")
    readFileWithReadString("nolinebreak.txt")
    fmt.Println()
    readFileWithScanner("nolinebreak.txt")
    fmt.Println()
    readFileWithReadLine("nolinebreak.txt")
    fmt.Println()
}

func createFileThatDoesNotEndWithALineBreak(fn string) (err error) {
    file, err := os.Create(fn)
    defer file.Close()

    if err != nil {
        return err
    }

    w := bufio.NewWriter(file)
    w.WriteString("Does not end with linebreak.")
    w.Flush()

    return
}

func createFileWithLongLine(fn string) (err error) {
    file, err := os.Create(fn)
    defer file.Close()

    if err != nil {
        return err
    }

    w := bufio.NewWriter(file)

    fs := 1024 * 1024 * 4 // 4MB

    // Create a 4MB long line consisting of the letter a.
    for i := 0; i < fs; i++ {
        w.WriteRune('a')
    }

    // Terminate the line with a break.
    w.WriteRune('\n')

    // Put in a second line, which doesn't have a linebreak.
    w.WriteString("Second line.")

    w.Flush()

    return
}

func limitLength(s string, length int) string {
    if len(s) < length {
        return s
    }

    return s[:length]
}

मैंने परीक्षण किया:

  • गो संस्करण go1.7 विंडोज़ / amd64
  • जाना संस्करण go1.6.3 linux / amd64
  • go version go1.7.4 darwin / amd64

परीक्षण कार्यक्रम आउटपुट:

Long lines

readFileWithReadString
 > Read 4194305 characters
 > > aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
 > Read 12 characters
 > > Second line.

readFileWithScanner - this will fail!
 > Failed!: bufio.Scanner: token too long

readFileWithReadLine
 > Read 4194304 characters
 > > aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
 > Read 12 characters
 > > Second line.

No linebreak

readFileWithReadString
 > Read 28 characters
 > > Does not end with linebreak.

readFileWithScanner - this will fail!
 > Read 28 characters
 > > Does not end with linebreak.

readFileWithReadLine
 > Read 28 characters
 > > Does not end with linebreak.

9
defer file.Close()त्रुटि जांच के बाद होना चाहिए; अन्यथा त्रुटि पर यह घबरा जाएगा।
मिग जूल

यदि आप इसे इस तरह कॉन्फ़िगर करते हैं तो स्कैनर समाधान लंबी लाइनों को संभालता है। देखें: golang.org/pkg/bufio/#Scanner.Buffer
Inanc Gumus

आपको डॉक्स में देखी गई त्रुटि को ठीक से जांचना चाहिए: play.golang.org/p/5CCPzVTSj6 यानी अगर इरेट == io.EOF {ब्रेक} बाकी {रिटर्न
इरेट

53

EDIT: go1.1 के रूप में, मुहावरेदार समाधान bufio.Scanner का उपयोग करना है

मैंने एक फाइल से प्रत्येक पंक्ति को आसानी से पढ़ने का एक तरीका लिखा। Readln (* bufio.Reader) फ़ंक्शन अंतर्निहित bufio.Reader संरचना से एक पंक्ति (sans \ n) लौटाता है।

// Readln returns a single line (without the ending \n)
// from the input buffered reader.
// An error is returned iff there is an error with the
// buffered reader.
func Readln(r *bufio.Reader) (string, error) {
  var (isPrefix bool = true
       err error = nil
       line, ln []byte
      )
  for isPrefix && err == nil {
      line, isPrefix, err = r.ReadLine()
      ln = append(ln, line...)
  }
  return string(ln),err
}

फाइल से हर लाइन पढ़ने के लिए आप रीडलेन का उपयोग कर सकते हैं। निम्न कोड एक फ़ाइल में प्रत्येक पंक्ति पढ़ता है और प्रत्येक पंक्ति को stdout में आउटपुट करता है।

f, err := os.Open(fi)
if err != nil {
    fmt.Printf("error opening file: %v\n",err)
    os.Exit(1)
}
r := bufio.NewReader(f)
s, e := Readln(r)
for e == nil {
    fmt.Println(s)
    s,e = Readln(r)
}

चीयर्स!


14
मैंने यह उत्तर गो १.१ आने से पहले लिखा था। गो 1.1 में stdlib में एक स्कैनर पैकेज है। कि मेरे जवाब के रूप में एक ही कार्यक्षमता प्रदान करता है। मैं अपने उत्तर के बजाय स्कैनर का उपयोग करने की सलाह दूंगा क्योंकि स्कैनर stdlib में है। हैप्पी हैकिंग! :-)
मैल्कम

30

फ़ाइल लाइन को लाइन से पढ़ने का दो सामान्य तरीका है।

  1. Bufio.Scanner का उपयोग करें
  2. Readfring / ReadBytes / ... का उपयोग bufio.Reader में करें

मेरे टेस्टकेस में, ~ 250MB, ~ 2,500,000 लाइनें , bufio.Scanner (समय का उपयोग: 0.395491384s) bufio.Reader.ReadString (time_used: 0.44686762222) की तुलना में तेज़ है।

स्रोत कोड: https://github.com/xpzouying/go-ults/tree/master/read_file_line_by_line

पढ़ें फ़ाइल का उपयोग bufio.Scanner,

func scanFile() {
    f, err := os.OpenFile(logfile, os.O_RDONLY, os.ModePerm)
    if err != nil {
        log.Fatalf("open file error: %v", err)
        return
    }
    defer f.Close()

    sc := bufio.NewScanner(f)
    for sc.Scan() {
        _ = sc.Text()  // GET the line string
    }
    if err := sc.Err(); err != nil {
        log.Fatalf("scan file error: %v", err)
        return
    }
}

पढ़ें फ़ाइल का उपयोग bufio.Reader,

func readFileLines() {
    f, err := os.OpenFile(logfile, os.O_RDONLY, os.ModePerm)
    if err != nil {
        log.Fatalf("open file error: %v", err)
        return
    }
    defer f.Close()

    rd := bufio.NewReader(f)
    for {
        line, err := rd.ReadString('\n')
        if err != nil {
            if err == io.EOF {
                break
            }

            log.Fatalf("read file line error: %v", err)
            return
        }
        _ = line  // GET the line string
    }
}

विदित हो कि यह bufio.Readerउदाहरण एक फाइल में अंतिम पंक्ति को नहीं पढ़ेगा यदि यह एक नई पंक्ति के साथ समाप्त नहीं होता है। ReadStringअंतिम पंक्ति और io.EOFइस मामले में दोनों वापस कर देंगे ।
कोनराड

18

इस जिस्ट से उदाहरण

func readLine(path string) {
  inFile, err := os.Open(path)
  if err != nil {
     fmt.Println(err.Error() + `: ` + path)
     return
  }
  defer inFile.Close()

  scanner := bufio.NewScanner(inFile)
  for scanner.Scan() {
    fmt.Println(scanner.Text()) // the line
  }
}

लेकिन यह एक त्रुटि देता है जब एक लाइन होती है जो स्कैनर के बफर से बड़ी होती है।

जब ऐसा हुआ, मैं क्या उपयोग है reader := bufio.NewReader(inFile)बना सकते हैं और अपने खुद के बफर concat या तो का उपयोग कर ch, err := reader.ReadByte()याlen, err := reader.Read(myBuffer)

एक और तरीका जो मैं उपयोग करता हूं (ऊपर की तरह फ़ाइल के साथ os.Stdin को प्रतिस्थापित करता है), यह एक समतल होता है जब लाइनें लंबी होती हैं (आइसप्रिक्स) और खाली लाइनों को अनदेखा करता है:


func readLines() []string {
  r := bufio.NewReader(os.Stdin)
  bytes := []byte{}
  lines := []string{}
  for {
    line, isPrefix, err := r.ReadLine()
    if err != nil {
      break
    }
    bytes = append(bytes, line...)
    if !isPrefix {
      str := strings.TrimSpace(string(bytes))
      if len(str) > 0 {
        lines = append(lines, str)
        bytes = []byte{}
      }
    }
  }
  if len(bytes) > 0 {
    lines = append(lines, string(bytes))
  }
  return lines
}

परवाह करने के लिए क्यों -1?
कोकिज्जू

मुझे लगता है, यह थोड़ा सा समाधान है, तुम नहीं?
देसलबल

10

आप विभाजक के रूप में ReadString का उपयोग \ N के साथ भी कर सकते हैं:

  f, err := os.Open(filename)
  if err != nil {
    fmt.Println("error opening file ", err)
    os.Exit(1)
  }
  defer f.Close()
  r := bufio.NewReader(f)
  for {
    path, err := r.ReadString(10) // 0x0A separator = newline
    if err == io.EOF {
      // do something here
      break
    } else if err != nil {
      return err // if you return error
    }
  }

5

bufio.Reader.eadLine () अच्छी तरह से काम करता है। लेकिन यदि आप एक स्ट्रिंग द्वारा प्रत्येक पंक्ति को पढ़ना चाहते हैं, तो ReadString ('\ n') का उपयोग करने का प्रयास करें । यह पहिया को सुदृढ़ करने की आवश्यकता नहीं है।


3
// strip '\n' or read until EOF, return error if read error  
func readline(reader io.Reader) (line []byte, err error) {   
    line = make([]byte, 0, 100)                              
    for {                                                    
        b := make([]byte, 1)                                 
        n, er := reader.Read(b)                              
        if n > 0 {                                           
            c := b[0]                                        
            if c == '\n' { // end of line                    
                break                                        
            }                                                
            line = append(line, c)                           
        }                                                    
        if er != nil {                                       
            err = er                                         
            return                                           
        }                                                    
    }                                                        
    return                                                   
}                                    

1

कोड bellow में, मैं CLI से उन हितों को पढ़ता हूं जब तक कि उपयोगकर्ता हिट नहीं करता है और मैं Readline का उपयोग कर रहा हूं:

interests := make([]string, 1)
r := bufio.NewReader(os.Stdin)
for true {
    fmt.Print("Give me an interest:")
    t, _, _ := r.ReadLine()
    interests = append(interests, string(t))
    if len(t) == 0 {
        break;
    }
}
fmt.Println(interests)

0

मुझे Lzap समाधान पसंद है, मैं गो में नया हूँ, मैं ज़ोर से पूछना चाहता हूँ, लेकिन मैं ऐसा नहीं कर सका, मेरे पास अभी तक 50 अंक नहीं हैं .. मैं आपके समाधान को थोड़ा बदल देता हूँ और कोड पूरा कर लेता हूँ ...

package main

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

func main() {
    f, err := os.Open("archiveName")
    if err != nil {
        fmt.Println(err)
        os.Exit(1)
    }
    defer f.Close()
    r := bufio.NewReader(f)
    line, err := r.ReadString(10)    // line defined once 
    for err != io.EOF {
        fmt.Print(line)              // or any stuff
        line, err = r.ReadString(10) //  line was defined before
    }
}

मुझे यकीन नहीं है कि मुझे 'फिर से' परीक्षण करने की आवश्यकता क्यों है, लेकिन वैसे भी हम यह कर सकते हैं। लेकिन, मुख्य सवाल यह है कि .. गो वाक्य के साथ त्रुटि क्यों पैदा नहीं करता है => लाइन, इर: = r.ReadString (10), लूप के अंदर? हर बार लूप निष्पादित होने पर इसे बार-बार परिभाषित किया जाता है। मैं अपने परिवर्तन, किसी भी टिप्पणी के साथ उस स्थिति से बचता हूं? मैंने थोड़ी देर के लिए भी ईओएफ को 'के लिए' स्थिति निर्धारित किया है। धन्यवाद


0
import (
     "bufio"
     "os"
)

var (
    reader = bufio.NewReader(os.Stdin)
)

func ReadFromStdin() string{
    result, _ := reader.ReadString('\n')
    witl := result[:len(result)-1]
    return witl
}

यहाँ फ़ंक्शन के साथ इसका एक उदाहरण है ReadFromStdin(), fmt.Scan(&name)लेकिन यह रिक्त स्थानों के साथ सभी तार लेता है जैसे: "हैलो मेरा नाम है ..."

var name string = ReadFromStdin()

println(name)
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.