Win32 文件分割合并

2024-01-04

CFileSplitUtils.h

#pragma once
#include <vector>
#include <string>
#include <functional>

#ifdef _UNICODE
using _tstring = std::wstring;
#else
using _tstring = std::string;
#endif

typedef struct _SPLIT_DATA_INFO
{
    long long llBeginPos = 0;   //分割起始位置
    long long llEndPos = 0;     //分割结束位置
    long long llSize = 0;       //分割大小
    long long llCount = 0;      //分割数量

    _SPLIT_DATA_INFO() : llBeginPos(0), llEndPos(0), llSize(0), llCount(0)
    {
        memset(this, 0, sizeof(*this));
    }
}SPLIT_DATA_INFO;

typedef struct _SPLIT_FILE_INFO
{
    long long llBeginPos = 0;   //分割起始位置
    long long llSize = 0;       //分割大小

    _SPLIT_FILE_INFO() : llBeginPos(0), llSize(0)
    {
        memset(this, 0, sizeof(*this));
    }
}SPLIT_FILE_INFO;

class CFileSplitUtils
{
public:

    //
    // @brief: 合并文件
    // @param: fileList         文件路径字符串容器
    // @param: strFilePath      保存文件路径
    // @param: cbProgress       进度回调函数, 返回false则终止合并
    // @ret: void
    static bool CombineFile(
        const std::vector<_tstring>& fileList,
        const _tstring& strFilePath,
        std::function<bool(
            unsigned long long ullFileCount,            //已处理文件数
            unsigned long long ullFileTotal,       //总文件数
            unsigned long long ullBytesCount,           //已处理数据量
            unsigned long long ullBytesTotal       //总数据量
            )> cbProgress = nullptr
    );

    //
    // @brief: 分割文件
    // @param: strFilePath      文件路径
    // @param: strFileOutDir    保存文件夹
    // @param: strSplitCfg      分割配置字符串, 参数: 起始, 结束, 块大小, 份数
    //                              如: "0, -1, 8K, 16", 从文件头开始, 文件尾结束, 每个块8KB, 最多分割为16个文件
    // @param: strPrefixName    文件名前缀
    // @param: strExtName       文件名后缀
    // @param: nIndex           文件名起始索引
    // @param: bHexnIndex       文件名起始索引使用十六进制
    // @param: bHasRange        文件名包含数据范围
    // @param: bHasCrc32        文件名包含CRC32校验码
    // @param: cbProgress       进度回调函数, 返回false则终止合并
    // @ret: void
    static bool SplitFile(
        const _tstring& strFilePath,
        const _tstring& strFileOutDir,
        const _tstring& strSplitCfg,
        const _tstring& strPrefixName,
        const _tstring& strExtName,
        long long nIndex = 0,
        bool bHexIndex = false,
        bool bHasRange = false,
        bool bHasCrc32 = false,

        std::function<bool(
            unsigned long long ullFileCount,            //已处理文件数
            unsigned long long ullFileTotal,       //总文件数
            unsigned long long ullBytesCount,           //已处理数据量
            unsigned long long ullBytesTotal       //总数据量
            )> cbProgress = nullptr
    );

    //
    // @brief: 获取文件大小
    // @param: strFilePath      保存文件路径
    // @ret: long long          文件大小
    static long long GetFileSize(
        const _tstring& strFilePath
    );

    //
    // @brief: 从字符串解析分割信息
    // @param: strSplitCfg      分割配置字符串
    // @ret: std::vector<FILE_SPLIT_INFO>        分割信息
    static std::vector<SPLIT_DATA_INFO> GetSplitDataInfos(
        const _tstring& strSplitCfg
    );

    //
    // @brief: 从分割信息分解出分割元素
    // @param: splitInfo        分割信息
    // @param: llFileSize       文件大小
    // @ret: std::vector<SPLIT_DATA_INFO>        单个文件分割信息
    static std::vector<SPLIT_FILE_INFO> GetSpliFilenfos(
        const std::vector<SPLIT_DATA_INFO>& splitInfo,
        long long llFileSize
    );

    //
    // @brief: 从分割信息统计分割后文件总数
    // @param: splitInfo        分割信息
    // @param: llFileSize       文件大小
    // @ret: long long          分割后文件总数
    static long long GetSplitFileCount(
        const std::vector<SPLIT_DATA_INFO>& splitInfo,
        long long llFileSize
    );

    //
    // @brief: 从分割信息统计分割后数据总量
    // @param: splitInfo        分割信息
    // @param: llFileSize       文件大小
    // @ret: long long          分割后数据总量
    static long long GetSplitTotalSize(
        const std::vector<SPLIT_DATA_INFO>& splitInfo,
        long long llFileSize
    );

    //
    // @brief: 从分割信息统计分割后最大数据位置
    // @param: splitInfo        分割信息
    // @param: llFileSize       文件大小
    // @ret: long long          分割后最大数据位置
    static long long GetSplitMaxPos(
        const std::vector<SPLIT_DATA_INFO>& splitInfo,
        long long llFileSize
    );
};

CFileSplitUtils.cpp

