Разделить файл обычно просто.
Требования к командной строке усложняют жизнь.
Кроме того, вы можете указать размер частей или их количество. Кроме того, необходимо учитывать, что детали могут быть не одинаковыми по размеру. Если ваш исходный размер файла составляет 20, а вы запрашиваете размер детали 7, то вам нужно написать 3 файла. 2 с 7 байтами и 1 с 6 байтами.
Вы можете использовать целочисленное деление и деление по модулю, чтобы получить эти значения
20 / 7 = 2
20 % 7 = 6
Оценка параметров строки команд является наиболее сложной частью. Для этого я создал отдельную функцию.
Я использую библиотеку C ++ std :: filesystem , чтобы выполнить всю работу, связанную с файлами.
Пожалуйста, посмотрите полный пример. Это пример кода. Это может быть реализовано так в C ++. У Бьюте, конечно, есть много других возможных решений. Но это должно дать вам представление о том, как это может работать.
Вы не поймете это. Он использует особенности C ++. Но не сдавайся рано. Go через это строка за строкой и Google все. Затем вы можете изучить хотя бы эту концепцию и реализовать ее своими словами.
#include <iostream>
#include <fstream>
#include <sstream>
#include <filesystem>
#include <cstdlib>
#include <tuple>
#include <iomanip>
#include <iterator>
#include <algorithm>
constexpr size_t NumberOfArgumentGroups = 3;
constexpr int NumberOfArguments = 7;
namespace fs = std::filesystem;
const std::string parameterExplanation{ "Please invoke program with parameters:\n-s path_of_source_file "
"-d path_of_destination -numpart x\nor\n-s path_of_source_file "
"-d path_of_destination -sizeapart x\n\n" };
std::tuple<bool, fs::path, fs::path, std::uintmax_t, std::uintmax_t> checkProgramArguments(int argc, char* argv[]) {
bool ok{ true }; // Indicates, if there was an error or not
fs::path sourceFilePath{}; // Path and file name of source file
fs::path destinationFilePath{}; // Output directory part
size_t numberOfParts{}; // So many parts are requested via option
unsigned long partSize{}; // The outpit file size requested via option
// First check the overall number of parameters
if (NumberOfArguments != argc) {
std::cerr << "\n*** Error:" << parameterExplanation;
ok = false;
}
// Now go through all parameters and check them
else for (size_t group{}; group < NumberOfArgumentGroups; ++group) {
// Get the option
std::string option(argv[group * 2U + 1U]);
// Specification of source path?
if ("-s" == option) {
// Get the source path
sourceFilePath = argv[group * 2U + 2U];
// And check, if the file exists and if it is a regurlar file
if (!fs::exists(sourceFilePath) || !fs::is_regular_file(sourceFilePath)) {
std::cerr << "\n*** Error: Problem with source file:\n" << sourceFilePath.string() << "\n";
ok = false;
}
}
// Specification of destination directory?
else if ("-d" == option) {
destinationFilePath = argv[group * 2U + 2U];
// Check, if directory exists
if (!fs::exists(destinationFilePath) || !fs::is_directory(destinationFilePath)) {
std::cerr << "\n*** Error: Destination file path does not exist\n" << destinationFilePath.string() << "\n";
ok = false;
}
}
// Specification of number of parts?
else if ("-numpart" == option) {
char* end;
// Convert to number
numberOfParts = std::strtoul(argv[group * 2U + 2U], &end, 10);
if (0 == numberOfParts) {
std::cerr << "\n*** Error: Invalid parameter for number of parts: " << argv[group * 2U + 2U] << "\n";
ok = false;
}
}
// Specification of size of one part
else if ("-sizeapart" == option) {
char* end;
// Convert to size number
partSize = std::strtoul(argv[group * 2U + 2U], &end, 10);
if (0 == partSize) {
std::cerr << "\n*** Error: Invalid parameter for number of parts: " << argv[group * 2U + 2U] << "\n";
ok = false;
}
}
else {
// Wrong option specified. Error message
std::cerr << "\n*** Error: Invalid option '" << option << "' specified.\n" << parameterExplanation;
ok = false;
}
}
// Sanity check,. Test combinations of parameters
if (sourceFilePath.empty() || destinationFilePath.empty() || ((numberOfParts) > 0 && (partSize > 0))) {
std::cerr << "\n*** Error: Invalid option combination\n" << parameterExplanation;
ok = false;
}
return { ok, sourceFilePath, destinationFilePath, numberOfParts, partSize };
}
// Driver code
int main(int argc, char* argv[]) {
// Check and evaluate comand line parameter
if (const auto& [ok, sourceFile, destinationDirectory, numberParts, sizeOfPart] = checkProgramArguments(argc, argv); ok) {
// Get the size of the source file
std::uintmax_t fileSize = fs::file_size(sourceFile);
// Now we need to calculate 3 parameters.
std::uintmax_t numberOfParts{}; // Number of files to create
std::uintmax_t numberOfBytesForLastPart; // The last file may contain less bytes
std::uintmax_t partSize{}; // Size of one output file (except the last
// So, either the number of parts of the part size is given. Depending on that
if (numberParts > 0) {
// Part number was given. Check, if the file size can be devided in exactly number parts
if (fileSize % (fileSize / numberParts) != 0)
// No, cannot. We need to write a lst file with less then part size bytes
// Therefore, decrement the number of output files with full number of bytes
numberOfParts = numberParts - 1;
else
// We will write numberParts files with equal number of bytes
numberOfParts = numberParts;
// Calculate the size of pne output file
partSize = fileSize / numberOfParts;
// And the number of vbytes for the last file
numberOfBytesForLastPart = fileSize % partSize;
}
else {
// The size of a part was given
partSize = sizeOfPart;
// Calculate the number of files that we will write
numberOfParts = fileSize / sizeOfPart;
// And the size of the last file
numberOfBytesForLastPart = fileSize % partSize;
}
// Open the input file and check, if it could be opened
if (std::ifstream sourceFileStream(sourceFile, std::ios::binary); sourceFileStream) {
// Write all parts
for (std::uintmax_t i{}; i <= numberOfParts; ++i) {
// Build the file name for the output file
std::ostringstream outputFileName{};
outputFileName << sourceFile.filename().string() << ".part" << std::setfill('0') << std::setw(2) << i+1;
// This is a smart overload of the / operator. It will concatenate the path and filename with correct delimiter
fs::path outputFilePathAndName = destinationDirectory / outputFileName.str();
// DO only something if we have still bytes to copy
if (uintmax_t bytesToCopy = ((i == numberOfParts) ? numberOfBytesForLastPart : partSize); bytesToCopy > 0) {
// Open the output file for this part and check, if it is open
if (std::ofstream outputFileStream(outputFilePathAndName, std::ios::binary); outputFileStream) {
// Copy bytes from source to destination
std::copy_n(std::istreambuf_iterator<char>(sourceFileStream),
bytesToCopy,
std::ostreambuf_iterator<char>(outputFileStream));
}
}
else {
std::cerr << "\n*** Error: Could not open destination file: " << outputFilePathAndName << "\n";
}
}
}
else {
std::cerr << "\n*** Error: Could not open source file: " << sourceFile << "\n";
}
}
return 0;
}
Я добавил много проверок на ошибки. Тебе тоже следует это сделать.