คัดลอกไฟล์ด้วยวิธีที่ปลอดภัยและมีประสิทธิภาพ


305

ฉันค้นหาวิธีที่ดีในการคัดลอกไฟล์ (ไบนารีหรือข้อความ) ฉันเขียนหลายตัวอย่างทุกคนทำงานได้ แต่ฉันต้องการฟังความคิดเห็นของโปรแกรมเมอร์ที่มีประสบการณ์

ฉันพลาดตัวอย่างที่ดีและค้นหาวิธีการทำงานกับ C ++

ANSI-C-WAY

#include <iostream>
#include <cstdio>    // fopen, fclose, fread, fwrite, BUFSIZ
#include <ctime>
using namespace std;

int main() {
    clock_t start, end;
    start = clock();

    // BUFSIZE default is 8192 bytes
    // BUFSIZE of 1 means one chareter at time
    // good values should fit to blocksize, like 1024 or 4096
    // higher values reduce number of system calls
    // size_t BUFFER_SIZE = 4096;

    char buf[BUFSIZ];
    size_t size;

    FILE* source = fopen("from.ogv", "rb");
    FILE* dest = fopen("to.ogv", "wb");

    // clean and more secure
    // feof(FILE* stream) returns non-zero if the end of file indicator for stream is set

    while (size = fread(buf, 1, BUFSIZ, source)) {
        fwrite(buf, 1, size, dest);
    }

    fclose(source);
    fclose(dest);

    end = clock();

    cout << "CLOCKS_PER_SEC " << CLOCKS_PER_SEC << "\n";
    cout << "CPU-TIME START " << start << "\n";
    cout << "CPU-TIME END " << end << "\n";
    cout << "CPU-TIME END - START " << end - start << "\n";
    cout << "TIME(SEC) " << static_cast<double>(end - start) / CLOCKS_PER_SEC << "\n";

    return 0;
}

POSIX-WAY (K&R ใช้สิ่งนี้ใน "ภาษาการเขียนโปรแกรม C", ระดับต่ำมากขึ้น)

#include <iostream>
#include <fcntl.h>   // open
#include <unistd.h>  // read, write, close
#include <cstdio>    // BUFSIZ
#include <ctime>
using namespace std;

int main() {
    clock_t start, end;
    start = clock();

    // BUFSIZE defaults to 8192
    // BUFSIZE of 1 means one chareter at time
    // good values should fit to blocksize, like 1024 or 4096
    // higher values reduce number of system calls
    // size_t BUFFER_SIZE = 4096;

    char buf[BUFSIZ];
    size_t size;

    int source = open("from.ogv", O_RDONLY, 0);
    int dest = open("to.ogv", O_WRONLY | O_CREAT /*| O_TRUNC/**/, 0644);

    while ((size = read(source, buf, BUFSIZ)) > 0) {
        write(dest, buf, size);
    }

    close(source);
    close(dest);

    end = clock();

    cout << "CLOCKS_PER_SEC " << CLOCKS_PER_SEC << "\n";
    cout << "CPU-TIME START " << start << "\n";
    cout << "CPU-TIME END " << end << "\n";
    cout << "CPU-TIME END - START " << end - start << "\n";
    cout << "TIME(SEC) " << static_cast<double>(end - start) / CLOCKS_PER_SEC << "\n";

    return 0;
}

KISS-C ++ - Streambuffer-WAY

#include <iostream>
#include <fstream>
#include <ctime>
using namespace std;

int main() {
    clock_t start, end;
    start = clock();

    ifstream source("from.ogv", ios::binary);
    ofstream dest("to.ogv", ios::binary);

    dest << source.rdbuf();

    source.close();
    dest.close();

    end = clock();

    cout << "CLOCKS_PER_SEC " << CLOCKS_PER_SEC << "\n";
    cout << "CPU-TIME START " << start << "\n";
    cout << "CPU-TIME END " << end << "\n";
    cout << "CPU-TIME END - START " <<  end - start << "\n";
    cout << "TIME(SEC) " << static_cast<double>(end - start) / CLOCKS_PER_SEC << "\n";

    return 0;
}