#include "CFileSplitUtils.h"
#include <sys/types.h>
#include <sys/stat.h>
#include <fstream>
#include <tchar.h>
#include "CCrc32Utils.h"
#include "CStrUtils.h"
#include "CPathUtils.h"

long long CFileSplitUtils::GetFileSize(const _tstring& strFilePath)
{
    struct _stat64 statbuf = { 0 };
    if (0 == _tstat64(strFilePath.c_str(), &statbuf))
    {
        return statbuf.st_size;
    }

    return 0;
}

bool CFileSplitUtils::SplitFile(
    const _tstring& strFilePath,
    const _tstring& strFileOutDir,
    const _tstring& strSplitCfg,
    const _tstring& strPrefixName,
    const _tstring& strExtName,
    long long nIndex/* = 0*/,
    bool bHexIndex/* = false*/,
    bool bHasRange/* = false*/,
    bool bHasCrc32/* = false*/,
    std::function<bool(
        unsigned long long ullFileCount,            //已处理文件数
        unsigned long long ullFileTotal,       //总文件数
        unsigned long long ullBytesCount,           //已处理数据量
        unsigned long long ullBytesTotal       //总数据量
        )
    > cbProgress/* = nullptr*/
)
{
    _tstring strOutDir = strFileOutDir;
    _tstring strPrefix = strPrefixName;
    _tstring strExt = strExtName;

    //获取分割信息
    std::vector<SPLIT_DATA_INFO> vFileSplitInfos = GetSplitDataInfos(strSplitCfg);

    //获取文件大小
    long long llFileSize = GetFileSize(strFilePath);

    //统计分数数量
    long long llSplitCount = GetSplitFileCount(vFileSplitInfos, llFileSize);

    //获取最大数据位置
    long long llSplitMaxPos = GetSplitMaxPos(vFileSplitInfos, llFileSize);

    //获取文件大小
    long long llTotalSize = GetSplitTotalSize(vFileSplitInfos, llFileSize);

    //分解分割任务
    std::vector<SPLIT_FILE_INFO> vSplitInfos = GetSpliFilenfos(vFileSplitInfos, llFileSize);

    if (0 == llFileSize || 0 == llSplitCount)
    {
        return false;
    }

    std::ifstream inputFile(strFilePath, std::ios::binary | std::ios::in);
    if (!inputFile.is_open())
    {
        return false;
    }

    long long ullFileCount = 0;
    long long ullFileTotal = llSplitCount;
    long long ullBytesCount = 0;
    long long ullBytesTotal = llTotalSize;
    bool bCancel = false;

    //计算索引字符串格式
    _tstring strIndexFormat;
    if (bHexIndex)
    {
        strIndexFormat = CStrUtils::Format(_T("%%0%dX"), CStrUtils::Format(_T("%X"), llSplitCount + nIndex - 1).size());
    }
    else
    {
        strIndexFormat = CStrUtils::Format(_T("%%0%dd"), CStrUtils::Format(_T("%d"), llSplitCount + nIndex - 1).size());
    }

    //计算数据范围字符串格式
    int nRangeLength = (int)CStrUtils::Format(_T("%llX"), llSplitMaxPos).size();
    _tstring strRangeFormat = CStrUtils::Format(_T("%%0%dllX-%%0%dllX"), nRangeLength, nRangeLength);

    //前缀名检查
    if (strPrefix.empty())
    {
        strPrefix = CPathUtils::GetFileName(strFilePath, false);
    }

    //输出目录检查
    if (strOutDir.empty())
    {
        strOutDir = CPathUtils::GetFileDir(strFilePath);
        strOutDir += _T("\\");
        strOutDir += CPathUtils::GetFileName(strFilePath, false);
        strOutDir += _T("_Split");
    }

    //创建文件夹
    if (!CPathUtils::IsExist(strOutDir))
    {
        CPathUtils::CreateDir(strOutDir);
    }

    //开始分割文件
    _tstring strOutTemp = strOutDir + _T("\\Temp");

    //读写文件缓冲分配
    const size_t bufSize = static_cast<size_t>(1024 * 1024) * 4;
    char* pDataBuf = new (std::nothrow) char[bufSize];
    if (nullptr == pDataBuf)
    {
        return false;
    }

    //开始分割文件
    for (int i = 0; i < vSplitInfos.size(); i++)
    {
        SPLIT_FILE_INFO& info = vSplitInfos[i];

        std::ofstream outFile(strOutTemp, std::ios::binary | std::ios::out);
        if (!outFile.is_open())
        {
            return 0;
        }

        inputFile.seekg(info.llBeginPos, std::ios::beg);

        uint32_t uCrc32 = 0;
        long long nBlockSize = bufSize;
        long long llSize = info.llSize;

        while (llSize > 0)
        {
            if (llSize > bufSize)
            {
                nBlockSize = bufSize;
            }
            else
            {
                nBlockSize = llSize;
            }

            inputFile.read(pDataBuf, nBlockSize);
            size_t llRead = (size_t)inputFile.gcount();

            if (bHasCrc32)
            {
                uCrc32 = CCrc32Utils::GetPartCrc32(uCrc32, pDataBuf, llRead);
            }

            outFile.write(pDataBuf, llRead);
            llSize -= nBlockSize;
            ullBytesCount += llRead;

            //输出单个文件结束处理
            if (0 == llSize)
            {
                ullFileCount++;
                long long llBegin = info.llBeginPos;
                long long llEnd = info.llBeginPos + info.llSize - 1;

                _tstring strOutPath = strOutDir + _T("\\");

                //文件名前缀
                if (!strPrefix.empty())
                {
                    strOutPath += strPrefix;
                    strOutPath += _T("_");
                }

                //文件名序号
                strOutPath += CStrUtils::Format(strIndexFormat.c_str(), nIndex + i);

                //文件名添加数据范围
                if (bHasRange)
                {
                    _tstring strRange = CStrUtils::Format(strRangeFormat.c_str(), llBegin, llEnd);
                    strOutPath += _T("_");
                    strOutPath += strRange;
                }

                //文件名添加CRC32校验码
                if (bHasCrc32)
                {
                    strOutPath += _T("_");
                    strOutPath += CCrc32Utils::GetStrFromValue(uCrc32, true);
                }

                //文件名添加扩展名
                if (!strExt.empty())
                {
                    strOutPath += _T(".");
                    strOutPath += strExt;
                }

                outFile.close();

                //删除文件, 防止影响重命名失败
                CPathUtils::DeleteArchive(strOutPath);

                //临时文件重命名
                CPathUtils::Rename(strOutTemp, strOutPath);
            }

            //回调检查
            if (nullptr == cbProgress)
            {
                continue;
            }

            //回调返回false则停止合并文件
            if (!cbProgress(ullFileCount, ullFileTotal, ullBytesCount, ullBytesTotal))
            {
                outFile.close();

                //中断则删除临时文件
                CPathUtils::Delete(strOutTemp);
                bCancel = true;
                break;
            }
        }
    }

    if (nullptr != pDataBuf)
    {
        delete[]pDataBuf;
        pDataBuf = nullptr;
    }

    return true;
}

