2009-05-21 51 views
11

Tôi đang đọc trong tệp văn bản sử dụng FileInputStream để đặt nội dung tệp vào một mảng byte. Sau đó tôi chuyển đổi mảng byte thành một String bằng cách sử dụng String (byte) mới.Phân tích tệp văn bản trong Java

Khi tôi có chuỗi, tôi đang sử dụng String.split("\n") để chia tệp thành mảng String và sau đó lấy chuỗi đó và phân tích cú pháp bằng cách thực hiện String.split(",") và giữ nội dung trong Danh sách.

Tôi có tệp 200MB + và sắp hết bộ nhớ khi tôi khởi động JVM với bộ nhớ 1GB. Tôi biết tôi phải làm một cái gì đó ở một nơi nào đó chính xác, tôi chỉ không chắc chắn nếu cách tôi phân tích cú pháp là không chính xác hoặc cấu trúc dữ liệu tôi đang sử dụng.

Nó cũng đưa tôi khoảng 12 giây để phân tích cú pháp tệp có vẻ như rất nhiều thời gian. Bất cứ ai có thể chỉ ra những gì tôi có thể làm đó là làm cho tôi hết bộ nhớ và những gì có thể khiến chương trình của tôi chạy chậm?

Nội dung của tập tin xem như hình dưới đây:

"12334", "100", "1.233", "TEST", "TEXT", "1234" 
"12334", "100", "1.233", "TEST", "TEXT", "1234" 
. 
. 
. 
"12334", "100", "1.233", "TEST", "TEXT", "1234" 

Cảm ơn

+0

Hãy làm rõ: Bạn đang nói rằng bạn đang sử dụng -Xmx1024m để khởi động JVM của bạn và bạn vẫn nhận được một lỗi OutOfMemoryError? – duffymo

+0

Sử dụng mẫu được biên dịch sẽ tốt hơn (thậm chí cuộn phân tách chuyên gia của riêng bạn). Nhưng có vẻ như bạn chỉ có rất nhiều dữ liệu đối tượng. –

+0

duffymo, chính xác là như vậy. Tôi đang thiết lập JVM với -Xmx1024m và nhận được hte OutOfMemoryError –

Trả lời

8

Có vẻ như bạn đang làm điều gì sai với tôi - một sáng tạo toàn bộ đối tượng Lotta xảy ra.

Đại diện là tệp "kiểm tra" đó như thế nào? Bạn đang thực sự làm gì với dữ liệu đó? Nếu đó là điển hình của những gì bạn thực sự có, tôi muốn nói có rất nhiều sự lặp lại trong dữ liệu đó.

Nếu tất cả sẽ ở trong chuỗi, hãy bắt đầu với BufferedReader để đọc từng dòng. Phân bổ trước Danh sách đó cho một kích thước gần với những gì bạn cần, do đó bạn không lãng phí tài nguyên bổ sung vào nó mỗi lần. Tách từng dòng trong dấu phẩy; hãy chắc chắn loại bỏ các dấu ngoặc kép.

Bạn có thể muốn tự hỏi: "Tại sao tôi cần toàn bộ tệp này trong bộ nhớ cùng một lúc?" Bạn có thể đọc một chút, xử lý một chút và không bao giờ có toàn bộ điều trong bộ nhớ cùng một lúc không? Chỉ có bạn mới biết vấn đề của bạn đủ để trả lời.

Có thể bạn có thể kích hoạt jvisualvm nếu bạn có JDK 6 và xem điều gì đang xảy ra với bộ nhớ. Đó sẽ là một đầu mối tuyệt vời.

+0

Cách người hỏi đang làm nó xuất hiện để tạo ra một char lớn [] (trong một String) và sau đó Strings là những lát đó, mà đáng ngạc nhiên thực sự là cách hiệu quả bộ nhớ uber làm việc đó. (Không kiểm tra việc thực hiện phân chia. Tất nhiên là tất cả phụ thuộc vào việc triển khai thực hiện.) –

+0

Bạn đúng về "hiệu quả uber", Tom. Lời khuyên của tôi sẽ thực sự làm cho nó tồi tệ hơn. Nếu vấn đề vẫn còn, tôi nghĩ rằng nó đang xử lý trên bay và jvisualvm sẽ giúp nhiều nhất. – duffymo

+0

Bây giờ chúng ta có các luồng với Java 8, tôi tự hỏi liệu điều này có thể được thực hiện hiệu quả hơn bằng cách sử dụng lập trình hàm hay không. Đó là những gì các dòng được sinh ra cho. – duffymo