คัดลอกขั้นตอนวิธีการ c ++ - WAY

#include <iostream>
#include <fstream>
#include <ctime>
#include <algorithm>
#include <iterator>
using namespace std;

int main() {
    clock_t start, end;
    start = clock();

    ifstream source("from.ogv", ios::binary);
    ofstream dest("to.ogv", ios::binary);

    istreambuf_iterator<char> begin_source(source);
    istreambuf_iterator<char> end_source;
    ostreambuf_iterator<char> begin_dest(dest); 
    copy(begin_source, end_source, begin_dest);

    source.close();
    dest.close();

    end = clock();

    cout << "CLOCKS_PER_SEC " << CLOCKS_PER_SEC << "\n";
    cout << "CPU-TIME START " << start << "\n";
    cout << "CPU-TIME END " << end << "\n";
    cout << "CPU-TIME END - START " <<  end - start << "\n";
    cout << "TIME(SEC) " << static_cast<double>(end - start) / CLOCKS_PER_SEC << "\n";

    return 0;
}

ตัวเอง BUFFER-C ++ - WAY

#include <iostream>
#include <fstream>
#include <ctime>
using namespace std;

int main() {
    clock_t start, end;
    start = clock();

    ifstream source("from.ogv", ios::binary);
    ofstream dest("to.ogv", ios::binary);

    // file size
    source.seekg(0, ios::end);
    ifstream::pos_type size = source.tellg();
    source.seekg(0);
    // allocate memory for buffer
    char* buffer = new char[size];

    // copy file    
    source.read(buffer, size);
    dest.write(buffer, size);

    // clean up
    delete[] buffer;
    source.close();
    dest.close();

    end = clock();

    cout << "CLOCKS_PER_SEC " << CLOCKS_PER_SEC << "\n";
    cout << "CPU-TIME START " << start << "\n";
    cout << "CPU-TIME END " << end << "\n";
    cout << "CPU-TIME END - START " <<  end - start << "\n";
    cout << "TIME(SEC) " << static_cast<double>(end - start) / CLOCKS_PER_SEC << "\n";

    return 0;
}

LINUX-WAY // ต้องการเคอร์เนล> = 2.6.33

#include <iostream>
#include <sys/sendfile.h>  // sendfile
#include <fcntl.h>         // open
#include <unistd.h>        // close
#include <sys/stat.h>      // fstat
#include <sys/types.h>     // fstat
#include <ctime>
using namespace std;

int main() {
    clock_t start, end;
    start = clock();

    int source = open("from.ogv", O_RDONLY, 0);
    int dest = open("to.ogv", O_WRONLY | O_CREAT /*| O_TRUNC/**/, 0644);

    // struct required, rationale: function stat() exists also
    struct stat stat_source;
    fstat(source, &stat_source);

    sendfile(dest, source, 0, stat_source.st_size);

    close(source);
    close(dest);

    end = clock();

    cout << "CLOCKS_PER_SEC " << CLOCKS_PER_SEC << "\n";
    cout << "CPU-TIME START " << start << "\n";
    cout << "CPU-TIME END " << end << "\n";
    cout << "CPU-TIME END - START " <<  end - start << "\n";
    cout << "TIME(SEC) " << static_cast<double>(end - start) / CLOCKS_PER_SEC << "\n";

    return 0;
}

สิ่งแวดล้อม

  • GNU / LINUX (Archlinux)
  • เคอร์เนล 3.3
  • GLIBC-2.15, LIBSTDC ++ 4.7 (GCC-LIBS), GCC 4.7, Coreutils 8.16
  • ใช้ RUNLEVEL 3 (ผู้ใช้หลายคน, เครือข่าย, เทอร์มินัล, ไม่มี GUI)
  • INTEL SSD-Postville 80 GB เติมได้สูงสุด 50%
  • คัดลอก OGG-VIDEO-FILE ขนาด 270 MB