bool CFileSplitUtils::CombineFile(
    const std::vector<_tstring>& fileList,
    const _tstring& strFilePath,
    std::function<bool(
        unsigned long long ullFileCount,            //已处理文件数
        unsigned long long ullFileTotal,       //总文件数
        unsigned long long ullBytesCount,           //已处理数据量
        unsigned long long ullBytesTotal       //总数据量
        )
    > cbProgress/* = nullptr*/
)
{
    const size_t bufSize = static_cast<size_t>(1024 * 1024) * 4;
    char* pDataBuf = new (std::nothrow) char[bufSize];
    if (nullptr == pDataBuf)
    {
        return false;
    }

    long long ullFileCount = 0;
    long long ullTotalFileCount = 0;
    long long ullBytesCount = 0;
    long long ullTotalBytesCount = 0;
    bool bCancel = false;

    //打开输出文件
    std::ofstream outFile(strFilePath, std::ios::binary | std::ios::out);
    if (!outFile.is_open())
    {
        return false;
    }

    //统计文件数量与数据量
    for (const auto& item : fileList)
    {
        long long llFileSize = GetFileSize(item);
        if (-1 != llFileSize)
        {
            ullTotalBytesCount += llFileSize;
            ullTotalFileCount++;
        }
    }

    //开始将文件合并输出
    for (const auto& item : fileList)
    {
        //打开文件
        std::ifstream inputFile(item, std::ios::binary | std::ios::in);
        if (!inputFile.is_open())
        {
            continue;
        }

        //块大小
        long long nBlockSize = bufSize;

        //文件大小
        long long llSize = GetFileSize(item);

        while (llSize > 0)
        {
            if (llSize > bufSize)
            {
                nBlockSize = bufSize;
            }
            else
            {
                nBlockSize = llSize;
            }

            inputFile.read(pDataBuf, nBlockSize);
            long long llRead = inputFile.gcount();
            outFile.write(pDataBuf, llRead);
            llSize -= nBlockSize;
            ullBytesCount += llRead;

            if (0 == llSize)
            {
                ullFileCount++;
            }

            //回调检查
            if (cbProgress)
            {
                //回调返回false则停止合并文件
                if (!cbProgress(ullFileCount, ullTotalFileCount, ullBytesCount, ullTotalBytesCount))
                {
                    bCancel = true;
                    break;
                }
            }
        }

        inputFile.close();

        if (bCancel)
        {
            break;
        }
    }

    //善后处理
    if (nullptr != pDataBuf)
    {
        delete[]pDataBuf;
        pDataBuf = nullptr;
    }

    outFile.close();

    return true;
}

