2012-04-26 29 views
6

Tôi muốn viết một hàm đơn giản lặp qua các dòng của một tệp văn bản. Tôi tin vào 2.8 người ta có thể làm:Lặp lại các dòng của một tập tin

def lines(filename: String) : Iterator[String] = { 
    scala.io.Source.fromFile(filename).getLines 
} 

và đó là thế, nhưng trong 2.9 ở trên không hoạt động và thay vào đó tôi phải làm:

def lines(filename: String) : Iterator[String] = { 
    scala.io.Source.fromFile(new File(filename)).getLines() 
} 

Bây giờ, những rắc rối là, tôi muốn để tạo nên vòng lặp trên trong một sự hiểu biết for:

for (l1 <- lines("file1.txt"); l2 <- lines("file2.txt")){ 
    do_stuff(l1, l2) 
} 

này một lần nữa, được sử dụng để làm việc tốt với 2.8 nhưng gây ra một "quá ma ny mở tệp " ngoại lệ để được ném vào số 2.9. Điều này là dễ hiểu - lần thứ hai lines trong lần đọc kết thúc mở (và không đóng) một tệp cho mỗi dòng trong trường hợp đầu tiên.

Trong trường hợp của tôi, tôi biết rằng "file1.txt" là lớn và tôi không muốn để hút nó vào
bộ nhớ, nhưng các tập tin thứ hai là nhỏ, vì vậy tôi có thể viết một khác nhau linesEager như vậy:

def linesEager(filename: String): Iterator[String] = 
    val buf = scala.io.Source.fromFile(new File(filename)) 
    val zs = buf.getLines().toList.toIterator 
    buf.close() 
    zs 

và sau đó lần lượt của tôi cho-hiểu thành:

for (l1 <- lines("file1.txt"); l2 <- linesEager("file2.txt")){ 
    do_stuff(l1, l2) 
} 

này hoạt động, nhưng rõ ràng là xấu xí. Ai đó có thể đề xuất một cách thống nhất & sạch cách để đạt được điều này. Có vẻ như bạn cần một cách cho trình lặp số được trả lại bởi lines đến close tệp khi đến cuối và điều này phải xảy ra trong 2.8 đó là lý do tại sao nó hoạt động ở đó?

Cảm ơn!

BTW - đây là một phiên bản tối thiểu của chương trình đầy đủ cho thấy vấn đề này:

import java.io.PrintWriter 
import java.io.File 

object Fail { 

    def lines(filename: String) : Iterator[String] = { 
    val f = new File(filename) 
    scala.io.Source.fromFile(f).getLines() 
    } 

    def main(args: Array[String]) = { 
    val smallFile = args(0) 
    val bigFile = args(1) 

    println("helloworld") 

    for (w1 <- lines(bigFile) 
     ; w2 <- lines(smallFile) 
     ) 
    { 
     if (w2 == w1){ 
     val msg = "%s=%s\n".format(w1, w2) 
     println("found" + msg) 
     } 
    } 

    println("goodbye") 
    } 

} 

On 2.9.0 tôi biên dịch với scalac WordsFail.scala và sau đó tôi có được điều này:

[email protected]:$ scalac WordsFail.scala 
[email protected]:$ scala Fail passwd words 
helloworld 
java.io.FileNotFoundException: passwd (Too many open files) 
    at java.io.FileInputStream.open(Native Method) 
    at java.io.FileInputStream.<init>(FileInputStream.java:120) 
    at scala.io.Source$.fromFile(Source.scala:91) 
    at scala.io.Source$.fromFile(Source.scala:76) 
    at Fail$.lines(WordsFail.scala:8) 
    at Fail$$anonfun$main$1.apply(WordsFail.scala:18) 
    at Fail$$anonfun$main$1.apply(WordsFail.scala:17) 
    at scala.collection.Iterator$class.foreach(Iterator.scala:652) 
    at scala.io.BufferedSource$BufferedLineIterator.foreach(BufferedSource.scala:30) 
    at Fail$.main(WordsFail.scala:17) 
    at Fail.main(WordsFail.scala) 
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) 
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39) 
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25) 
    at java.lang.reflect.Method.invoke(Method.java:597) 
    at scala.tools.nsc.util.ScalaClassLoader$$anonfun$run$1.apply(ScalaClassLoader.scala:78) 
    at scala.tools.nsc.util.ScalaClassLoader$class.asContext(ScalaClassLoader.scala:24) 
    at scala.tools.nsc.util.ScalaClassLoader$URLClassLoader.asContext(ScalaClassLoader.scala:88) 
    at scala.tools.nsc.util.ScalaClassLoader$class.run(ScalaClassLoader.scala:78) 
    at scala.tools.nsc.util.ScalaClassLoader$URLClassLoader.run(ScalaClassLoader.scala:101) 
    at scala.tools.nsc.ObjectRunner$.run(ObjectRunner.scala:33) 
    at scala.tools.nsc.ObjectRunner$.runAndCatch(ObjectRunner.scala:40) 
    at scala.tools.nsc.MainGenericRunner.runTarget$1(MainGenericRunner.scala:56) 
    at scala.tools.nsc.MainGenericRunner.process(MainGenericRunner.scala:80) 
    at scala.tools.nsc.MainGenericRunner$.main(MainGenericRunner.scala:89) 
    at scala.tools.nsc.MainGenericRunner.main(MainGenericRunner.scala) 
+3

Mã hoạt động cho tôi trong REPL (Scala 2.9). –

+0

Nó không phải là ';' không may. –

+0

@userunknown Nó hoạt động nhưng nó không mở rộng. (Hãy tưởng tượng các tệp lớn/nhiều dòng.) – Debilski

Trả lời

13

scala-arm cung cấp cơ chế tuyệt vời để đóng tài nguyên tự động khi bạn đã hoàn tất.

import resource._ 
import scala.io.Source 

for (file1 <- managed(Source.fromFile("file1.txt")); 
    l1 <- file1.getLines(); 
    file2 <- managed(Source.fromFile("file2.txt")); 
    l2 <- file2.getLines()) { 
    do_stuff(l1, l2) 
} 

Nhưng trừ khi bạn đang trông chờ vào các nội dung của file2.txt thay đổi trong khi bạn đang Looping qua file1.txt, nó sẽ là tốt nhất để đọc đó vào một List trước khi bạn lặp. Không cần phải chuyển đổi nó thành một Iterator.

+0

Không chuyển đổi nó thành một danh sách kết thúc giữ toàn bộ tập tin trong bộ nhớ? Tôi đã hy vọng để tránh điều đó ... –

+0

Nhưng 'file2.txt' là nhỏ, vì vậy mà nên được chấp nhận. Ngoài ra, đó là những gì 'lineEager' của bạn làm (' .List'), ngoại trừ việc bạn đang tạo nó trong bộ nhớ và ném nó đi cho mọi dòng trong 'file1.txt'. – leedm777

+0

Xin chào Dave, vâng, bạn đã đúng. Tôi đã bị hiểu lầm rằng các hành động khác nhau trong hiểu biết phải có cùng một loại, và do đó các 'toList.toIterator' không liên quan khi chỉ' toList' sẽ đủ ... Cảm ơn! –

2

Có lẽ bạn nên hãy xem scala-arm (https://github.com/jsuereth/scala-arm) và để cho việc đóng các tệp (các luồng đầu vào của tệp) diễn ra tự động trong nền.

Các vấn đề liên quan