ขั้นตอนในการทำซ้ำ

 1. $ rm from.ogg
 2. $ reboot                           # kernel and filesystem buffers are in regular
 3. $ (time ./program) &>> report.txt  # executes program, redirects output of program and append to file
 4. $ sha256sum *.ogv                  # checksum
 5. $ rm to.ogg                        # remove copy, but no sync, kernel and fileystem buffers are used
 6. $ (time ./program) &>> report.txt  # executes program, redirects output of program and append to file

ผลลัพธ์ (ใช้ CPU TIME)

Program  Description                 UNBUFFERED|BUFFERED
ANSI C   (fread/frwite)                 490,000|260,000  
POSIX    (K&R, read/write)              450,000|230,000  
FSTREAM  (KISS, Streambuffer)           500,000|270,000 
FSTREAM  (Algorithm, copy)              500,000|270,000
FSTREAM  (OWN-BUFFER)                   500,000|340,000  
SENDFILE (native LINUX, sendfile)       410,000|200,000  

ขนาดไฟล์ไม่เปลี่ยนแปลง
sha256sum พิมพ์ผลลัพธ์เดียวกัน
ไฟล์วิดีโอยังคงเล่นได้

คำถาม

  • คุณต้องการวิธีใด
  • คุณรู้วิธีแก้ปัญหาที่ดีกว่าหรือไม่?
  • คุณเห็นข้อผิดพลาดในรหัสของฉันหรือไม่?
  • คุณรู้เหตุผลในการหลีกเลี่ยงทางออกหรือไม่?

  • FSTREAM (KISS, Streambuffer)
    ฉันชอบอันนี้มากเพราะมันสั้นและเรียบง่าย เท่าที่ฉันรู้ตัวดำเนินการ << ถูกโอเวอร์โหลดสำหรับ rdbuf () และไม่แปลงอะไรเลย แก้ไข?

ขอบคุณ

ปรับปรุง 1
ผมเปลี่ยนแหล่งที่มาในทุกตัวอย่างในทางที่ว่าเปิดและปิดอธิบายไฟล์คือรวมไว้ในวัดของนาฬิกา () ไม่มีการเปลี่ยนแปลงที่สำคัญอื่น ๆ ในซอร์สโค้ด ผลลัพธ์จะไม่เปลี่ยนแปลง! ฉันใช้เวลาในการตรวจสอบผลลัพธ์อีกครั้ง

อัปเดต 2
ANSI C ตัวอย่างเปลี่ยนแปลง: เงื่อนไขของwhile-loopไม่ได้เรียกfeof ()อีกต่อไปแทนที่จะฉันย้ายfread ()เข้าสู่เงื่อนไข ดูเหมือนว่าโค้ดจะรัน 10,000 นาฬิกาเร็วขึ้น

การวัดมีการเปลี่ยนแปลง: ผลลัพธ์ในอดีตถูกบัฟเฟอร์เสมอเพราะฉันทำซ้ำบรรทัดคำสั่งเก่าrm to.ogv && sync && time ./programสำหรับแต่ละโปรแกรมสองสามครั้ง ตอนนี้ฉันรีบูตระบบสำหรับทุกโปรแกรม ผลลัพธ์ที่ไม่มีข้อผิดพลาดเป็นเรื่องใหม่และไม่แปลกใจเลย ผลลัพธ์ที่ไม่มีข้อผิดพลาดไม่ได้เปลี่ยนแปลงอย่างแท้จริง