std::vector<SPLIT_DATA_INFO> CFileSplitUtils::GetSplitDataInfos(
    const _tstring& strSplitCfg
)
{
    std::vector<SPLIT_DATA_INFO> infos;

    //解析分割范围
    _tstring strSplitList = strSplitCfg;

    CStrUtils::Replace(strSplitList, _T(" "), _T(""), false);
    std::vector<_tstring> vSplitList = CStrUtils::SplitStr(strSplitList, _T("\r\n"));

    for (const auto& lineItem : vSplitList)
    {
        //开头 ; 认为是注释, 忽略不管
        if (_T(';') == lineItem.front())
        {
            continue;
        }

        //拆分子项
        std::vector<_tstring> vSplitItem = CStrUtils::SplitStr(lineItem, _T(";"));
        for (const auto& subItem : vSplitItem)
        {
            long long llValues[4] = { 0, -1, 0, 0 };

            std::vector<_tstring> vSplitValue = CStrUtils::SplitStr(subItem, _T(","));
            for (int i = 0; i < (int)vSplitValue.size(); i++)
            {
                if (i >= _countof(llValues))
                {
                    break;
                }

                _tstring strHex = _T("0x");
                _tstring strNegative = _T("-");
                bool bNegative = false;

                TCHAR* pEndChar = nullptr;

                _tstring itemValue = vSplitValue[i];

                if (0 == CStrUtils::FindSubString(itemValue, strNegative))
                {
                    bNegative = true;
                    itemValue = itemValue.substr(1, (size_t)-1);
                }

                if (0 == CStrUtils::FindSubString(itemValue, strHex))
                {
                    llValues[i] = _tcstoll(itemValue.c_str(), &pEndChar, 16);
                }
                else
                {
                    llValues[i] = _tcstoll(itemValue.c_str(), &pEndChar, 10);
                }

                if (nullptr != pEndChar)
                {
                    if (_T('K') == *pEndChar || _T('k') == *pEndChar)
                    {
                        llValues[i] *= 1024;
                    }
                    if (_T('M') == *pEndChar || _T('m') == *pEndChar)
                    {
                        llValues[i] *= 1024 * 1024;
                    }
                    if (_T('G') == *pEndChar || _T('g') == *pEndChar)
                    {
                        llValues[i] *= 1024 * 1024 * 1024;
                    }
                }

                if (bNegative)
                {
                    llValues[i] = 0 - llValues[i];
                }
            }

            SPLIT_DATA_INFO info;
            info.llBeginPos = llValues[0];
            info.llEndPos = llValues[1];
            info.llSize = llValues[2];
            info.llCount = llValues[3];

            infos.push_back(info);
        }
    }

    return infos;
}

long long CFileSplitUtils::GetSplitFileCount(
    const std::vector<SPLIT_DATA_INFO>& splitInfo,
    long long llFileSize
)
{
    long long llSplitCopunt = 0;

    for (const auto& item : splitInfo)
    {
        long long llBeginPos = item.llBeginPos;
        long long llEndPos = item.llEndPos;
        long long llSize = item.llSize;
        long long llCount = item.llCount;

        //结束位置
        if (item.llEndPos < 0)
        {
            llEndPos = llFileSize + item.llEndPos;
        }

        //最大结束位置不得超过文件末尾
        if (item.llEndPos >= llFileSize)
        {
            llEndPos = llFileSize - 1;
        }

        //确定起始位置
        if (item.llBeginPos < 0)
        {
            llBeginPos = llFileSize + item.llBeginPos;
        }

        //起始位置大小排序
        if (llEndPos < llBeginPos)
        {
            long long llTemp = llBeginPos;
            llBeginPos = llEndPos;
            llEndPos = llTemp;
        }

        //起始位置必须位于文件范围内
        if (llBeginPos < 0 || llBeginPos >= llFileSize || llEndPos < 0 || llEndPos >= llFileSize)
        {
            continue;
        }

        if (llCount < 0)//文件分割个数不能为负数
        {
            continue;
        }

        //未指定数量, 则认为是分割为1个文件
        if (0 == llCount)
        {
            if (0 == llSize)
            {
                llCount = 1;
            }
            else
            {
                llCount = llFileSize;
            }
        }

        if (0 == llSize)//大小为0, 则按照个数平均分割
        {
            if (llCount <= 1)
            {
                llCount = 1;
            }

            //统计分割大小
            long long packSize = 0;
            packSize = (llEndPos - llBeginPos + 1) / llCount;
            if (0 != (llEndPos - llBeginPos + 1) % llCount)
            {
                packSize++;
            }

            //最多分割个数
            llCount = (llEndPos - llBeginPos + 1) / packSize;
            if (0 != (llEndPos - llBeginPos + 1) % packSize)
            {
                llCount++;
            }

            //文件个数比实际数据还大, 则按照一个字节分割
            if (llEndPos - llBeginPos + 1 < llCount)
            {
                packSize = 1;
                llCount = llEndPos - llBeginPos + 1;
            }

            llSplitCopunt += llCount;
        }
        else if (llSize < 0)//从文件末尾向文件头部分割
        {
            llSize = abs(llSize);

            //大小限定为文件大小
            if (abs(llSize) > llFileSize)
            {
                llSize = llFileSize;
            }

            long long llMaxSplitCount = (llEndPos - llBeginPos + 1) / llSize;
            if (0 != (llEndPos - llBeginPos + 1) % llSize)
            {
                llMaxSplitCount++;
            }

            if (llCount > llMaxSplitCount)
            {
                llCount = llMaxSplitCount;
            }

            llSplitCopunt += llCount;
        }
        else if (llSize > 0)//从文件头部向文件末尾分割
        {
            //大小限定为文件大小
            if (abs(llSize) > llFileSize)
            {
                llSize = llFileSize;
            }

            long long llMaxSplitCount = (llEndPos - llBeginPos + 1) / llSize;
            if (0 != (llEndPos - llBeginPos + 1) % llSize)
            {
                llMaxSplitCount++;
            }

            if (llCount > llMaxSplitCount)
            {
                llCount = llMaxSplitCount;
            }

            llSplitCopunt += llCount;
        }
    }

    return llSplitCopunt;
}

