Как скопировать файл c
Обновлено: 21.11.2024
Функция CopyFileEx предоставляет две дополнительные возможности. CopyFileEx может вызывать указанную функцию обратного вызова каждый раз, когда завершается часть операции копирования, а CopyFileEx может быть отменен во время операции копирования.
Чтобы выполнить эту операцию как транзакционную, используйте функцию CopyFileTransacted.
Синтаксис
Параметры
Имя существующего файла.
В версии ANSI этой функции имя ограничено символами MAX_PATH. Чтобы увеличить это ограничение до 32 767 широких символов, вызовите Unicode-версию функции и добавьте "\?" к пути. Дополнительную информацию см. в разделе Именование файла.
Совет. Начиная с Windows 10 версии 1607, для юникодной версии этой функции (CopyFileW) вы можете снять ограничение MAX_PATH без добавления "\\?\". Дополнительные сведения см. в разделе «Ограничение максимальной длины пути» статьи «Именование файлов, путей и пространств имен».
Имя нового файла.
В версии ANSI этой функции имя ограничено символами MAX_PATH. Чтобы увеличить это ограничение до 32 767 широких символов, вызовите Unicode-версию функции и добавьте "\?" к пути. Дополнительную информацию см. в разделе Именование файла.
Совет. Начиная с Windows 10 версии 1607, для юникодной версии этой функции (CopyFileW) вы можете снять ограничение MAX_PATH без добавления "\\?\". Дополнительные сведения см. в разделе «Ограничение максимальной длины пути» статьи «Именование файлов, путей и пространств имен».
Если этот параметр имеет значение TRUE, а новый файл, указанный параметром lpNewFileName, уже существует, функция завершается ошибкой. Если этот параметр имеет значение FALSE, а новый файл уже существует, функция перезаписывает существующий файл и завершается успешно.
Возвращаемое значение
Если функция завершается успешно, возвращаемое значение не равно нулю.
Если функция завершается ошибкой, возвращаемое значение равно нулю. Чтобы получить расширенную информацию об ошибке, вызовите GetLastError.
Примечания
Свойства ресурса безопасности (ATTRIBUTE_SECURITY_INFORMATION) для существующего файла копируются в новый файл.
Windows 7, Windows Server 2008 R2, Windows Server 2008, Windows Vista, Windows Server 2003 и Windows XP: свойства ресурсов безопасности для существующего файла не копируются в новый файл до Windows 8 и Windows Server 2012.
Атрибуты существующего файла копируются в новый файл. Например, если существующий файл имеет файловый атрибут FILE_ATTRIBUTE_READONLY, копия, созданная с помощью вызова CopyFile, также будет иметь файловый атрибут FILE_ATTRIBUTE_READONLY. Дополнительные сведения см. в разделе Получение и изменение атрибутов файла.
Эта функция завершается с ошибкой ERROR_ACCESS_DENIED, если конечный файл уже существует и для него установлен атрибут FILE_ATTRIBUTE_HIDDEN или FILE_ATTRIBUTE_READONLY.
Когда CopyFile используется для копирования зашифрованного файла, он пытается зашифровать целевой файл с помощью ключей, используемых при шифровании исходного файла. Если это невозможно сделать, эта функция пытается зашифровать целевой файл ключами по умолчанию. Если ни один из этих методов не может быть выполнен, CopyFile завершается с ошибкой ERROR_ENCRYPTION_FAILED.
Поведение символической ссылки. Если исходный файл является символической ссылкой, фактически скопированный файл является целью символической ссылки.
Если конечный файл уже существует и является символической ссылкой, цель символической ссылки перезаписывается исходным файлом.
В Windows 8 и Windows Server 2012 эта функция поддерживается следующими технологиями.
Я ищу Unix-эквивалент Win32 CopyFile, я не хочу изобретать велосипед и писать свою собственную версию.
Чтобы не изобретать велосипед, скомпилируйте GNU coreutils, насколько я знаю, у него есть статическая библиотека для копирования файлов в дереве сборки, используемая cp и другими. Поддерживает разреженность и btrfs cow
12 ответов 12
Нет необходимости ни вызывать непереносимые API, такие как sendfile , ни обращаться к внешним утилитам. Тот же метод, который работал еще в 70-х годах, работает и сейчас:
@Caf: Боже мой. идти к. :) В любом случае ваш код более разумен, чем мой. ;) Старый цикл с чтением/записью самый переносимый. +1 от меня.
Не подходит для общего использования. Копия файла — это больше, чем просто поток данных. Как насчет разреженных файлов или расширенных атрибутов? Вот еще раз, почему Windows API настолько уродлив, насколько это возможно, превосходит Linux
@Lothar Файлы Unix концептуально представляют собой просто последовательность байтов. Метаданные, такие как разрешения, ACL и т. д., обрабатываются ортогонально фактическому копированию данных. Как они должны быть. Форматы файлов, характерные для приложения, являются проблемой приложения. Как и должно быть.
В API нет встроенной эквивалентной функции CopyFile. Но sendfile можно использовать для копирования файла в режиме ядра, что является более быстрым и лучшим решением (по многим причинам), чем открытие файла, циклическое чтение его в буфер и запись вывода в другой файл.
Начиная с ядра Linux версии 2.6.33 ограничение, требующее, чтобы вывод sendfile был сокетом, было снято, и исходный код будет работать как в Linux, так и в OS X 10.9 Mavericks, sendfile теперь в OS X требует, чтобы вывод был сокетом, и код не будет работать!
Следующий фрагмент кода должен работать на большинстве OS X (начиная с 10.5), (Free)BSD и Linux (начиная с 2.6.33). Реализация является «нулевой копией» для всех платформ, что означает, что все это делается в пространстве ядра, и нет копирования буферов или данных в пространство пользователя и из него. Практически лучшая производительность, которую вы можете получить.
EDIT: Заменено открытие пункта назначения вызовом creat(), так как мы хотим, чтобы был указан флаг O_TRUNC. См. комментарий ниже.
Согласно справочной странице, выходным аргументом sendfile должен быть сокет. Вы уверены, что это работает?
Что касается Linux, Джей Конрод прав: out_fd файла sendfile может быть обычным файлом в ядрах 2.4, но теперь он должен поддерживать внутренний API ядра sendpage (что по сути означает канал или сокет). sendpage реализован по-разному в разных UNIX - для него нет стандартной семантики.
Прототип под Linux отличается от OSX, поэтому вы могли подумать (и я тоже так подумал), когда я увидел вашу реализацию и дополнительные параметры для файла отправки. это зависит от платформы — об этом стоит помнить!
К вашему сведению, вы можете сэкономить много работы, если (PathsMatch(source, target)) return 1; /* где PathsMatch — это подходящая процедура сравнения путей для локали */, иначе я полагаю, что второе открытие завершится ошибкой.
+1 man sendfile говорит, что начиная с версии 2.6.33 это снова поддерживается. sendfile() превосходит CopyFile(), так как допускает смещение. Это полезно для удаления информации заголовка из файла.
Проще всего использовать fork/execl для запуска cp, который сделает всю работу за вас. Это имеет преимущество перед системой в том, что она не подвержена атаке Bobby Tables, и вам не нужно очищать аргументы в той же степени. Кроме того, поскольку system() требует, чтобы вы объединили аргумент команды, у вас вряд ли возникнет проблема переполнения буфера из-за небрежной проверки sprintf().
Преимущество прямого вызова cp вместо его записи заключается в том, что вам не нужно беспокоиться об элементах целевого пути, существующих в месте назначения. Делать это в самостоятельном коде чревато ошибками и утомительно.
Я написал этот пример на ANSI C и заглушил только простейшую обработку ошибок, кроме того, что это простой код.
+1 за еще один длинный и подробный рассказ. Действительно заставляет вас оценить «векторную»/списковую форму system() в Perl. Хм. Возможно, неплохо было бы иметь системную функцию с массивом argv.
<р>. в конце концов, это было реализовано 17 лет назад в glibc и являлось стандартной функцией за 10 ушей до того, как был написан ваш ответ..Еще один вариант функции копирования с использованием обычных вызовов POSIX и без цикла. Код, вдохновленный вариантом буферной копии ответа caf. Предупреждение: использование mmap может легко привести к сбою в 32-битных системах, в 64-битных системах опасность менее вероятна.
(Я понимаю, что это старый вопрос, но.) Что произойдет с mmap, когда размер отображаемого файла очень велик по сравнению с размером доступной памяти и файла подкачки? Будет ли зависать система из-за нехватки памяти/подкачки?
Сопоставление файла с диапазоном адресов процесса само по себе не требует памяти. Это как если бы вы сказали, что ваш файл теперь является частью пространства подкачки. Это означает, что когда вы обращаетесь к адресу в вашем сопоставленном файле, он сначала генерирует ошибку страницы, поскольку в памяти ничего нет. Затем ОС загружает соответствующую страницу по этому адресу с диска и восстанавливает управление процессом. Если доступной памяти не будет, ОС просто освободит некоторые другие отображаемые страницы из любого другого процесса; в приоритете чистые страницы (т.е. те, которые не нужно записывать на диск), но и грязные страницы. =>р>
Подкачка происходит, когда шаблон доступа к отображаемым страницам превышает объем физической памяти в системе, и ей приходится все время читать и записывать страницы. mmap можно рассматривать как не что иное, как просто увеличение области подкачки систем. mmap с параметром MAP_SHARED также можно рассматривать как способ сделать кэш файлов доступным для процесса.
Поэтому, если вы ммапируете большой файл, а затем получаете доступ к его большому количеству, а объем файла, к которому вы обращаетесь, превышает вашу реальную память, ОС начнет подкачку других процессов. Если это произойдет слишком часто, ОС начнет зависать при обмене. Я хочу сказать, что с файлами, большими по отношению к памяти + подкачке, вы должны думать о размере данных mmap, к которым осуществляется доступ, чтобы не вызывать проблем
Есть способ сделать это, не прибегая к системному вызову, вам нужно включить обертку примерно так:
Приведенный выше пример (проверка ошибок опущена!) использует open , close и sendfile .
Я не уверен на 100% в прототипе sendfile, я думаю, что ошибся в одном из параметров. Пожалуйста, имейте это в виду. :)
У вас есть состояние гонки — файл, который вы открыли как fdSource, и файл, который вы stat(), не обязательно совпадают.
@caf: Можете ли вы дать более подробную информацию, как я смотрю на это, и как может быть состояние гонки? Я внесу соответствующие поправки в ответ... спасибо, что сообщили мне.
tommbieb75: Просто — между вызовом open() и вызовом stat() кто-то другой мог переименовать файл и поместить другой файл под этим именем — поэтому вы скопируете данные из первого файла, но используя длина второго.
@caf: Боже мой. почему я не подумал об этом. хорошо подмечено. блокировка должна помочь с исходным файлом. молодец, что заметил это. состояние гонки.. ну я никогда. как говорит Клинт Иствуд в «Гран Торино»: «Джей Си всю пятницу. '
Побайтовое копирование файлов работает, но в современных UNIX это медленно и расточительно. Современные UNIX имеют поддержку «копирования при записи», встроенную в файловую систему: системный вызов создает новую запись каталога, указывающую на существующие байты на диске, и никакие байты содержимого файла на диске не затрагиваются до тех пор, пока одна из копий не будет изменена. , после чего на диск записываются только измененные блоки. Это позволяет практически мгновенно копировать файлы, не используя дополнительные файловые блоки, независимо от размера файла. Например, вот некоторые подробности о том, как это работает в xfs.
В macOS используйте clonefile(2) для мгновенных копий на томах APFS. Это то, что использует Apple cp -c. Документы не совсем ясны, но вполне вероятно, что copyfile(3) с COPYFILE_CLONE также использует это. Оставьте комментарий, если хотите, чтобы я это проверил.
Если эти операции копирования при записи не поддерживаются (устарела ли ОС, не поддерживает ли базовая файловая система или потому, что вы копируете файлы между разными файловыми системами), вам нужно вернуться к попытке sendfile или, в крайнем случае, побайтовое копирование. Но чтобы сэкономить всем много времени и дискового пространства, сначала попробуйте FICLONE и clonefile(2).
Один из вариантов заключается в том, что вы можете использовать system() для выполнения cp . Это просто повторно использует команду cp(1) для выполнения работы. Если вам нужно только сделать еще одну ссылку на файл, это можно сделать с помощью link() или symlink() .
Правда? Вы бы использовали это в производственном коде? Я не могу придумать веской причины не делать этого, но это не кажется мне чистым решением.
Если вы укажете путь к /bin/cp, вы в относительной безопасности, если только злоумышленнику не удалось скомпрометировать систему до такой степени, что он может вносить изменения в произвольные утилиты системной оболочки в /bin. Если они скомпрометировали систему до такой степени, у вас гораздо большие проблемы.
Использование системы для запуска команд довольно распространено в Unix-стране. При надлежащей гигиене он может быть достаточно безопасным и надежным. В конце концов, команды предназначены для использования таким образом.
Я вижу, никто еще не упомянул copy_file_range, поддерживаемый по крайней мере в Linux и FreeBSD. Преимущество этого заключается в том, что он явно документирует возможность использования методов CoW, таких как рефлинки. Цитата:
copy_file_range() дает файловым системам возможность реализовать методы «ускорения копирования», такие как использование рефлинков (т. е. два или более индексных дескриптора, которые совместно используют указатели на одни и те же блоки диска копирования при записи). ) или копировать на стороне сервера (в случае NFS).
FWIW, я не уверен, что старый файл отправки может это сделать. Несколько упоминаний, которые я нашел, утверждают, что это не так. В этом смысле copy_file_range превосходит sendfile .
Ниже приведен пример использования вызова (который дословно скопирован из руководства). Я также проверил, что после использования этого кода для копирования двоичного файла bash в файловой системе BTRFS копия повторно связывается с исходным (я сделал это, вызвав duperemove для файлов и увидев пропуск — экстенты уже дедуплицированы. сообщения) < /эм>.
Добавьте проверку ошибок.
В противном случае откройте оба и зациклите чтение/запись, но, возможно, не то, что вам нужно.
ОБНОВЛЕНИЕ для решения действительных проблем безопасности:
Вместо использования "system()" выполните fork/wait и вызовите execv() или execl() в дочернем элементе.
Это не работает для файлов, в имени которых есть пробелы (или кавычки, обратная косая черта, знак доллара и т. д.). Я довольно часто использую пробелы в именах файлов.
Хорошо, это швейцарский сыр (см. действительные проблемы безопасности в комментариях в другом месте), но если у вас есть относительно контролируемая среда, это может быть полезно.
У вас есть уязвимость внедрения шелл-кода, если вы неправильно обрабатываете одинарные кавычки в значениях old или new . Немного больше усилий по использованию fork и собственному exec может избежать всех этих проблем с цитированием.
Да, просто очевидно и неправильно во многих случаях. Вот почему я проголосовал за некоторые из наиболее сложных примеров.
Работает в Windows и Linux.
cp() ничего не возвращает, но имеет тип int , что может вызвать проблемы и даже может быть UB.
Хороший вопрос. В связи с другим хорошим вопросом:
Есть два подхода к "самой простой" реализации cp. Один из подходов использует какую-то функцию системного вызова для копирования файлов — наиболее близкую к Cp-версии команды Unix cp. Другой подход использует буфер и функции системного вызова чтения/записи либо напрямую, либо с помощью оболочки FILE.
Вероятно, системные вызовы копирования файлов, которые выполняются исключительно в памяти ядра, быстрее, чем системные вызовы, которые выполняются как в памяти ядра, так и в памяти пользователя, особенно в настройках сетевой файловой системы (копирование между машинами). Но это потребует тестирования (например, с командным временем Unix) и будет зависеть от оборудования, на котором компилируется и выполняется код.
Также вполне вероятно, что кто-то с ОС, в которой нет стандартной библиотеки Unix, захочет использовать ваш код. Тогда вы захотите использовать версию для чтения/записи буфера, так как она зависит только от и (и друзей)
Вот пример использования функции copy_file_range из стандартной библиотеки unix для копирования исходного файла в (возможно, несуществующий) целевой файл. Копирование происходит в пространстве ядра.
Это основано на примере из справочной страницы моего дистрибутива Ubuntu 20.x Linux для copy_file_range. Проверьте свои справочные страницы с помощью:
Затем нажимайте j или Enter, пока не перейдете к разделу примеров. Или выполните поиск, введя /example .
Вот пример, в котором используется только stdlib/stdio. Недостатком является использование промежуточного буфера в пространстве пользователя.
В этом посте мы узнаем, как копировать файл в C. С помощью этой программы вы узнаете, как выполнять основные операции с файлами в C, такие как открытие файла в режиме чтения/ режим записи, чтение содержимого из файла C и запись текста в файл на C.
На самом деле мы не можем скопировать файл напрямую в C. Мы откроем один файл, прочитаем содержимое файла и запишем это содержимое в новый файл.
Для чтения данных мы можем открыть файл в режиме чтения, а для записи нам нужно открыть его в режиме записи.
Действия по копированию файла в C:
- Откройте файл в режиме чтения. Это файл для копирования.
- Откройте другой файл в режиме записи. Это файл для копирования данных из указанного выше файла.
- Прочитайте содержимое первого файла, используя цикл. Этот цикл будет читать символ за символом и одновременно записывать во второй файл.
- После завершения чтения и записи закройте файлы.
Программа C для копирования файла:
Ниже представлена полная программа на C, которая копирует текстовый файл:
- Мы создали два указателя FILE: readFile и writeFile. Эти переменные будут содержать указатель на файл после того, как мы их откроем.
- readFilePath и writeFilePath — это пути к входному и выходному файлам соответственно. В этой программе мы читаем содержимое из файла input.txt в той же папке и записываем содержимое в файл output.txt в этой же папке.
- Нам нужно использовать fopen, чтобы открыть файл. Первый файл открывается в режиме чтения, а второй открывается в режиме записи. При открытии в режиме чтения, если файл не существует, выдается ошибка. Но в режиме записи, если он не существует, он создает этот файл.
- buffer – это символ, в котором мы временно сохраняем значение чтения. Когда будет достигнут конец файла, это будет EOF.
- цикл while продолжает считывать содержимое из readFile и записывать в writeFile.
Если вы запустите эту программу, она создаст один новый файл output.txt в той же папке и скопирует все содержимое input.txt в output. текст.
Некоторая информация относится к предварительной версии продукта, которая может быть существенно изменена до ее выпуска. Microsoft не дает никаких явных или подразумеваемых гарантий в отношении представленной здесь информации.
Копирует существующий файл в новый файл.
Перегрузки
Копирует существующий файл в новый файл. Запрещается перезаписывать файл с таким же именем.
Копирует существующий файл в новый файл. Разрешена перезапись файла с таким же именем.
Копировать(Строка, Строка)
Копирует существующий файл в новый файл. Запрещается перезаписывать файл с таким же именем.
Параметры
Файл для копирования.
Имя целевого файла. Это не может быть каталог или существующий файл.
Исключения
У вызывающего абонента нет необходимого разрешения.
sourceFileName или destFileName — это строка нулевой длины, содержащая только пробел или один или несколько недопустимых символов. Вы можете запросить недопустимые символы с помощью метода GetInvalidPathChars().
sourceFileName или destFileName указывает каталог.
sourceFileName или destFileName имеет значение null .
Указанный путь, имя файла или оба параметра превышают максимальную длину, определенную системой.
Путь, указанный в sourceFileName или destFileName, недействителен (например, он находится на несопоставленном диске).
имя исходного файла не найдено.
Произошла ошибка ввода-вывода.
Имя исходного файла или имя целевого файла имеет недопустимый формат.
Примеры
В следующем примере файлы копируются в резервную папку C:\archives\2008. Он использует две перегрузки метода Copy следующим образом:
Сначала он использует перегрузку метода File.Copy(String, String) для копирования текстовых файлов (.txt). Код демонстрирует, что эта перегрузка не позволяет перезаписывать уже скопированные файлы.
Затем он использует перегрузку метода File.Copy(String, String, Boolean) для копирования изображений (файлов .jpg). Код демонстрирует, что эта перегрузка позволяет перезаписывать уже скопированные файлы.
Примечания
Этот метод эквивалентен перегрузке метода Copy(String, String, Boolean) с параметром перезаписи, для которого задано значение false .
Параметры sourceFileName и destFileName могут указывать относительный или абсолютный путь. Информация об относительном пути интерпретируется как относительная к текущему рабочему каталогу. Чтобы получить текущий рабочий каталог, используйте метод Directory.GetCurrentDirectory. Этот метод не поддерживает подстановочные знаки в параметрах.
В этой статье вы узнаете и получите код для копирования содержимого одного файла в другой на языке C. Но прежде чем приступить к работе с программой, приведенной ниже, давайте сначала разберемся, что необходимо сделать перед выполнением операции копирования файла в C.
Что нужно сделать перед программой
Прежде чем перейти к программе, давайте сначала сделаем следующее:
- создайте два файла, а именно code.txt и Cracker.txt
- Поместите эти два файла в тот же каталог, где вы собираетесь сохранить файл исходного кода (программы копирования на C)
Предположим, здесь мы создали папку с именем cprograms в родительском каталоге папки Documents моего компьютера. Вот снимок папки с именем cprograms
Потому что это только что созданная папка. Итак, папка пока пуста. Но я собираюсь поместить некоторые файлы, связанные с программами копирования файлов
Теперь пришло время создать файл с именем code.txt и следующим содержимым.
И создайте второй файл с именем Cracker.txt без содержимого. Сохраните оба файла в той же папке, как показано на снимке выше, то есть в папке cprograms. Теперь папка выглядит так:
Если вы откроете оба файла, вы увидите некоторый контент внутри первого файла code.txt, тогда как второй файл Cracker.txt пуст. Теперь перейдем к программе, которая скопирует содержимое файла code.txt в файл cracker.txt.
Программа для копирования одного файла в другой на языке C
Чтобы скопировать содержимое одного файла в другой в программировании на C, вы должны сначала открыть оба файла, то есть исходный файл и целевой файл. Затем начните чтение содержимого исходного файла посимвольно и поместите или запишите содержимое исходного файла в целевой файл при каждом чтении символа.
Вопрос в том, напишите программу на C, которая копирует содержимое одного файла в другой файл. Ответ на этот вопрос дан ниже:
Эта программа была собрана и запущена в Code::Blocks IDE. Сохраните исходный код вышеуказанной программы в том же каталоге, который находится внутри папки с именем cprograms. Чтобы сохранить его, используйте Файл->Сохранить файл как. навигация внутри Code::Blocks. Здесь мы использовали codecracker.c в качестве имени файла с исходным кодом. После сохранения исходного кода вышеуказанной программы в папке c программами, присутствующими в документах, папка выглядит так:
Теперь пришло время собрать и запустить указанную выше программу. Вот пример запуска:
Теперь укажите имя первого файла, коды.txt (исходный файл, созданный в начале этой статьи в той же папке, к которой принадлежит исходный код).Нажмите клавишу ENTER, затем снова укажите имя другого файла, скажем, cracker.txt (целевой файл, куда будет скопировано содержимое файла source.txt). Наконец, нажмите клавишу ENTER, чтобы увидеть следующий вывод:
Вот окончательный снимок папки c программами:
Теперь, если вы откроете файл Cracker.txt (созданный с пустым содержимым в начале этой статьи), вы увидите, что в него скопировано содержимое файла code.txt. Вот и все. Чтобы узнать больше об операциях ввода и вывода файлов в C, обратитесь к учебнику по вводу/выводу файлов в C.
Читайте также: