2012-02-16 32 views
6

Tôi đang cố gắng sửa đổi tệp PostScript lớn trong Scala (một số có kích thước 1GB). Các tập tin là một nhóm các lô, mỗi lô có chứa mã đại diện cho số lô, số lượng trang vvSửa đổi một tệp lớn trong Scala

tôi cần phải:

  1. Tìm kiếm các tập tin cho các mã hàng loạt (mà luôn luôn bắt đầu bằng cùng một dòng trong tệp)
  2. Đếm số trang cho đến khi mã hàng loạt tiếp theo
  3. Sửa đổi mã lô để bao gồm số lượng trang trong mỗi lô.
  4. Lưu tệp mới ở một vị trí khác.

Giải pháp hiện tại của tôi sử dụng hai trình lặp (iterAiterB), được tạo từ Source.fromFile("file.ps").getLines. Trình lặp đầu tiên (iterA) truyền tải trong một vòng lặp while đến đầu của một mã lô (với số iterB.next cũng được gọi mỗi lần). iterB sau đó tiếp tục tìm kiếm cho đến khi mã hàng loạt tiếp theo (hoặc kết thúc của tệp), đếm số trang nó vượt qua khi nó đi. Sau đó, nó cập nhật mã lô tại vị trí của iterA, một quá trình lặp lại.

Điều này có vẻ rất không giống với Scala và tôi vẫn chưa thiết kế một cách hay để lưu các thay đổi này vào một tệp mới.

Cách tiếp cận tốt cho vấn đề này là gì? Tôi có nên bỏ qua trình vòng lặp hoàn toàn không? Tôi muốn làm điều đó mà không cần phải có toàn bộ đầu vào hoặc đầu ra vào bộ nhớ cùng một lúc.

Cảm ơn!

Trả lời

2

Bạn có thể thực hiện điều này với lớp học Stream của Scala. Tôi giả định rằng bạn không nhớ giữ một "lô" trong bộ nhớ cùng một lúc.

import scala.annotation.tailrec 
import scala.io._ 

def isBatchLine(line:String):Boolean = ... 

def batchLine(size: Int):String = ... 

val it = Source.fromFile("in.ps").getLines 
// cannot use it.toStream here because of SI-4835 
def inLines = Stream.continually(i).takeWhile(_.hasNext).map(_.next) 

// Note: using `def` instead of `val` here means we don't hold 
// the entire stream in memory 
def batchedLinesFrom(stream: Stream[String]):Stream[String] = { 
    val (batch, remainder) = stream span { !isBatchLine(_) } 
    if (batch.isEmpty && remainder.isEmpty) { 
    Stream.empty 
    } else { 
    batchLine(batch.size) #:: batch #::: batchedLinesFrom(remainder.drop(1)) 
    } 
} 

def newLines = batchedLinesFrom(inLines dropWhile isBatchLine) 

val ps = new java.io.PrintStream(new java.io.File("out.ps")) 

newLines foreach ps.println 

ps.close() 
+1

Tôi đoán giải pháp này sẽ giữ toàn bộ tệp trong bộ nhớ vì trên mẫu 2.9.x này 'Source.fromFile (" in.ps "). getLines.toStream' giữ trên đầu luồng. Xem http://stackoverflow.com/a/8640680/257449 và https://issues.scala-lang.org/browse/SI-4835. – huynhjl

+0

huynhjl, tôi đã cập nhật mẫu mã để sửa lỗi (gây phiền nhiễu) mà bạn đã tìm thấy. – stephenjudkins

0

Có thể là bạn có thể sử dụng spanduplicate một cách hiệu quả. Giả sử trình vòng lặp được đặt ở đầu của một lô, bạn lấy khoảng trước lô tiếp theo, sao chép nó để bạn có thể đếm các trang, viết dòng lô đã sửa đổi, sau đó viết các trang bằng trình lặp lặp. Sau đó xử lý hàng loạt tiếp theo đệ quy ...

def batch(i: Iterator[String]) { 
    if (i.hasNext) { 
    assert(i.next() == "batch") 
    val (current, next) = i.span(_ != "batch") 
    val (forCounting, forWriting) = current.duplicate 
    val count = forCounting.filter(_ == "p").size 
    println("batch " + count) 
    forWriting.foreach(println) 
    batch(next) 
    } 
} 

Giả sử đầu vào sau:

val src = Source.fromString("head\nbatch\np\np\nbatch\np\nbatch\np\np\np\n") 

Bạn xác định vị trí các iterator vào lúc bắt đầu của hàng loạt và sau đó bạn xử lý các lô:

val (head, next) = src.getLines.span(_ != "batch") 
head.foreach(println) 
batch(next) 

Bản in này:

head 
batch 2 
p 
p 
batch 1 
p 
batch 3 
p 
p 
p 
1

Nếu bạn không theo đuổi giác ngộ scala chức năng, tôi muốn giới thiệu một phong cách bắt buộc hơn bằng cách sử dụng java.util.Scanner#findWithinHorizon. Ví dụ của tôi khá ngây thơ, lặp lại thông qua đầu vào hai lần.

val scanner = new Scanner(inFile) 

val writer = new BufferedWriter(...) 

def loop() = { 
    // you might want to limit the horizon to prevent OutOfMemoryError 
    Option(scanner.findWithinHorizon(".*YOUR-BATCH-MARKER", 0)) match { 
    case Some(batch) => 
     val pageCount = countPages(batch) 
     writePageCount(writer, pageCount) 
     writer.write(batch)   
     loop() 

    case None => 
    } 
} 

loop() 
scanner.close() 
writer.close() 
Các vấn đề liên quan