long long CFileSplitUtils::GetSplitMaxPos(
    const std::vector<SPLIT_DATA_INFO>& splitInfo,
    long long llFileSize
)
{
    long long llSplitMaxPos = 0;

    for (const auto& item : splitInfo)
    {
        long long llBeginPos = item.llBeginPos;
        long long llEndPos = item.llEndPos;
        long long llSize = item.llSize;
        long long llCount = item.llCount;

        //结束位置
        if (item.llEndPos < 0)
        {
            llEndPos = llFileSize + item.llEndPos;
        }

        //最大结束位置不得超过文件末尾
        if (item.llEndPos >= llFileSize)
        {
            llEndPos = llFileSize - 1;
        }

        //确定起始位置
        if (item.llBeginPos < 0)
        {
            llBeginPos = llFileSize + item.llBeginPos;
        }

        //起始位置大小排序
        if (llEndPos < llBeginPos)
        {
            long long llTemp = llBeginPos;
            llBeginPos = llEndPos;
            llEndPos = llTemp;
        }

        //起始位置必须位于文件范围内
        if (llBeginPos < 0 || llBeginPos >= llFileSize || llEndPos < 0 || llEndPos >= llFileSize)
        {
            continue;
        }

        if (llCount < 0)//文件分割个数不能为负数
        {
            continue;
        }

        //未指定数量, 则认为是分割为1个文件
        if (0 == llCount)
        {
            if (0 == llSize)
            {
                llCount = 1;
            }
            else
            {
                llCount = llFileSize;
            }
        }

        if (llSize <= 0)//大小为0, 则按照个数平均分割
        {
            llSplitMaxPos = llEndPos;
        }
        else if (llSize > 0)//从文件头部向文件末尾分割
        {
            //大小限定为文件大小
            if (abs(llSize) > llFileSize)
            {
                llSize = llFileSize;
            }

            long long llMaxSplitCount = (llEndPos - llBeginPos + 1) / llSize;
            if (0 != (llEndPos - llBeginPos + 1) % llSize)
            {
                llMaxSplitCount++;
            }

            if (llCount > llMaxSplitCount)
            {
                llCount = llMaxSplitCount;
            }

            long long llMaxSplitSize = llCount * llSize;
            if (llMaxSplitSize > (llEndPos - llBeginPos + 1))
            {
                llSplitMaxPos = llEndPos;
            }
            else
            {
                llSplitMaxPos = llBeginPos + llCount * llSize - 1;
            }
        }

    }

    return llSplitMaxPos;
}