หากฉันไม่ลบสำเนาเก่าโปรแกรมจะตอบสนองต่างกัน การเขียนทับไฟล์ที่บัฟเฟอร์ที่มีอยู่นั้นเร็วขึ้นด้วย POSIX และ SENDFILE โปรแกรมอื่น ๆ ทั้งหมดจะช้าลง บางทีตัวเลือกตัดหรือสร้างอาจมีผลกระทบต่อพฤติกรรมนี้ แต่การเขียนทับไฟล์ที่มีอยู่ด้วยสำเนาเดียวกันไม่ใช่กรณีการใช้งานจริง

การทำสำเนาด้วยcpจะใช้เวลาบัฟเฟอร์ 0.44 วินาทีและบัฟเฟอร์ไม่ถูกบัฟเฟอร์ 0.30 วินาที ดังนั้นซีพีเป็นเล็กน้อยช้ากว่าตัวอย่าง POSIX ดูดีสำหรับฉัน

บางทีฉันอาจเพิ่มตัวอย่างและผลลัพธ์ของmmap ()และcopy_file()จาก boost :: filesystem

อัปเดต 3
ฉันใส่สิ่งนี้ลงในหน้าบล็อกและขยายออกไปเล็กน้อย รวมถึงsplice ()ซึ่งเป็นฟังก์ชั่นระดับต่ำจากเคอร์เนล Linux บางทีตัวอย่างเพิ่มเติมกับ Java จะตามมา http://www.ttyhoney.com/blog/?page_id=69


5
fstreamเป็นตัวเลือกที่ดีสำหรับการทำงานของไฟล์
chris


28
คุณลืมวิธีขี้เกียจ: ระบบ ("cp from.ogv to.ogv");
fbafelipe

3
#include <copyfile.h> copyfile(const char *from, const char *to, copyfile_state_t state, copyfile_flags_t flags);
Martin York

3
ขออภัยที่มีการจัดส่งล่าช้า แต่ฉันจะไม่อธิบายสิ่งเหล่านี้ว่า 'ปลอดภัย' เนื่องจากไม่มีข้อผิดพลาดในการจัดการ
Richard Kettlewell

คำตอบ:


259

คัดลอกไฟล์อย่างมีสติ:

#include <fstream>

int main()
{
    std::ifstream  src("from.ogv", std::ios::binary);
    std::ofstream  dst("to.ogv",   std::ios::binary);

    dst << src.rdbuf();
}

ง่ายและใช้งานง่ายในการอ่านมีค่าใช้จ่ายเพิ่มเติม ถ้าเราทำมันเยอะเราควรถอยการเรียกใช้ระบบปฏิบัติการไปยังระบบไฟล์ ฉันแน่ใจว่าboostมีวิธีการคัดลอกไฟล์ในระดับระบบไฟล์

มีวิธี C สำหรับโต้ตอบกับระบบไฟล์:

#include <copyfile.h>

int
copyfile(const char *from, const char *to, copyfile_state_t state, copyfile_flags_t flags);

28
copyfileไม่พกพา; ฉันคิดว่ามันเฉพาะกับ Mac OS X แน่นอนว่ามันไม่มีอยู่บน Linux boost::filesystem::copy_fileอาจเป็นวิธีพกพาที่สุดในการคัดลอกไฟล์ผ่านระบบไฟล์เนทิฟ
Mike Seymour

4
@MikeSeymour: copyfile () น่าจะเป็นส่วนขยาย BSD
Martin York

10
@ duedl0r: ไม่วัตถุมี destructors ตัวทำลายสำหรับสตรีมจะเรียก close () โดยอัตโนมัติ codereview.stackexchange.com/q/540/507
Martin York

11
@ duedl0r: ใช่ แต่นั่นก็เหมือนกับการพูดว่า "ถ้าพระอาทิตย์ตกดิน" คุณสามารถวิ่งไปทางตะวันตกอย่างรวดเร็วและอาจทำให้วันของคุณยาวขึ้นเล็กน้อย แต่พระอาทิตย์กำลังจะตก จนกว่าคุณจะมีข้อผิดพลาดและหน่วยความจำรั่ว (มันจะออกนอกขอบเขต) แต่เนื่องจากไม่มีการจัดการหน่วยความจำแบบไดนามิกที่นี่จึงไม่สามารถรั่วไหลและพวกเขาจะออกนอกขอบเขต (เช่นเดียวกับที่พระอาทิตย์จะตกดิน)
Martin York

