গো-এ লাইনে একটি ফাইল লাইন পড়া


334

আমি গোতে file.ReadLineফাংশনটি খুঁজে পাচ্ছি না । কীভাবে কীভাবে একটি লিখতে হয় তা আমি বুঝতে পারি, তবে আমি এখানে ভাবছি যে আমি এখানে কিছু উপেক্ষা করছি। কীভাবে কেউ এক লাইনে ফাইল ফাইল পড়তে পারে?


7
Go1.1 হিসাবে, বুফিও.স্ক্যানার এটি করার সর্বোত্তম উপায়।
ম্যালকম

উত্তর:


133

দ্রষ্টব্য: গোড়ার প্রাথমিক সংস্করণগুলিতে স্বীকৃত উত্তরটি সঠিক ছিল। সর্বাধিক ভোট দেওয়া উত্তরটিতে এটি অর্জনের জন্য আরও সাম্প্রতিক মুহুর্তের উপায় রয়েছে।

প্যাকেজে ফাংশন রিডলাইন রয়েছে bufio

দয়া করে মনে রাখবেন যে লাইনটি রিড বাফারের সাথে ফিট না করে তবে ফাংশনটি অসম্পূর্ণ লাইনটি ফিরিয়ে দেবে। আপনি যদি কোনও প্রোগ্রামে একক কল করে আপনার প্রোগ্রামের সর্বদা একটি সম্পূর্ণ লাইন পড়তে চান তবে আপনাকে ReadLineফাংশনটি আপনার নিজের ফাংশনে আবদ্ধ করতে হবে যা ReadLineএকটি লুপে কল করে।

bufio.ReadString('\n')সম্পূর্ণরূপে সমতুল্য নয় ReadLineকারণ ReadStringকোনও ফাইলের শেষ লাইনটি যখন নিউলাইন চরিত্রের সাথে শেষ হয় না তখন কেসটি পরিচালনা করতে অক্ষম।


37
দস্তাবেজগুলি থেকে: "রিডলাইন একটি নিম্ন-স্তরের লাইন রিডিং আদিম। বেশিরভাগ কলকারীদের পরিবর্তে রিডবাইটস ('\ n') বা রিডস্ট্রিং ('\ n') ব্যবহার করা উচিত বা স্ক্যানার ব্যবহার করা উচিত" "
এমডিচটকাট

12
@ এমডিচটকাট কেন এটি "নিম্ন-স্তরের লাইন-পঠন আদিম" যে বিষয়টি বিবেচনা করে? কীভাবে এই সিদ্ধান্তে পৌঁছে যায় যে "বেশিরভাগ কলারদের পরিবর্তে রিডবাইটস ('\ n') বা রিডস্ট্রিং ('\ n') ব্যবহার করা উচিত বা কোনও স্ক্যানার ব্যবহার করা উচিত?"
চার্লি পার্কার 21

12
@ চর্লিপারপার্কার - নিশ্চিত নন, প্রসঙ্গ যুক্ত করার জন্য কেবলমাত্র ডক্সকে উদ্ধৃত করা।
এমডিহটকাট 21

11
একই দস্তাবেজ থেকে .. "যদি রিড স্ট্রিং কোনও ডিলিমিটার সন্ধানের আগে একটি ত্রুটির মুখোমুখি হয়, তবে এটি ত্রুটি এবং ত্রুটি হওয়ার আগে নিজেই পড়া ডেটা ফেরত দেয় (প্রায়শই আইওইওএফ)"। সুতরাং আপনি কেবল আইও.ও.এফ ত্রুটি পরীক্ষা করতে পারেন এবং আপনার সম্পন্ন হয়েছে তা জানতে পারবেন।
eduncan911

1
মনে রাখবেন যে একটি পঠন বা লিখন একটি বাধাগুলি সিস্টেম কলের কারণে ব্যর্থ হতে পারে, যার ফলশ্রুতি প্রত্যাশিত সংখ্যার বাইট কম পড়ে বা লিখিত হয়।
জাস্টিন সোয়ানহার্ট

597

যান 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
সমস্যাটি স্ক্যানার S স্ক্যান () প্রতি লাইন 4096 [] বাইট বাফার আকারে সীমাবদ্ধ। আপনি bufio.ErrTooLongত্রুটি পাবেন, যা bufio.Scanner: token too longযদি লাইনটি দীর্ঘ হয়। কোন ক্ষেত্রে আপনাকে বুফিও ব্যবহার করতে হবে e রিডারলাইন () বা রিডস্ট্রিং ()।
eduncan911