long long CFileSplitUtils::GetSplitTotalSize(
    const std::vector<SPLIT_DATA_INFO>& splitInfo,
    long long llFileSize
)
{
    long long llSplitSize = 0;

    for (const auto& item : splitInfo)
    {
        long long llBeginPos = item.llBeginPos;
        long long llEndPos = item.llEndPos;
        long long llSize = item.llSize;
        long long llCount = item.llCount;

        //结束位置
        if (item.llEndPos < 0)
        {
            llEndPos = llFileSize + item.llEndPos;
        }

        //最大结束位置不得超过文件末尾
        if (item.llEndPos >= llFileSize)
        {
            llEndPos = llFileSize - 1;
        }

        //确定起始位置
        if (item.llBeginPos < 0)
        {
            llBeginPos = llFileSize + item.llBeginPos;
        }

        //起始位置大小排序
        if (llEndPos < llBeginPos)
        {
            long long llTemp = llBeginPos;
            llBeginPos = llEndPos;
            llEndPos = llTemp;
        }

        //起始位置必须位于文件范围内
        if (llBeginPos < 0 || llBeginPos >= llFileSize || llEndPos < 0 || llEndPos >= llFileSize)
        {
            continue;
        }

        if (llCount < 0)//文件分割个数不能为负数
        {
            continue;
        }

        //未指定数量, 则认为是分割为1个文件
        if (0 == llCount)
        {
            if (0 == llSize)
            {
                llCount = 1;
            }
            else
            {
                llCount = llFileSize;
            }
        }

        if (0 == llSize)//大小为0, 则按照个数平均分割
        {
            if (llCount <= 1)
            {
                llCount = 1;
            }

            //统计分割大小
            long long packSize = 0;
            packSize = (llEndPos - llBeginPos + 1) / llCount;
            if (0 != (llEndPos - llBeginPos + 1) % llCount)
            {
                packSize++;
            }

            //最多分割个数
            llCount = (llEndPos - llBeginPos + 1) / packSize;
            if (0 != (llEndPos - llBeginPos + 1) % packSize)
            {
                llCount++;
            }

            //文件个数比实际数据还大, 则按照一个字节分割
            if (llEndPos - llBeginPos + 1 < llCount)
            {
                packSize = 1;
                llCount = llEndPos - llBeginPos + 1;
            }

            long long llMaxSplitSize = llCount * packSize;
            if (llMaxSplitSize > (llEndPos - llBeginPos + 1))
            {
                llMaxSplitSize = (llEndPos - llBeginPos + 1);
            }

            llSplitSize += llMaxSplitSize;
        }
        else if (llSize < 0)//从文件末尾向文件头部分割
        {
            llSize = abs(llSize);

            //大小限定为文件大小
            if (abs(llSize) > llFileSize)
            {
                llSize = llFileSize;
            }

            long long llMaxSplitCount = (llEndPos - llBeginPos + 1) / llSize;
            if (0 != (llEndPos - llBeginPos + 1) % llSize)
            {
                llMaxSplitCount++;
            }

            if (llCount > llMaxSplitCount)
            {
                llCount = llMaxSplitCount;
            }

            long long llMaxSplitSize = llCount * llSize;
            if (llMaxSplitSize > (llEndPos - llBeginPos + 1))
            {
                llMaxSplitSize = (llEndPos - llBeginPos + 1);
            }

            llSplitSize += llMaxSplitSize;
        }
        else if (llSize > 0)//从文件头部向文件末尾分割
        {
            //大小限定为文件大小
            if (abs(llSize) > llFileSize)
            {
                llSize = llFileSize;
            }

            long long llMaxSplitCount = (llEndPos - llBeginPos + 1) / llSize;
            if (0 != (llEndPos - llBeginPos + 1) % llSize)
            {
                llMaxSplitCount++;
            }

            if (llCount > llMaxSplitCount)
            {
                llCount = llMaxSplitCount;
            }

            long long llMaxSplitSize = llCount * llSize;
            if (llMaxSplitSize > (llEndPos - llBeginPos + 1))
            {
                llMaxSplitSize = (llEndPos - llBeginPos + 1);
            }

            llSplitSize += llMaxSplitSize;
        }
    }

    return llSplitSize;
}

std::vector<SPLIT_FILE_INFO> CFileSplitUtils::GetSpliFilenfos(
    const std::vector<SPLIT_DATA_INFO>& splitInfo,
    long long llFileSize
)
{
    std::vector<SPLIT_FILE_INFO> infos;

    for (const auto& item : splitInfo)
    {
        long long llBeginPos = item.llBeginPos;
        long long llEndPos = item.llEndPos;
        long long llSize = item.llSize;
        long long llCount = item.llCount;

        //结束位置
        if (item.llEndPos < 0)
        {
            llEndPos = llFileSize + item.llEndPos;
        }

        //最大结束位置不得超过文件末尾
        if (item.llEndPos >= llFileSize)
        {
            llEndPos = llFileSize - 1;
        }

        //确定起始位置
        if (item.llBeginPos < 0)
        {
            llBeginPos = llFileSize + item.llBeginPos;
        }

        //起始位置大小排序
        if (llEndPos < llBeginPos)
        {
            long long llTemp = llBeginPos;
            llBeginPos = llEndPos;
            llEndPos = llTemp;
        }

        //起始位置必须位于文件范围内
        if (llBeginPos < 0 || llBeginPos >= llFileSize || llEndPos < 0 || llEndPos >= llFileSize)
        {
            continue;
        }

        if (llCount < 0)//文件分割个数不能为负数
        {
            llCount = 0;
        }

        //未指定数量, 则认为是分割为1个文件
        if (0 == llCount)
        {
            if (0 == llSize)
            {
                llCount = 1;
            }
            else
            {
                llCount = llFileSize;
            }
        }

        if (0 == llSize)//大小为0, 则按照个数平均分割
        {
            if (llCount <= 1)
            {
                llCount = 1;
            }

            //统计分割大小
            long long packSize = 0;
            packSize = (llEndPos - llBeginPos + 1) / llCount;
            if (0 != (llEndPos - llBeginPos + 1) % llCount)
            {
                packSize++;
            }

            //最多分割个数
            llCount = (llEndPos - llBeginPos + 1) / packSize;
            if (0 != (llEndPos - llBeginPos + 1) % packSize)
            {
                llCount++;
            }

            //文件个数比实际数据还大, 则按照一个字节分割
            if (llEndPos - llBeginPos + 1 < llCount)
            {
                packSize = 1;
                llCount = llEndPos - llBeginPos + 1;
            }

            //平均分配
            for (int i = 0; i < llCount; i++)
            {
                SPLIT_FILE_INFO info;
                info.llBeginPos = llBeginPos + i * packSize;
                info.llSize = packSize;

                //超限处理
                if ((info.llBeginPos + info.llSize - 1) > llEndPos)
                {
                    info.llSize = llEndPos - info.llBeginPos + 1;
                }

                infos.push_back(info);
            }
        }
        else if (llSize < 0)//从文件末尾向文件头部分割
        {
            llSize = abs(llSize);

            //大小限定为文件大小
            if (abs(llSize) > llFileSize)
            {
                llSize = llFileSize;
            }

            //最大分割数量
            long long llMaxSplitCount = (llEndPos - llBeginPos + 1) / llSize;
            if (0 != (llEndPos - llBeginPos + 1) % llSize)
            {
                llMaxSplitCount++;
            }

            if (llCount > llMaxSplitCount)
            {
                llCount = llMaxSplitCount;
            }

            for (int i = 0; i < llCount; i++)
            {
                SPLIT_FILE_INFO info;
                info.llSize = llSize;
                info.llBeginPos = llEndPos - ((i + 1) * llSize) + 1;

                //越界处理
                if (info.llBeginPos + info.llSize < llBeginPos)
                {
                    break;
                }

                //越界处理
                if (info.llBeginPos < llBeginPos)
                {
                    info.llBeginPos = llBeginPos;
                    info.llSize = llEndPos - (i * llSize) + 1;
                }

                infos.push_back(info);
            }
        }
        else if (llSize > 0)//从文件头部向文件末尾分割
        {
            //大小限定为文件大小
            if (abs(llSize) > llFileSize)
            {
                llSize = llFileSize;
            }

            //最大分割数量
            long long llMaxSplitCount = (llEndPos - llBeginPos + 1) / llSize;
            if (0 != (llEndPos - llBeginPos + 1) % llSize)
            {
                llMaxSplitCount++;
            }

            if (llCount > llMaxSplitCount)
            {
                llCount = llMaxSplitCount;
            }

            for (int i = 0; i < llCount; i++)
            {
                SPLIT_FILE_INFO info;
                info.llBeginPos = llBeginPos + i * llSize;
                info.llSize = llSize;

                //越界处理
                if (info.llBeginPos > llEndPos)
                {
                    break;
                }

                //越界处理
                if ((info.llBeginPos + info.llSize - 1) > llEndPos)
                {
                    info.llSize = llEndPos - info.llBeginPos + 1;
                }

                infos.push_back(info);
            }
        }
    }

    return infos;
}