6
จากนั้นก็หุ้มมันในบล็อกขอบเขต {}
paulm

62

ด้วย C ++ 17 วิธีมาตรฐานในการคัดลอกไฟล์จะรวมถึง<filesystem>ส่วนหัวและการใช้:

bool copy_file( const std::filesystem::path& from,
                const std::filesystem::path& to);

bool copy_file( const std::filesystem::path& from,
                const std::filesystem::path& to,
                std::filesystem::copy_options options);

แบบฟอร์มแรกเทียบเท่ากับแบบที่สองที่copy_options::noneใช้เป็นตัวเลือก (ดูเพิ่มเติมcopy_file)

filesystemห้องสมุดถูกพัฒนามาเป็นboost.filesystemและในที่สุดก็รวมมาตรฐาน ISO C ++ เป็นของ C ++ 17


2
ทำไมมีไม่ฟังก์ชั่นเดียวกับการโต้เถียงเริ่มต้นเช่นbool copy_file( const std::filesystem::path& from, const std::filesystem::path& to, std::filesystem::copy_options options = std::filesystem::copy_options::none);?
Jepessen

2
@Jepessen ฉันไม่แน่ใจเกี่ยวกับเรื่องนี้ บางทีมันอาจจะไม่ได้เรื่องจริงๆ
manlio

@Jepessen ในไลบรารีมาตรฐานรหัสสะอาดเป็นสิ่งสำคัญยิ่ง การมีโอเวอร์โหลด (ตรงข้ามกับฟังก์ชันหนึ่งที่มีพารามิเตอร์เริ่มต้น) ทำให้ความตั้งใจของโปรแกรมเมอร์ชัดเจนยิ่งขึ้น
Marc.2377

@Peter ตอนนี้น่าจะเป็นคำตอบที่ยอมรับเนื่องจาก C ++ 17 พร้อมใช้งาน
Martin York

21

มากเกินไป!

วิธีบัฟเฟอร์ "ANSI C" ซ้ำซ้อนเนื่องจาก a FILEถูกบัฟเฟอร์แล้ว (ขนาดของบัฟเฟอร์ภายในนี้เป็นสิ่งที่BUFSIZกำหนดจริง)

"OWN-BUFFER-C ++ - WAY" จะช้าลงเมื่อผ่านไปfstreamซึ่งจะทำการส่งข้อมูลเสมือนจริงเป็นจำนวนมากและยังคงรักษาบัฟเฟอร์ภายในหรือวัตถุแต่ละกระแสอีกครั้ง ("COPY-ALGORITHM-C ++ - WAY" ไม่ประสบปัญหานี้เนื่องจากstreambuf_iteratorคลาสจะข้ามชั้นเลเยอร์)

ฉันชอบ "COPY-ALGORITHM-C ++ - WAY" แต่ไม่มีการfstreamสร้างเพียงแค่สร้างstd::filebufอินสแตนซ์เปล่าเมื่อไม่จำเป็นต้องฟอร์แมตจริง

สำหรับประสิทธิภาพแบบดิบคุณไม่สามารถเอาชนะตัวอธิบายไฟล์ POSIX ได้ มันน่าเกลียด แต่พกพาสะดวกและรวดเร็วในทุกแพลตฟอร์ม

วิธี Linux ดูเหมือนจะรวดเร็วอย่างไม่น่าเชื่อ - ระบบปฏิบัติการอาจให้ฟังก์ชันส่งคืนก่อนที่ I / O จะเสร็จสิ้นหรือไม่ ไม่ว่าในกรณีใด ๆ มันไม่สามารถพกพาได้เพียงพอสำหรับแอพพลิเคชั่นมากมาย