5
কেবল আমার $ 0.02 - এটি পৃষ্ঠায় সবচেয়ে সঠিক উত্তর :)
sethvargo

5
আপনি স্ক্যানারকে এর বাফার () পদ্ধতিটি ব্যবহার করে লম্বা লাইনগুলি পরিচালনা করতে কনফিগার করতে পারেন: golang.org/pkg/bufio/# স্ক্যানার.ফুফার
অ্যালেক্স রবিনসন

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 উইন্ডোজ / এএমডি 64 সংস্করণে যান
  • Go1.6.3 লিনাক্স / এএমডি 64 সংস্করণে যান
  • Go সংস্করণ go1.7.4 ডারউইন / এএমডি 64

পরীক্ষার প্রোগ্রাম ফলাফল:

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/#
স্ক্যানার।

ডক্সে যেমন দেখা গেছে তেমনি আপনার ত্রুটিটি সঠিকভাবে পরীক্ষা করা উচিত: play.golang.org/p/5CCPzVTSj6 অর্থাত্ যদি ত্রুটি == io.EOF {বিরতি} অন্য {ফেরত
ত্রুটি

53

সম্পাদনা: Go1.1 হিসাবে, idiomatic সমাধান ব্যবহার করা হয় বুফিও.স্ক্যানার হয়

আমি ফাইল থেকে প্রতিটি লাইন সহজেই পড়ার উপায় লিখেছিলাম। Readln (* bufio.Reader) ফাংশন অন্তর্নিহিত bufio.Reader স্ট্রাক্ট থেকে একটি লাইন (সান \ 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
}

আপনি ফাইল থেকে প্রতিটি লাইন পড়তে Readln ব্যবহার করতে পারেন। নিম্নলিখিত কোডটি একটি ফাইলের প্রতিটি লাইন পড়ে এবং প্রতিটি লাইন 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
Go 1.1 প্রকাশের আগে আমি এই উত্তরটি লিখেছিলাম। Go 1.1 এর stdlib এ একটি স্ক্যানার প্যাকেজ রয়েছে। এটি আমার উত্তর হিসাবে একই কার্যকারিতা সরবরাহ করে। আমি আমার উত্তরের পরিবর্তে স্ক্যানার ব্যবহারের পরামর্শ দেব কারণ স্ক্যানার স্টাডলিবায় রয়েছে। শুভ হ্যাকিং! :-)
ম্যালকম

30

লাইন দ্বারা ফাইল লাইন পড়ার দুটি সাধারণ উপায়।

  1. বুফিও.স্ক্যানার ব্যবহার করুন
  2. রিফ স্ট্রিং / রিডবাইট / ... বুফিও.আরেডার ব্যবহার করুন

আমার টেস্টকেসে , ~ 250MB, ~ 2,500,000 লাইন , বুফিও.স্ক্যানার (সময় ব্যবহৃত হয়েছে: 0.395491384 গুলি) বুফিওর চেয়ে দ্রুত। রিডার.আবার স্ট্রিং (সময়_ ব্যবহৃত: 0.446867622s)।

উত্স কোড: https://github.com/xpzouying/go-pੈਕਟ / tree / master / read_file_line_by_line

ফাইল ব্যবহার বুফিও পড়ুন। স্ক্যানার,

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

ফাইল ব্যবহার বুফিও পড়ুন। রেডার,

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)

আমি অন্য একটি উপায়ে ব্যবহার করি (ওএস স্টিডিনকে উপরের মতো ফাইলের সাথে প্রতিস্থাপন করুন), লাইন দীর্ঘ হলে (ইসপ্রিফিক্স) খালি লাইন উপেক্ষা করার পরে এটি এক হয়ে যায়:


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

আপনি বিভাজক হিসাবে Read 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.ReadLine () ভাল কাজ করে। তবে আপনি যদি প্রতিটি লাইনের স্ট্রিং দিয়ে পড়তে চান তবে রিড স্ট্রিং ('\ 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

কোড বেলোতে, আমি সিএলআই থেকে আগ্রহগুলি পড়ি যতক্ষণ না ব্যবহারকারী প্রবেশ করায় এবং আমি রিডলাইন ব্যবহার করি:

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

আমি লাজাপের সমাধানটি পছন্দ করি, আমি গোতে নতুন, আমি লাজাপ বলতে চাই কিন্তু আমি এটি করতে পারিনি আমার এখনও 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.আর্টস্ট্রিং (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)
আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.