main.cpp

#include <iostream>
#include <vector>
#include <stdarg.h>
#include <tchar.h>
#include <windows.h>
#include <thread>
#include <strsafe.h>
#include "Win32Utils/CFileSplitUtils.h"
#include "Win32Utils/CPathUtils.h"

int _tmain(int argc, LPCTSTR argv[])
{
    setlocale(LC_ALL, "");

    
    CFileSplitUtils::SplitFile(
        CPathUtils::GetCurrentModulePath(),
        _T(""),
        _T("0, -1, 64K, 0"),    // 从文件头开始, 文件尾结束, 每个块8KB, 分割数量不限
        _T(""),
        _T("bin"),
        0,
        false,
        true,
        true,
        [](
            unsigned long long ullFileCount,            //已处理文件数
            unsigned long long ullTotalFileCount,       //总文件数
            unsigned long long ullBytesCount,           //已处理数据量
            unsigned long long ullTotalBytesCount       //总数据量
            
            )
        {
            printf("%0.3lf/%%, %lld/%lld, %lld/%lld\n", 
                ((double)ullBytesCount / (double)ullTotalBytesCount) * 100,
                ullFileCount, ullTotalFileCount,
                ullBytesCount, ullTotalBytesCount
            );

            return true;
        }
    );

    return 0;
}

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Win32 文件分割合并 的相关文章