แก้ไข : อ่า "native Linux" อาจปรับปรุงประสิทธิภาพโดยการอ่านและเขียนด้วย I / O แบบอะซิงโครนัส การปล่อยให้คำสั่งกองพะเนินเทินทึกสามารถช่วยให้ไดรเวอร์ดิสก์ตัดสินใจได้ว่าควรหาเมื่อใด คุณอาจลอง Boost Asio หรือ pthreads เพื่อเปรียบเทียบ สำหรับ "ไม่สามารถเอาชนะตัวอธิบายไฟล์ POSIX" ... นั่นเป็นเรื่องจริงถ้าคุณทำอะไรกับข้อมูลไม่ใช่แค่คัดลอกสุ่มสี่สุ่มห้า


ANSI C: แต่ฉันต้องให้ฟังก์ชั่น fread / fwrite ขนาดเหรอ? pubs.opengroup.org/onlinepubs/9699919799/toc.htm
ปีเตอร์

@ PeterWeber ใช่แล้วมันเป็นความจริงที่ว่า BUFSIZ นั้นมีคุณค่าดีพอ ๆ กันและอาจจะเพิ่มความเร็วให้เร็วขึ้นเมื่อเทียบกับอักขระหนึ่งตัวหรือ "เพียงไม่กี่ตัว" ในแต่ละครั้ง อย่างไรก็ตามการวัดประสิทธิภาพแสดงให้เห็นว่าไม่ใช่วิธีที่ดีที่สุดในทุกกรณี
Potatoswatter

1
ฉันไม่เข้าใจอย่างถ่องแท้เกี่ยวกับเรื่องนี้ดังนั้นฉันจึงควรระมัดระวังสมมติฐานและความคิดเห็น Linux-Way ทำงานใน Kernelspace afaik สิ่งนี้ควรหลีกเลี่ยงการสลับบริบทช้าระหว่าง Kernelspace และ Userspace หรือไม่ พรุ่งนี้ฉันจะดู manpage ของ sendfile อีกครั้ง เมื่อไม่นานมานี้ Linus Torvalds กล่าวว่าเขาไม่ชอบ Userspace-Filesystems สำหรับงานหนัก บางที sendfile เป็นตัวอย่างที่ดีสำหรับมุมมองของเขา?
ปีเตอร์

5
" sendfile()คัดลอกข้อมูลระหว่าง descriptor ไฟล์หนึ่งกับอีกไฟล์หนึ่งเนื่องจากการคัดลอกนี้ทำภายในเคอร์เนลsendfile()จะมีประสิทธิภาพมากกว่าการรวมกันของread(2)และwrite(2)ซึ่งจะต้องมีการถ่ายโอนข้อมูลไปยังและจากพื้นที่ผู้ใช้": kernel.org/doc/man-pages /online/pages/man2/sendfile.2.html
Max Lybbert

1
คุณสามารถโพสต์ตัวอย่างการใช้วัตถุดิบได้filebufหรือไม่
Kerrek SB

14

ฉันต้องการที่จะทำให้บันทึกที่สำคัญมากว่าวิธีการ Linux ที่ใช้ sendfile () มีปัญหาที่สำคัญในการที่มันไม่สามารถคัดลอกไฟล์ที่มีขนาดเกิน 2GB! ฉันใช้มันตามคำถามนี้และกำลังตีปัญหาเพราะฉันใช้มันเพื่อคัดลอกไฟล์ HDF5 ที่มีขนาด GB จำนวนมาก

http://man7.org/linux/man-pages/man2/sendfile.2.html

sendfile () จะถ่ายโอนได้สูงสุด 0x7ffff000 (2,147,479,552) ไบต์ส่งคืนจำนวนไบต์ที่ถ่ายโอนจริง (สิ่งนี้เป็นจริงทั้งในระบบ 32- บิตและ 64- บิต)


