// Проверить наличие проблем в потоках...
in.close();
out.close();
}
Можно посчитать, что копирование файла — это простая операция чтения из одного потока и записи в другой поток. Однако библиотека потоков C++ достаточно большая, и существует несколько различных способов чтения и записи потоков, поэтому надо обладать некоторыми знаниями об этой библиотеке, чтобы избежать ошибок, снижающих производительность этой операции.
Пример 10.9 работает быстро, потому что используется буферизация ввода-вывода. Функции read
и write
оперируют сразу всем содержимым буфера вместо посимвольного копирования, когда в цикле считывается символ из потока ввода в буфер и затем записывается в поток вывода. При их выполнении не делается никакого форматирования, подобного тому, которое выполняется операторами сдвига влево и вправо, что ускоряет выполнение операции. Кроме того, поскольку потоки работают в двоичном режиме, не надо специально обрабатывать символы EOF. В зависимости от используемого вами оборудования, ОС и т.д. вы получите различный результат при различных размерах буфера. Экспериментально вы можете найти наилучшие параметры для вашей системы
Однако можно добиться большего. Все потоки C++ уже буферизуют данные при их чтении и записи, поэтому в примере 10.9 фактически выполняется read
, operator<<
, getc
или любых других функций-членов, а поток вывода имеет буфер, который содержит вывод, записанный в поток, но не в «пункт назначения» (в случае применения ofstream
это файл, но могла бы быть строка, сетевое соединение и кто знает, что еще). Поэтому лучше всего обеспечить непосредственный обмен данных буферов. Вы это можете сделать с помощью оператора operator<<
, который работает иначе с буферами потоков. Например, вместо цикла do/while
приведенного в примере 10.9, используйте следующий оператор.
out << in.rdbuf();
Не следует размещать этот оператор в теле цикла, замените весь цикл одной строкой. Это выглядит немного странно, поскольку обычно оператор operator<<
говорит, «возьмите правую часть и передайте ее в поток левой части», однако, поверьте мне, эта запись имеет смысл, rdbuf
возвращает буфер потока ввода, а реализация operator<<
, принимающая буфер потока справа, считывает каждый символ буфера ввода и записывает его в буфер вывода. Когда буфер ввода заканчивается, он «знает», что должен заново заполнить себя данными из реального источника, a operator<<
ведет себя не лучше.
Пример 10.9 показывает, как можно скопировать copy_file
, которая выполняет системные вызовы ОС для той платформы, для которой она компилируется. Пример 10.10 содержит короткую программу, которая копирует файл из одного места в другое.
#include
#include
#include
#include
using namespace std;
using namespace boost::filesystem;
int main(int argc, char** argv) {
// Проверка параметров...
try {
// Преобразовать аргументы в абсолютные пути, используя «родное»
// форматирование
path src = complete(path(argv[1], native));
path dst = complete(path(argv[2], native));
copy_file(src, dst);
} catch (exception& e) {
cerr << e.what() << endl;
}
return(EXIT_SUCCESS);
}
В этой небольшой программе все же имеется несколько ключевых вопросов, которые необходимо пояснить, поскольку другие рецепты данной главы используют библиотеку Boost Filesystem. Во первых, центральным компонентом библиотеки Boost Filesystem является класс path
, описывающий независимым от ОС способом путь к файлу или каталогу. Вы можете создать path
, используя как переносимый тип строки, так и специфичный для конкретной ОС. В примере 10.10 я создаю путь path
из аргументов программы (этот путь я затем передаю функции complete
, которую мы вскоре рассмотрим).
path src = complete(path(argv[1], native));