2009-01-31 34 views
31

OK, mkstemp là cách ưu tiên để tạo tệp tạm thời trong POSIX.Cách tạo std :: ofstream thành tệp tạm thời?

Nhưng nó sẽ mở tệp và trả về một int, là bộ mô tả tệp. Từ đó tôi chỉ có thể tạo một FILE *, nhưng không phải là std::ofstream, mà tôi thích trong C++. (Rõ ràng, trên AIX và một số hệ thống khác, bạn có thể tạo một std::ofstream từ một bộ mô tả tệp, nhưng trình biên dịch của tôi phàn nàn khi tôi thử điều đó.)

Tôi biết tôi có thể lấy tên tệp tạm thời bằng tmpnam và sau đó tự mở ofstream với nó, nhưng đó là dường như không an toàn do điều kiện chủng tộc, và kết quả là một trình biên dịch cảnh báo (g ++ v3.4 trên Linux.):

warning: the use of `tmpnam' is dangerous, better use `mkstemp' 

vì vậy, có cách nào di động để tạo ra một std::ofstream đến một temp tập tin?

Trả lời

10

Tôi nghĩ rằng điều này sẽ làm việc:

char *tmpname = strdup("/tmp/tmpfileXXXXXX"); 
    ofstream f; 
    int fd = mkstemp(tmpname); 
    f.attach(fd); 

EDIT: Vâng, điều này có thể không phải cầm tay. Nếu bạn không thể sử dụng đính kèm và không thể tạo ra một ofstream trực tiếp từ một bộ mô tả tập tin, sau đó bạn phải làm điều này:

char *tmpname = strdup("/tmp/tmpfileXXXXXX"); 
mkstemp(tmpname); 
ofstream f(tmpname); 

Như mkstemp đã tạo ra các tập tin cho bạn, điều kiện chủng tộc không nên một vấn đề ở đây.

+0

Điều đó không biên dịch với g ++ v3.4.4 của tôi trên Linux. Rõ ràng chỉ có một số nền tảng có chức năng đó. – Frank

+0

Cảm ơn! Đối với phương pháp thứ hai của bạn (bằng cách sử dụng mkstemp và sau đó ofstream): Đó là vẫn còn hiệu quả về I/O? Điều đó sẽ truy cập vào hệ thống tập tin hai lần, phải không? Hệ thống tập tin của chúng tôi là siêu chậm và tôi lo lắng rằng nó sẽ đặt một gánh nặng không cần thiết vào nó. – Frank

+0

strdup không phải là di động, hoặc là ... – Sol

16

tôi đã thực hiện chức năng này:

#include <stdlib.h> 
#include <fstream> 
#include <iostream> 
#include <vector> 

std::string open_temp(std::string path, std::ofstream& f) { 
    path += "/XXXXXX"; 
    std::vector<char> dst_path(path.begin(), path.end()); 
    dst_path.push_back('\0'); 

    int fd = mkstemp(&dst_path[0]); 
    if(fd != -1) { 
     path.assign(dst_path.begin(), dst_path.end() - 1); 
     f.open(path.c_str(), 
       std::ios_base::trunc | std::ios_base::out); 
     close(fd); 
    } 
    return path; 
} 

int main() { 
    std::ofstream logfile; 
    open_temp("/tmp", logfile); 
    if(logfile.is_open()) { 
     logfile << "hello, dude" << std::endl; 
    } 
} 

Bạn có lẽ nên hãy chắc chắn để gọi umask với một mặt nạ tạo hồ sơ hợp lệ (Tôi muốn 0600) - manpage cho mkstemp nói rằng mặt nạ tạo chế độ tập tin không được chuẩn hóa. Nó sử dụng thực tế là mkstemp sửa đổi đối số của nó thành tên tệp mà nó sử dụng. Vì vậy, chúng tôi mở nó và đóng tập tin nó mở ra (do đó, để không có nó mở hai lần), được trái với một ofstream được kết nối với tập tin đó.

+0

Tôi tự hỏi nếu nó an toàn để chỉ sử dụng std :: string như mẫu và sử dụng (char *) dst.path.c_str(). Có vẻ là tốt cho hầu hết các triển khai hợp lý của std :: string. – ididak

+0

ididak, nó không an toàn. c-string được trỏ tới không thể ghi được :) –

+0

Trong C++ 11 a std :: string có thể có mục nhập đầu tiên bị hủy đăng ký.Do đó bạn có thể vượt qua & somestring [0]; với một hàm mong đợi một char *. Xem: http://en.cppreference.com/w/cpp/string/basic_string –

2

lẽ điều này sẽ làm việc:

char tmpname[256]; 
ofstream f; 
sprintf (tmpname, "/tmp/tmpfileXXXXXX"); 
int fd = mkstemp(tmpname); 
ofstream f(tmpname); 

tôi đã không thử nó, nhưng bạn có thể kiểm tra.

0
char tempFileName[20]; // name only valid till next invocation of tempFileOpen 
ofstream tempFile; 
void tempFileOpen() 
{ 
    strcpy(tempFileName, "/tmp/XXXXXX"); 
    mkstemp(tempFileName); 
    tempFile.open(tempFileName); 
} 

Mã này cũng hoạt động với GCC/libstdC++ 6 4.8.4 và Clang 3.9. Nhờ những câu trả lời khác cũng rất hữu ích cho tôi.

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