1
sendfile64 () มีปัญหาเดียวกันหรือไม่
graywolf

1
@ Paladin ดูเหมือนว่า sendfile64 ได้รับการพัฒนาเพื่อหลีกเลี่ยงข้อ จำกัด นี้ จากหน้า man: "" "การเรียกระบบ Linux sendfile () ดั้งเดิมไม่ได้ถูกออกแบบมาเพื่อจัดการกับการออฟเซ็ตไฟล์ขนาดใหญ่ดังนั้น Linux 2.4 ได้เพิ่ม sendfile64 () พร้อมชนิดที่กว้างขึ้นสำหรับอาร์กิวเมนต์ offset ฟังก์ชัน glibc sendfile () wrapper จัดการกับความแตกต่างของเคอร์เนลอย่างโปร่งใส "" "
rveale

sendfile64 มีปัญหาเดียวกันกับที่ดูเหมือน อย่างไรก็ตามการใช้ประเภทออฟเซทoff64_tอนุญาตให้หนึ่งใช้วนรอบเพื่อคัดลอกไฟล์ขนาดใหญ่ตามที่แสดงในคำตอบของคำถามที่เชื่อมโยง
pcworld

สิ่งนี้เกิดขึ้นในมนุษย์: 'โปรดทราบว่าการเรียก sendfile () ที่ประสบความสำเร็จอาจเขียนจำนวนไบต์น้อยกว่าที่ร้องขอ ผู้โทรควรเตรียมพร้อมที่จะลองโทรอีกครั้งหากมีไบต์ที่ยังไม่ส่ง ' sendfile หรือ sendfile64 อาจต้องมีการเรียกภายในลูปจนกว่าจะทำสำเนาแบบเต็ม
philippe lhardy

2

Qt มีวิธีการคัดลอกไฟล์:

#include <QFile>
QFile::copy("originalFile.example","copiedFile.example");

โปรดทราบว่าในการใช้สิ่งนี้คุณต้องติดตั้ง Qt (คำแนะนำที่นี่ ) และรวมไว้ในโครงการของคุณ (หากคุณใช้ Windows และคุณไม่ใช่ผู้ดูแลระบบคุณสามารถดาวน์โหลด Qt ได้ที่นี่แทน) ดูคำตอบนี้ด้วย


1
QFile::copyเป็นขันช้าเนื่องจากเป็น4k บัฟเฟอร์
Nicolas Holthaus

1
Qtช้าได้รับการแก้ไขในรุ่นใหม่ของ ฉันกำลังใช้งาน5.9.2และความเร็วก็ใกล้เคียงกับการใช้งานจริง Btw การดูซอร์สโค้ด Qt ดูเหมือนจะเรียกการใช้งานจริง
VK

1

สำหรับผู้ที่ชอบเพิ่ม:

boost::filesystem::path mySourcePath("foo.bar");
boost::filesystem::path myTargetPath("bar.foo");

// Variant 1: Overwrite existing
boost::filesystem::copy_file(mySourcePath, myTargetPath, boost::filesystem::copy_option::overwrite_if_exists);

// Variant 2: Fail if exists
boost::filesystem::copy_file(mySourcePath, myTargetPath, boost::filesystem::copy_option::fail_if_exists);

โปรดทราบว่าการเพิ่ม :: ระบบแฟ้ม :: เส้นทางยังมีอยู่ในwpathสำหรับ Unicode และคุณสามารถใช้

using namespace boost::filesystem

หากคุณไม่ชอบชื่อแบบยาวเหล่านั้น


ไลบรารีระบบไฟล์ของ Boost เป็นหนึ่งในข้อยกเว้นที่ต้องการให้รวบรวม เพียงแค่ FYI!
SimonC

0

ฉันไม่แน่ใจว่าการคัดลอกไฟล์เป็น "วิธีที่ดี" แต่สมมติว่า "ดี" หมายถึง "เร็ว" ฉันสามารถขยายหัวเรื่องได้เล็กน้อย