2

Nếu bạn có tệp 200.000 ký tự và chia nhỏ năm ký tự đó, bạn có 40.000.000 đối tượng String. Giả sử họ đang chia sẻ dữ liệu ký tự thực tế với 400 MB ban đầu String (char là 2 byte). A String được cho là 32 byte, do đó 1.280.000.000 byte của String đối tượng.

(Có lẽ đáng chú ý rằng đây là rất thực hiện phụ thuộc. split có thể tạo ra hoàn toàn dây với sự ủng hộ hoàn toàn mới char[] hay, OTOH, chia sẻ một số String giá trị chung. Một số triển khai Java không sử dụng cắt của char[]. Một số có thể sử dụng một hình thức nhỏ gọn giống như UTF-8 và cho thời gian truy cập ngẫu nhiên rất kém.)

Thậm chí giả sử chuỗi dài hơn, đó là rất nhiều đối tượng. Với nhiều dữ liệu đó, bạn có thể muốn làm việc với hầu hết dữ liệu ở dạng nén như bản gốc (chỉ với các chỉ mục). Chỉ chuyển đổi thành các đối tượng mà bạn cần. Việc thực hiện nên được cơ sở dữ liệu như (mặc dù họ theo truyền thống không xử lý các chuỗi có độ dài thay đổi một cách hiệu quả).

4

Có vẻ như bạn hiện có 3 bản sao của toàn bộ tệp trong bộ nhớ: mảng byte, chuỗi và mảng của các dòng.

Thay vì đọc các byte thành mảng byte và sau đó chuyển đổi thành ký tự bằng cách sử dụng new String() thì tốt hơn nên sử dụng InputStreamReader, sẽ chuyển đổi thành ký tự theo từng bước chứ không phải tất cả lên phía trước.

Ngoài ra, thay vì sử dụng String.split ("\ n") để nhận các dòng riêng lẻ, bạn nên đọc từng dòng một. Bạn có thể sử dụng phương thức readLine() trong BufferedReader.

Hãy thử một cái gì đó như thế này:

BufferedReader reader = new BufferedReader(new InputStreamReader(fileInputStream, "UTF-8")); 
try { 
    while (true) { 
    String line = reader.readLine(); 
    if (line == null) break; 
    String[] fields = line.split(","); 
    // process fields here 
    } 
} finally { 
    reader.close(); 
} 
+0

Cách ban đầu các Strings (nên) tất cả chia sẻ cùng một sao lưu char [], và do đó có hiệu quả hơn. Một dòng tách có lẽ không phải là quá xấu, bởi vì sẽ chỉ có một char [] trên mỗi dòng. –

+0

(Và mảng byte không cần phải có trong bộ nhớ cùng một lúc với mảng của dòng.) –

+0

Tôi bắt đầu cảm thấy như tôi đã có nhiều bản sao của nội dung tệp trong bộ nhớ. Tôi sẽ thử điều này và thấy sự khác biệt –

11

Tôi không chắc chắn hiệu quả như thế nào là nhớ-khôn ngoan, nhưng cách tiếp cận đầu tiên của tôi sẽ được sử dụng một Scanner vì nó là cực kỳ dễ sử dụng:

File file = new File("/path/to/my/file.txt"); 
Scanner input = new Scanner(file); 

while(input.hasNext()) { 
    String nextToken = input.next(); 
    //or to process line by line 
    String nextLine = input.nextLine(); 
} 

input.close(); 

Kiểm tra API để biết cách thay đổi dấu phân tách mà nó sử dụng để tách mã thông báo.

5

Hãy xem các trang này. Chúng chứa nhiều trình phân tích cú pháp CSV nguồn mở. JSaPar là một trong số chúng.

+0

Bất kỳ đề xuất cụ thể nào? –

+0

Vâng, tôi hơi thiên vị ở đây vì tôi là tác giả của thư viện JSaPar. Đó là lý do tại sao tôi đề cập đến nó trong câu trả lời của tôi nhưng một trong những thư viện khác có thể phù hợp hơn với bạn tùy thuộc vào vấn đề bạn đang cố giải quyết. – stenix

0

Trong khi gọi/gọi chương trình của bạn, bạn có thể sử dụng lệnh này: java [-options] className [args ...]
ở vị trí của [-options] cung cấp nhiều bộ nhớ hơn, ví dụ -Xmx1024m hoặc hơn. nhưng đây chỉ là một cách giải quyết, bạn phải thay đổi cơ chế phân tích cú pháp ur.

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