随机推荐

  • 运维的本质是什么?阿里“无人化”智能运维平台的演进

    差不多在两年前 阿里内部出现了很多运维中台 研发中台等等 那有没有后台呢 不好意思 我们只有中台 没有后台 会在中台上构建与业务相关的各个前台 目前阿里的业务几乎覆盖了所有行业 有着很多业务线 如果业务线的前台到中台全部都是我们自己去建设
  • 系统有万亿条消息怎么存储?

    系统有万亿条消息怎么存储 本文转自 公众号 ByteByteGo 如有侵权 请联系 立即删除 我们如何设计一个能存储数万亿条信息的系统 Discord 的消息存储演进给我们提供了真实案例参考 下图显示了 Discord 消息存储的演变过程
  • 学了网安有机会自己当老板——初创网络安全公司的指南

    初创公司的创始人常常会误以为黑客不会在他们创业的公司身上浪费时间 因为他们还不够大 不够有名 没有什么价值 你的规模小 知名度小并不意味着你不会成为攻击者的目标 初创公司的规模并不能使其免于网络攻击 因为黑客们会不断扫描互联网 寻找他们可以
  • C语言——!(--x)和!(-x)以及while(!(--x)) 和 while(!(-x))

    一 x 和 x 1 x 这个表达式表示先对变量 x 执行自减操作 即 x 的值减去 1 然后对自减后得到的结果取逻辑非 如果自减后的 x 等于 0 则逻辑非运算的结果为真 值为 1 否则结果为假 值为 0 2 x 这个表达式表示对 x 取反
  • Python 操作 JMeter 探索:pymeter 实操指南

    概要 JMeter 是一个流行的性能测试工具 用于测试 Web 应用程序的性能和负载 它通常与 GUI 一起使用 但如果您想在自动化测试中集成 JMeter 或者以编程方式创建和运行测试计划 那么 pymeter 库将是一个强大的工具 本文
  • HOOPS助力AVEVA数字化转型:支持多种3D模型格式转换!

    行业 电力和公用事业 化工 造船 能源 采矿业 挑战 创建大规模复杂资产的客户需要汇集多种类型的数据 以支持初始设计和创建强大的数字双胞胎 现有版本的产品只支持半打CAD格式 有限的内部开发资源限制了增加对新格式支持的能力 解决方案 HOO
  • 成为一名合格的软件测试工程师,得掌握什么技能?

    在这个信息时代 软件行业的需求空前增长 而 软件测试 工程师作为 软件开发 过程中的重要角色 也越来越受企业的重视 那么 成为一名合格的软件测试工程师需要掌握什么技能呢 我结合多年的教学经验为大家总结出以下几点 1 掌握基础理论 要想楼层盖
  • Web前端安全学习-CSRF

    今天下午上了一堂前端安全的课 挺有意思 记录下来 在上课之前 我对安全的概念是 用户输入是不可信的 所有用户的输入都必须转义之后才入库 然后 上面这个这种方式 仅仅是防止SQL注入攻击 避免业务数据库被渗入 在数据库有了一层安全保护之后 攻
  • 门禁管理:我才知道这个技术,可以如此高效!

    随着社会的发展和科技的进步 门禁监控技术在各类场所得到了广泛的应用 门禁监控系统不仅仅是简单的出入口管理 更是对安全和隐私的重要保障 客户案例 企业办公大楼 在现代企业办公环境中 对于办公大楼的安全管理显得尤为重要 通过部署泛地缘科技推出的
  • 挑选知识付费平台不再迷茫:掌握这些技巧,轻松找到适合自己的平台

    明理信息科技知识付费saas租户平台 在当今的知识付费市场中 用户面临的选择越来越多 如何从众多知识付费平台中正确选择属于自己的平台呢 下面 我们将为您介绍明理信息科技知识付费平台相比同行的优势 帮助您做出明智的选择 一 创新的技术架构 高
  • 8大数字技术打造ESG发展新范式!工业“双碳”建设应该这么做

    随着国家 双碳 目标的提出 数字化 绿色化证进入历史交汇 协同发展 路径合一 相辅相成的重要阶段 在此背景下 中国信通院 中国航天航空 工业互联网产业联盟于2022年联合出版了 数字技术赋能工业碳达峰碳中和应用指南 以下简称 指南 今年又细
  • 高德地图API

    首先注册高德地图API账户成为开发者用户 然后申请key与密钥 应用管理 我的应用 创建新应用 加载高德地图
  • 图像分割-Grabcut法

    版权声明 本文为博主原创文章 转载请在显著位置标明本文出处以及作者网名 未经作者允许不得用于商业目的 本文的C 版本请访问 图像分割 Grabcut法 C CSDN博客 GrabCut是一种基于图像分割的技术 它可以用于将图像中的前景和背景
  • 在线视频编辑大师视频画中画批量制作、批量添加背景图片的视频剪辑教程

    在数字媒体时代 创意与个性化的内容成为吸引观众的关键 媒体梦工厂软件以其独特的画中画功能 让您的视频剪辑更具创意与视觉冲击力 无论是制作图片与视频的画中画效果 还是批量添加背景图片效果 都能助您一臂之力 1 软件的画中画功能 允许您将图片与
  • 淘宝商品类目接口API:获取淘宝商品分类类目信息

    cat get 获得淘宝分类详情 响应参数 名称 类型 必须 示例值 描述 info Mix 0 cid 16 parent cid 0 name 其他女装 is parent true status normal sort order 0
  • 深入了解Python中的进程控制和监控技巧,提高系统稳定性

    概要 在计算机系统中 进程管理是一个重要的任务 它涉及创建 启动 监控 终止和管理运行中的进程 Python作为一门强大的编程语言 提供了丰富的库和模块 使得进程管理变得相对容易 本文将介绍如何使用Python来实现系统进程管理 包括创建和
  • stm32毕设分享 stm32单片机酒精浓度酒驾检测系统 - 物联网 嵌入式

    文章目录 0 前言 1 简介 2 主要器件 3 实现效果 4 硬件设计 MQ 3酒精乙醇传感器模块 SIM800C模块 5 软件说明 系统框图
  • 5年经验之谈 —— 为什么要做自动化测试?

    自动化测试是一种软件测试方法 通过编写和使用自动化脚本和工具 以自动执行测试用例并生成结果 自动化旨在替代手动测试过程 提高测试效率和准确性 自动化测试可以覆盖多种测试类型 包括功能测试 性能测试 安全测试等 以确保软件系统在各个方面的质量
  • 【史上最细教程】CentOS7 下载安装 RabbitMQ(两种方式:手动安装 / Docker安装)

    文章目录 史上最细教程 CentOS7 下载安装 RabbitMQ 方式一 手动安装 1 下载安装Erlang RabbitMQ 2 防火墙 安全组端口放行 3 启动RabbitMQ服务 4 浏览器用户登录
  • Win32 文件分割合并

    CFileSplitUtils h pragma once include