ระบบปฏิบัติการปัจจุบันได้รับการปรับให้เหมาะสมเพื่อจัดการกับการคัดลอกไฟล์มิล ไม่มีโค้ดอันชาญฉลาดที่จะเอาชนะได้ อาจเป็นไปได้ว่าเทคนิคการทำสำเนาของคุณแตกต่างกันจะพิสูจน์ได้เร็วขึ้นในบางสถานการณ์การทดสอบ

โดยทั่วไปแล้วsendfileฟังก์ชั่นอาจกลับมาก่อนที่จะเขียนได้รับมอบหมายจึงทำให้การแสดงผลของการเป็นเร็วกว่าที่เหลือ ฉันยังไม่ได้อ่านรหัส แต่แน่นอนที่สุดเพราะมันจัดสรรบัฟเฟอร์เฉพาะของตัวเองหน่วยความจำการซื้อขายสำหรับเวลา และสาเหตุที่ใช้ไม่ได้กับไฟล์ที่มีขนาดใหญ่กว่า 2Gb

ตราบใดที่คุณจัดการกับไฟล์จำนวนน้อยทุกอย่างจะเกิดขึ้นในบัฟเฟอร์ต่าง ๆ (รันไทม์ C ++ เป็นครั้งแรกถ้าคุณใช้iostream, ไฟล์ภายในของ OS, เห็นได้ชัดว่าเป็นบัฟเฟอร์เสริมขนาดไฟล์ในกรณีของsendfile) การเข้าถึงสื่อจัดเก็บข้อมูลจริงจะทำได้ก็ต่อเมื่อมีการย้ายข้อมูลมากพอที่จะทำให้เกิดปัญหาในการหมุนฮาร์ดดิสก์

ฉันคิดว่าคุณสามารถปรับปรุงการแสดงได้เล็กน้อยในบางกรณี ปิดส่วนหัวของฉัน:

  • หากคุณกำลังคัดลอกไฟล์ขนาดใหญ่บนดิสก์เดียวกันการใช้บัฟเฟอร์ที่มีขนาดใหญ่กว่าของระบบปฏิบัติการอาจปรับปรุงสิ่งต่าง ๆ เล็กน้อย (แต่เราอาจพูดถึงกิกะไบต์ที่นี่)
  • หากคุณต้องการคัดลอกไฟล์เดียวกันบนปลายทางทางกายภาพที่แตกต่างกันสองแห่งคุณอาจจะเปิดไฟล์สามไฟล์พร้อมกันได้เร็วกว่าเรียกสองไฟล์copy_fileตามลำดับ (แม้ว่าคุณจะสังเกตเห็นความแตกต่างได้ยากตราบใดที่ไฟล์นั้นพอดีกับแคชของระบบปฏิบัติการ)
  • หากคุณกำลังจัดการกับไฟล์ขนาดเล็กจำนวนมากบน HDD คุณอาจต้องการอ่านไฟล์เหล่านั้นเป็นชุดเพื่อลดเวลาในการค้นหา (แม้ว่าระบบปฏิบัติการจะแคชรายการไดเรกทอรีไว้แล้วเพื่อหลีกเลี่ยงการค้นหาไฟล์ที่บ้าและไฟล์เล็ก ๆ

แต่สิ่งที่อยู่นอกขอบเขตของฟังก์ชันคัดลอกไฟล์วัตถุประสงค์ทั่วไป

ดังนั้นในความเห็นของโปรแกรมเมอร์รุ่นเก๋าของฉันการคัดลอกไฟล์ C ++ ควรใช้file_copyฟังก์ชั่นเฉพาะของC ++ 17 ยกเว้นว่าเป็นที่รู้จักกันมากขึ้นเกี่ยวกับบริบทที่การคัดลอกไฟล์เกิดขึ้นและกลยุทธ์ที่ชาญฉลาดบางอย่าง

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.