跳转至

MyBlog

CSS 的复合选择器

什么是复合选择器

在 CSS 中,可以根据选择器的类型把选择器分为基础选择器和复合选择器,复合选择器是建立在基础选择器之上,对基本选择器进行组合形成的。

  • 复合选择器可以更准确、更高效的选择目标元素(标签)
  • 复合选择器是由两个或多个基础选择器,通过不同的方式组合而成的
  • 常用的复合选择器包括:后代选择器、子选择器、并集选择器、伪类选择器等等

后代选择器(重要)

后代选择器又称为包含选择器,可以选择父元素里面子元素。其写法就是把外层标签写在前面,内层标签写在后面,中间用空格分隔。当标签发生嵌套时,内层标签就成为外层标签的后代。

Bash
元素1 元素2 { 样式声明 }

上述语法表示选择元素 1 里面的所有元素 2 (后代元素)。

例如:

Bash
ul li { 样式声明 } /* 选择 ul 里面所有的 li标签元素 */
  • 元素1 和 元素2 中间用空格隔开
  • 元素1 是父级,元素2 是子级,最终选择的是元素2
  • 元素2 可以是儿子,也可以是孙子等,只要是元素1 的后代即可
  • 元素1 和 元素2 可以是任意基础选择器

子选择器(重要)

子元素选择器(子选择器)只能选择作为某元素的最近一级子元素。简单理解就是选亲儿子元素.

语法:

Bash
元素1 > 元素2 { 样式声明 }

上述语法表示选择元素1 里面的所有直接后代(子元素) 元素2。

例如:

Bash
div > p { 样式声明 } /* 选择 div 里面所有最近一级 p 标签元素 */
  • 元素1 和 元素2 中间用 大于号 隔开
  • 元素1 是父级,元素2 是子级,最终选择的是元素2
  • 元素2 必须是亲儿子,其孙子、重孙之类都不归他管. 你也可以叫他 亲儿子选择器

并集选择器 (重要)

并集选择器是各选择器通过英文逗号(,)连接而成,任何形式的选择器都可以作为并集选择器的一部分。

例如:

Bash
ul,div { 样式声明 } /* 选择 ul  div标签元素 */
  • 元素1 和 元素2 中间用逗号隔开
  • 逗号可以理解为和的意思
  • 并集选择器通常用于集体声明

伪类选择器

伪类选择器书写最大的特点是用冒号(:)表示,比如 :hover 、 :first-child 。

因为伪类选择器很多,比如有链接伪类、结构伪类等,所以这里先给大家讲解常用的链接伪类选择器。

链接伪类选择器

链接伪类选择器注意事项
  1. 为了确保生效,请按照 LVHA 的循顺序声明 :link-:visited-:hover-:active。
  2. 记忆法:love hate 或者 lv 包包 hao 。
  3. 因为 a 链接在浏览器中具有默认样式,所以我们实际工作中都需要给链接单独指定样式。
链接伪类选择器

链接伪类选择器实际工作开发中的写法:

HTML
1
2
3
4
5
6
7
8
/* a 是标签选择器 所有的链接 */
a {
color: gray;
}
/* :hover 是链接伪类选择器 鼠标经过 */
a:hover {
color: red; /* 鼠标经过的时候,由原来的 灰色 变成了红色 */
}

:focus 伪类选择器

:focus 伪类选择器用于选取获得焦点的表单元素。 焦点就是光标,一般情况 类表单元素才能获取,因此这个选择器也主要针对于表单元素来说。

HTML
1
2
3
input:focus {
background-color:yellow;
}

复合选择器总结

选择器 作用 特征 使用情况 隔开符号及用法
后代选择器 用来选择后代元素 可以是子孙后代 较多 符号是空格.nav a
子代选择器 选择最近一级元素 只能选亲儿子 较少 符号是大于 .nav >p
并集选择器 选择某些相同样式的元素 可以用于集体申明 较多 符号是逗号 .nav,.header
链接伪类选择器 选择不同状态的链接 选择不同状态的链接 较多 重点记住a{}和a:hover 实际开发的写法
:focus选择器 选择获得光标的表单 选择获得光标的表单 较少 input:focus 记住这个写法

ftp上传下载文件

libcurl从ftp上获取文件列表信息,包含中文

C++
inline std::wstring to_wstring(std::string const& str)
{
    std::wstring ustr;
    int const length = MultiByteToWideChar(
        CP_UTF8, 0,
        str.c_str(), static_cast<int>(str.length()),
        nullptr, 0);
    if (length <= 0)
        return ustr;
    ustr.resize(static_cast<size_t>(length));
    int const result = MultiByteToWideChar(
        CP_UTF8, 0,
        str.c_str(), static_cast<int>(str.length()),
        const_cast<wchar_t*>(ustr.data()), length);
    if (result != length)
        ustr.clear();
    return ustr;
}

std::wstring_convert<std::codecvt_utf8<wchar_t>> utf8Converter;

size_t WriteCallback(char* contents, size_t size, size_t nmemb, std::string* userp)
{
    size_t totalSize = size * nmemb;
    userp->append(contents, totalSize);
    return totalSize;
}
bool GetFtpFileList(const char* ftpUrl, const char* username, const char* password, CString& strResult)
{
    CURL* curl;
    CURLcode res;

    curl_global_init(CURL_GLOBAL_DEFAULT);
    curl = curl_easy_init();

    if (curl)
    {
        std::string response;

        struct curl_slist* headers = NULL;
        headers = curl_slist_append(headers, "Accept: */*");
        string strUserName = username;
        string strPassWord = password;
        curl_easy_setopt(curl, CURLOPT_URL, ftpUrl);
        curl_easy_setopt(curl, CURLOPT_USERPWD, (strUserName + ":" + strPassWord).c_str());  // 密码和用户名以":"连接,如:"username:password"
        curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, WriteCallback);
        curl_easy_setopt(curl, CURLOPT_WRITEDATA, &response);
        curl_easy_setopt(curl, CURLOPT_FTP_USE_EPRT, 1L);  // 使用更安全的EPR命令(如果支持)
        curl_easy_setopt(curl, CURLOPT_FTPLISTONLY, 1L);   // 只列出文件,不下载

        res = curl_easy_perform(curl);

        if (res != CURLE_OK)
        {
            fprintf(stderr, "curl_easy_perform() failed: %s\n", curl_easy_strerror(res));
        }
        else
        {
            // 尝试将字符串转换为宽字符串以便正确显示中文等非ASCII字符
            std::wstring wideResponse = utf8Converter.from_bytes(response);
            std::wcout << L"FTP directory listing:\n";
            std::wcout << wideResponse.c_str() << std::endl;
            strResult = wideResponse.c_str();
        }

        curl_slist_free_all(headers);
        curl_easy_cleanup(curl);
    }

    curl_global_cleanup();

    return (res == CURLE_OK);
}
int GetFtpDirectoryList(const char* ftpUrl, const char* username, const char* password, const char* strRemotePath, vector<CString>& fileList)
{
    CURL* curl;
    CURLcode res;
    CString strRemote = strRemotePath;
    curl_global_init(CURL_GLOBAL_DEFAULT);
    curl = curl_easy_init();

    if (curl)
    {
        std::string response;

        curl_slist* headers = NULL;
        headers = curl_slist_append(headers, "Accept: */*");
        string strUserName = username;
        string strPassWord = password;
        curl_easy_setopt(curl, CURLOPT_URL, ftpUrl);
        curl_easy_setopt(curl, CURLOPT_USERPWD, (strUserName + ":" + strPassWord).c_str());  // 密码和用户名以":"连接,如:"username:password"
        curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, WriteCallback);
        curl_easy_setopt(curl, CURLOPT_WRITEDATA, &response);
        curl_easy_setopt(curl, CURLOPT_FTP_USE_EPRT, 1L);  // 使用更安全的EPR命令(如果支持)
        curl_easy_setopt(curl, CURLOPT_DIRLISTONLY, 1L);   // 只列出目录内容

        res = curl_easy_perform(curl);

        if (res == CURLE_OK)
        {
            // 解析响应字符串,提取文件和子目录名
            // 这里假设FTP服务器返回的是NLST格式,每行一个条目
            std::wstring wideResponse = utf8Converter.from_bytes(response);
            CString strResult = wideResponse.c_str();
            vector<CString> dirVec;
            dirVec.clear();
            int nFind = strResult.Replace(_T("\r\n"), _T("\r\n"));
            if (nFind > 0)
            {
                // 使用CString::Find()函数找到第一个\r\n的位置
                int pos = strResult.Find(_T("\r\n"));
                // 循环遍历所有\r\n分隔符,并将字符串拆分成一个一个的
                while (pos != -1)
                {
                    // 获取当前分隔符之前的子串
                    CString subStr = strResult.Left(pos);
                    if ((subStr.CompareNoCase(_T(".")) == 0)
                        ||(subStr.CompareNoCase(_T("..")) == 0))
                    {
                        continue;
                    }

                    if (subStr.GetAt(subStr.GetLength() - 1) == _T('/'))
                    {
                        dirVec.push_back(strRemote + subStr);
                    }
                    strRemote.Replace(_T("/"), _T("\\"));

                    fileList.push_back(strRemote + subStr);
                    // 将字符串指针移动到下一个分隔符的位置
                    strResult = strResult.Mid(pos + 2);
                    // 重新获取分隔符的位置
                    pos = strResult.Find(_T("\r\n"));
                }
                // 处理最后一个子串

                if (!strResult.IsEmpty())
                {
                    if (strResult.GetAt(strResult.GetLength() - 1) == _T('/'))
                    {
                        dirVec.push_back(strRemote + strResult);
                    }
                    fileList.push_back(strRemote + strResult);
                }
            }
            else
            {
                fileList.push_back(strRemote + strResult);
            }
            // 对于每个子目录,进行递归调用

            for (auto entry : dirVec)
            {
                string strFtp = ftpUrl;
                string strEntry = (LPCSTR)(CStringA)(entry);
                strEntry += +"/";
                string subDirUrl = strFtp + strEntry;
                GetFtpDirectoryList(subDirUrl.c_str(), username, password, strEntry.c_str(), fileList);
            }
        }
        else
        {
            fprintf(stderr, "curl_easy_perform() failed: %s\n", curl_easy_strerror(res));
        }

        curl_slist_free_all(headers);
        curl_easy_cleanup(curl);
    }

    curl_global_cleanup();

    return (res == CURLE_OK);
}

CPP

具体调用函数为:

C++
static void ADSKMyGroupTEST() {

        //const char* ftpServer = "ftp://127.0.0.1:21/";
        //const char* ftpUsername = "ftp";
        //const char* ftpPassword = "ftp";

        CString strFtpSerVer = CUtility::getValueByKey(INI_SETTING_FTPSETTING, INI_NODE_FTPSERVER);
        CString ftpServer = _T("ftp://") + strFtpSerVer;
        CString ftpUsername = CUtility::getValueByKey(INI_SETTING_FTPSETTING, INI_NODE_FTPUSER);
        CString ftpPassword = CUtility::getValueByKey(INI_SETTING_FTPSETTING, INI_NODE_FTPPASSWD);
        CString strLocal = CUtility::getValueByKey(INI_SETTING_FTPSETTING, INI_NODE_FTPLOCALPATH);
        std::vector<CString> allFileList;

        GetFtpDirectoryList((LPCSTR)(LPCTSTR)ftpServer, (LPCSTR)(LPCTSTR)ftpUsername, (LPCSTR)(LPCTSTR)ftpPassword, "", allFileList);

        FtpManage ftp;
        ftp.ConnectFtp();
        for (const auto& item : allFileList)
        {
            CString localPath = strLocal + item;  // 设置本地存储路径

            if (localPath.Find(_T(".")) < 0)  // 如果是目录
            {
                ::CreateDirectory(localPath, NULL);
            }
            else  // 如果是文件
            {
                CString ftpFilePath = _T("/") + item;
                string outputfile = (LPCSTR)(CStringA)(localPath);
                ftp.GetFileFromFtp(ftpFilePath, localPath);
            }
        }
        ftp.CloseFtpConnect();
    }

CPP

从ftp下载文件

ftpmanage.h

C++
#pragma once
#include <afxinet.h>
#include <vector>
using namespace std;

//对 ftp文件进行下载及相关的操作类
class FtpManage
{
public:
    FtpManage();
    ~FtpManage();
private:
    void InitFtpSetting();

    CString m_strFTPServer;//ftp服务器
    CString m_strFTPPassive;//passive标记
    CString m_strFTPRoot;//ftp根目录
    CString m_strFTPUser;//ftp账号
    CString m_strFTPPassWd;//ftp密码

    CInternetSession* m_pInetSession;
    CFtpConnection* m_pFtpConnection;
    vector<CString> getSplitStringVector(CString strFtpPath, LPCTSTR param2 = _T("/"));
public:


    int ConnectFtp();

    //从ftp下载文件
    int GetFileFromFtp(/*CString strFtpPath, */CString strFtpFileName, CString strDwgSavePath);

    //上传文件到ftp
    int PutFileToFtp(CString strLocalFilePath, CString strFtpPath);
    int PutFileToFtpEx(CString strLocalFilePath, CString strFtpPath, CString strFtpFileName);

    //分级设置ftp dir 
    int SetCurrentFtpDir(CString strFtpPath);

    //关闭ftp连接
    int CloseFtpConnect();
    //获取ftp目录中的所有文件//获取中文文件名乱码,不可用
    vector<CString> getAllFileFromFtpServer();
};

CPP

ftpmanage.cpp

C++
#include "stdafx.h"
#include "FtpManage.h"
#include <xlocbuf>
#include <codecvt>

FtpManage::FtpManage():m_pInetSession(NULL),m_pFtpConnection(NULL)
{
    InitFtpSetting();
}

FtpManage::~FtpManage()
{

}

void FtpManage::InitFtpSetting()
{
    m_strFTPServer = CUtility::getValueByKey(INI_SETTING_FTPSETTING, INI_NODE_FTPSERVER);
    m_strFTPPassive = CUtility::getValueByKey(INI_SETTING_FTPSETTING, INI_NODE_FTPPASSIVE);
    m_strFTPRoot = CUtility::getValueByKey(INI_SETTING_FTPSETTING, INI_NODE_FTPROOT);
    m_strFTPUser = CUtility::getValueByKey(INI_SETTING_FTPSETTING, INI_NODE_FTPUSER);
    m_strFTPPassWd = CUtility::getValueByKey(INI_SETTING_FTPSETTING, INI_NODE_FTPPASSWD);
}

std::vector<CString> FtpManage::getSplitStringVector(CString strFtpPath, LPCTSTR param2 /*= _T("/")*/)
{
    vector<CString> retVec;
    retVec.clear();
    int nFind = strFtpPath.Replace(_T("/"), _T("/"));
    if (nFind <= 0)
    {
        retVec.push_back(_T("/"));
        return retVec;
    }
    for (int i=0; i<=nFind; i++)
    {
        retVec.push_back(CUtility::SubString(strFtpPath, _T("/"), i) + _T("/"));
    }
    return retVec;
}

int FtpManage::GetFileFromFtp(/*CString strFtpPath, */CString strFtpFileName, CString strDwgSavePath)
{
    //if (SetCurrentFtpDir(strFtpPath)<0)
    //{
    //  return -1;
    //}
    CFtpFileFind findFile(m_pFtpConnection);
    if (findFile.FindFile(strFtpFileName, INTERNET_FLAG_DONT_CACHE))
    {
        if (!m_pFtpConnection->GetFile(strFtpFileName,strDwgSavePath, FALSE, FILE_ATTRIBUTE_NORMAL, FTP_TRANSFER_TYPE_BINARY, 1))
        {
            DWORD dw = GetLastError();
            CString sError;
            AfxMessageBox(_T("ftp getfile error :%d"), dw);
            return -3;
        }
    }
    else
    {
        return -2;
    }

    return 0;
}

int FtpManage::ConnectFtp()
{
    CWaitCursor wait;

    CString m_sDomainName(m_strFTPServer);
    CString m_ftpUser(m_strFTPUser);
    CString m_ftpPassword(m_strFTPPassWd);

    m_pFtpConnection = NULL;
    m_pInetSession = new CInternetSession(_T("ESAPP"), 1, PRE_CONFIG_INTERNET_ACCESS, NULL, NULL, INTERNET_FLAG_DONT_CACHE);
    if (!m_pInetSession)
    {
        return -1;
    }

    CString strFtpSite = m_sDomainName;
    CString strServerName;
    CString strObject;
    INTERNET_PORT nPort;
    DWORD dwServiceType;

    //检查URL是否正确
    if (!AfxParseURL(strFtpSite, dwServiceType, strServerName, strObject, nPort) || dwServiceType == AFX_INET_SERVICE_UNK)
    {
        CString strFtpURL = _T("ftp://");
        strFtpURL += strFtpSite;

        if (!AfxParseURL(strFtpURL, dwServiceType, strServerName, strObject, nPort))
        {
            return -2;
        }
    }

    if ((dwServiceType == INTERNET_SERVICE_FTP) && !strServerName.IsEmpty())
    {
        try
        {
            //AfxMessageBox(strServerName + _T("\r") + m_ftpUser + _T("\r") + m_ftpPassword);
            if (m_strFTPPassive == _T("TRUE"))
            {
                m_pFtpConnection = m_pInetSession->GetFtpConnection(strServerName, m_ftpUser, m_ftpPassword, nPort, TRUE);
            }
            else
            {
                m_pFtpConnection = m_pInetSession->GetFtpConnection(strServerName, m_ftpUser, m_ftpPassword, nPort, FALSE);
            }
        }
        catch (CInternetException* pEx)
        {
            CString strInteError = _T("");
            TCHAR szErr[1024];
            if (pEx->GetErrorMessage(szErr, 1024))
            {
                strInteError.Format(_T("%s"), szErr);
                pEx->Delete();
            }
            AfxMessageBox(strInteError);
            return -3;
        }
    }
    return 0;
}

int FtpManage::PutFileToFtp(CString strLocalFilePath, CString strFtpPath)
{
    CString sFileName;
    int nFind = strLocalFilePath.ReverseFind(_T('\\'));
    sFileName = strLocalFilePath.Mid(nFind+1);

    return PutFileToFtpEx(strLocalFilePath, strFtpPath, sFileName);
}

int FtpManage::PutFileToFtpEx(CString strLocalFilePath, CString strFtpPath, CString strFtpFileName)
{
    int nRes = SetCurrentFtpDir(strFtpPath);
    if (nRes!=0)
    {
        return -1;
    }
    CFtpFileFind findFile(m_pFtpConnection);
    if (findFile.FindFile(strFtpFileName,INTERNET_FLAG_DONT_CACHE))
    {
        m_pFtpConnection->Remove(strFtpFileName);
    }
    if (!m_pFtpConnection->PutFile(strLocalFilePath,strFtpFileName))
    {
        DWORD dw = GetLastError();
        int nError = (int)dw;
        CString strInterError;
        strInterError.Format(_T("%d"), nError);
        AfxMessageBox(strInterError);
        return -2;
    }
    return 0;
}

int FtpManage::SetCurrentFtpDir(CString strFtpPath)
{
    if (m_pFtpConnection==NULL)
    {
        return -1;
    }
    //会存在多级 然后一次设置下去 如果失败了 就返回问题
    strFtpPath.Replace(_T("\\"), _T("/"));

    vector<CString> vecCatalogue;
    //gMyString.Split(strFtpPath, _T("/"), vecCatalogue);
    vecCatalogue = getSplitStringVector(strFtpPath, _T("/"));
    for (int i=0;i<vecCatalogue.size();i++)
    {
        CString sTempCatalogue = vecCatalogue[i];
        if (sTempCatalogue==_T(""))
        {
            continue;
        }
        if (!m_pFtpConnection->SetCurrentDirectory(sTempCatalogue))
        {
            if (!m_pFtpConnection->CreateDirectory(sTempCatalogue))
            {
                return -2;
            }
            if (!m_pFtpConnection->SetCurrentDirectory(sTempCatalogue))
            {
                return -3;
            }
        }
    }
    return 0;
}

int FtpManage::CloseFtpConnect()
{
    if (m_pFtpConnection!=NULL)
    {
        m_pFtpConnection->Close();
        delete m_pFtpConnection;
        m_pFtpConnection = NULL;
    }
    if (m_pInetSession!=NULL)
    {
        m_pInetSession->Close();
        delete m_pInetSession;
        m_pInetSession = NULL;
    }
    return 0;
}
wchar_t * ANSIToUnicode(const char* str)
{
    int textlen;
    wchar_t * result;
    textlen = MultiByteToWideChar(CP_ACP, 0, str, -1, NULL, 0);
    result = (wchar_t *)malloc((textlen + 1) * sizeof(wchar_t));
    memset(result, 0, (textlen + 1) * sizeof(wchar_t));
    MultiByteToWideChar(CP_ACP, 0, str, -1, (LPWSTR)result, textlen);
    return result;
}

//获取中文文件名乱码
std::vector<CString> FtpManage::getAllFileFromFtpServer()
{
    vector<CString> tmpVec;
    tmpVec.clear();
    if (SetCurrentFtpDir(m_strFTPServer) < 0)
    {
        return tmpVec;
    }
    try
    {
        // use a file find object to enumerate files
        CFtpFileFind findFile(m_pFtpConnection);
        CString strName, strDirectory;
        m_pFtpConnection->GetCurrentDirectory(strDirectory);
        // start looping
        BOOL bWorking = findFile.FindFile(_T("*"));

        //while (bWorking)
        //{
        //  bWorking = findFile.FindNextFile();
        //  tmpVec.push_back(findFile.GetFileURL());
        //  //_tprintf_s(_T("%s\n"), (LPCTSTR)findFile.GetFileURL());
        //}

        BOOL bFind = findFile.FindFile(_T("/"), INTERNET_FLAG_EXISTING_CONNECT);
        bool flag = false;
        while (bFind)
        {
            bFind = findFile.FindNextFile();

            if (findFile.IsDots())
            {
                continue;
            }
            CString remoteFileName = findFile.GetFileName();
            // 转换为 UTF-8 编码
            std::string fileNameUtf8 = std::wstring_convert<std::codecvt_utf8<wchar_t>>().to_bytes(findFile.GetFileName());

            CString strName = fileNameUtf8.c_str();

    /*      CString remoteFilePath = remoteDir + remoteFileName;
            CString localFilePath = localDir + remoteFileName;*/
            //flag = 1为获取目录下的子文件,flag = 0为获取当前文件夹下的所有子文件。
            if (flag)
            {
                if (findFile.IsDirectory())
                {
                    // 如果是目录,递归下载其中的文件和子目录
                    //CreateDirectory(localFilePath, nullptr);

                    strDirectory = strDirectory + _T("/") + strName;
                    tmpVec.push_back(strName);
                }
            }
            else
            {
                tmpVec.push_back(strName);
            }
        }

        findFile.Close();
    }
    catch (CInternetException* pEx)
    {
        TCHAR sz[1024];
        pEx->GetErrorMessage(sz, 1024);
        _tprintf_s(_T("ERROR!  %s\n"), sz);
        pEx->Delete();
    }
    return tmpVec;
}

css第03天

浮动

1、传统网页布局的三种方式

​ CSS 提供了三种传统布局方式(简单说,就是盒子如何进行排列顺序):

  • 普通流(标准流)

  • 浮动

  • 定位

这三种布局方式都是用来摆放盒子的,盒子摆放到合适位置,布局自然就完成了。

注意:实际开发中,一个页面基本都包含了这三种布局方式(后面移动端学习新的布局方式) 。

2、标准流(普通流/文档流)

所谓的标准流: 就是标签按照规定好默认方式排列

  1. 块级元素会独占一行,从上向下顺序排列。常用元素:div、hr、p、h1~h6、ul、ol、dl、form、table
  2. 行内元素会按照顺序,从左到右顺序排列,碰到父元素边缘则自动换行。常用元素:span、a、i、em 等

以上都是标准流布局,我们前面学习的就是标准流,标准流是最基本的布局方式。

3、为什么需要浮动?

​ 总结: 有很多的布局效果,标准流没有办法完成,此时就可以利用浮动完成布局。 因为浮动可以改变元素标签默认的排列方式.

​ 浮动最典型的应用:可以让多个块级元素一行内排列显示。

​ 网页布局第一准则:多个块级元素纵向排列找标准流,多个块级元素横向排列找浮动

4、什么是浮动?

​ float 属性用于创建浮动框,将其移动到一边,直到左边缘或右边缘触及包含块或另一个浮动框的边缘。

语法:

CSS
 选择器 { float: 属性值; }

1571543209934

5、浮动特性

加了浮动之后的元素,会具有很多特性,需要我们掌握的.

1、浮动元素会脱离标准流(脱标:浮动的盒子不再保留原先的位置)

1571544664994

2、浮动的元素会一行内显示并且元素顶部对齐

1571544725757

注意:

​ 浮动的元素是互相贴靠在一起的(不会有缝隙),如果父级宽度装不下这些浮动的盒子,多出的盒子会另起一行对齐。

3、浮动的元素会具有行内块元素的特性

​ 浮动元素的大小根据内容来决定

​ 浮动的盒子中间是没有缝隙的

6、浮动元素经常和标准流父级搭配使用

为了约束浮动元素位置, 我们网页布局一般采取的策略是:

​ 先用标准流父元素排列上下位置, 之后内部子元素采取浮动排列左右位置. 符合网页布局第一准侧

1571544991989

常见网页布局

浮动布局注意点

1、浮动和标准流的父盒子搭配。

先用标准流的父元素排列上下位置, 之后内部子元素采取浮动排列左右位置

2、一个元素浮动了,理论上其余的兄弟元素也要浮动。

一个盒子里面有多个子盒子,如果其中一个盒子浮动了,其他兄弟也应该浮动,以防止引起问题。

浮动的盒子只会影响浮动盒子后面的标准流,不会影响前面的标准流.

案例1

image-20231214171207034

HTML
<style>
    div {
        width: auto;
        height: 120px;
        /* 常见的写法,以下三种都可以:
        以上方法是让块级元素水平居中,
        行内元素或者行内块元素水平居中给其父元素添加 text-align:center 即可。
        margin-left: auto; margin-right: auto;
        margin: auto;
        margin: 0 auto; */
        margin: 0 auto;
        /* margin: 20px; */
    }
    .box,
    .box1,
    .box2,
    .box3,
    .box4,
    .box5 {
        /* float: left; */
        /* 如果父级宽度装不下这些浮动的盒子, 多出的盒子会另起一行对齐。 */
        width: 300px;
    }
    .item1 {
        float: left;
        width: 100px;
        height: 100px;
        background-color: aqua;
    }
    .item2 {
        float: left;
        width: 100px;
        height: 100px;
        background-color: beige;
    }
    .item3 {
        float: left;
        width: 100px;
        height: 100px;
        background-color: blueviolet;
        /* float: right; */
    }
</style>
<!-- 先用标准流的父元素排列上下位置, 之后内部子元素采取浮动排列左右位置. 符合网页布局第一准侧. -->
<div class="box">
    <div class="item1"></div>
    <div class="item2"></div>
    <div class="item3"></div>
</div>
<div class="box1">
    <div class="item1">box1</div>
    <div class="item2">box1</div>
    <div class="item3">box1</div>
</div>
<div class="box2">
    <div class="item1">box2</div>
    <div class="item2">box2</div>
    <div class="item3">box2</div>
</div>
<div class="box3">
    <div class="item1">box3</div>
    <div class="item2">box3</div>
    <div class="item3">box3</div>
</div>
<div class="box4">
    <div class="item1">box4</div>
    <div class="item2">box4</div>
    <div class="item3">box4</div>
</div>
<div class="box5">
    <div class="item1">box5</div>
    <div class="item2">box5</div>
    <div class="item3">box5</div>
</div>

案例2 小米手机页面

image-20231214173033431

HTML
<style>
    .box{
        background-color: antiquewhite;
        width: 1200px;
        height: auto;
        margin: 0 auto;
    }
    .left{
        width: 300px;
        height: 610px;
        background-color: rgb(151, 151, 214);
        float: left;
        line-height: 610px;
        text-align: center;
        font-size: 50px;
    }
    .right{
        width: 840px;
        height: 610px;
        float: left;
        padding-left: 10px;
        /* margin-left: 10px; */
        /* background-color: skyblue; */
    }
    .right div{
        width: 200px;
        height: 300px;
        float: left;
        margin: 0 10px 10px 0;
        line-height: 300px;
        text-align: center;
        font-size: 30px;
    }
    .right div{
        background-color: pink;
    }
</style>
<div class="box">
    <div class="left">
        手机新浪网
    </div>
    <div class="right">
        <div class="item1">1</div>
        <div class="item2">2</div>
        <div class="item3">3</div>
        <div class="item4">4</div>
        <div class="item5">5</div>
        <div class="item6">6</div>
        <div class="item7">7</div>
        <div class="item8">8</div>
    </div>
</div>

四、清除浮动

1、为什么需要清除浮动?

​ 由于父级盒子很多情况下,不方便给高度,但是子盒子浮动又不占有位置,最后父级盒子高度为 0 时,就会影响下面的标准流盒子。

1571555883628

2、清除浮动本质

清除浮动的本质是清除浮动元素造成的影响:浮动的子标签无法撑开父盒子的高度

注意:

  • 如果父盒子本身有高度,则不需要清除浮动
  • 清除浮动之后,父级就会根据浮动的子盒子自动检测高度。
  • 父级有了高度,就不会影响下面的标准流了

3、清除浮动样式

语法:

CSS
 选择器{clear:属性值;} 

1571555980419

我们实际工作中, 几乎只用 clear: both;

清除浮动的策略是: 闭合浮动.

4、清除浮动的多种方式

4.1、额外标签法

额外标签法也称为隔墙法,是 W3C 推荐的做法。

使用方式:

​ 额外标签法会在浮动元素末尾添加一个空的标签。

HTML
例如 <div style="clear:both"></div>,或者其他标签(如<br />等)。

​ 优点: 通俗易懂,书写方便

​ 缺点: 添加许多无意义的标签,结构化较差

​ 注意: 要求这个新的空标签必须是块级元素。

总结:

​ 1、清除浮动本质是?

​ 清除浮动的本质是清除浮动元素脱离标准流造成的影响

​ 2、清除浮动策略是?

​ 闭合浮动. 只让浮动在父盒子内部影响,不影响父盒子外面的其他盒子.

​ 3、额外标签法?

​ 隔墙法, 就是在最后一个浮动的子元素后面添

​ 4、加一个额外标签, 添加 清除浮动样式.

​ 实际工作可能会遇到,但是不常用

4.2、父级添加 overflow 属性

可以给父级添加 overflow 属性,将其属性值设置为 hidden、 auto 或 scroll 。

例如:

CSS
overflow:hidden | auto | scroll;

优点:代码简洁

缺点:无法显示溢出的部分

注意:是给父元素添加代码

4.3、父级添加after伪元素

:after 方式是额外标签法的升级版。给父元素添加:

CSS
 .clearfix:after {  
   content: ""; 
   display: block; 
   height: 0; 
   clear: both; 
   visibility: hidden;  
 } 
 .clearfix {  /* IE6、7 专有 */ 
   *zoom: 1;
 }   

优点:没有增加标签,结构更简单

缺点:照顾低版本浏览器

代表网站: 百度、淘宝网、网易等

4.4、父级添加双伪元素

给父元素添加

CSS
 .clearfix:before,.clearfix:after {
   content:"";
   display:table; 
 }
 .clearfix:after {
   clear:both;
 }
 .clearfix {
    *zoom:1;
 }   

优点:代码更简洁

缺点:照顾低版本浏览器

代表网站:小米、腾讯等

总结

为什么需要清除浮动?

  1. 父级没高度。
  2. 子盒子浮动了。
  3. 影响下面布局了,我们就应该清除浮动了。

1571556500074

五、PS 切图

1、图层切图

HTML
最简单的切图方式:右击图层 → 导出 → 切片。

2、切片切图

2.1、利用切片选中图片

Text Only
 利用切片工具手动划出

2.2、导出选中的图片

HTML
文件菜单 → 存储为 web 设备所用格式 → 选择我们要的图片格式 → 存储 。

3、PS插件切图

​ Cutterman 是一款运行在 Photoshop 中的插件,能够自动将你需要的图层进行输出,以替代传统的手工 "导出 web 所用格式" 以及使用切片工具进行挨个切图的繁琐流程。

官网:http://www.cutterman.cn/zh/cutterman

注意:Cutterman 插件要求你的 PS 必须是完整版,不能是绿色版,所以大家需要安装完整版本。

1571556821045

保存标记

DBMOD,只读

  • //声明设置数据库模式的函数
C++
extern long acdbSetDbmod(AcDbDatabase * pDb, long newVal); 
  • 另外还有方法的,AcApDocment类有两个成员接口,pushDbmod和popDbmod

将子目录中的所有文件拷贝到子目录中并添加-

先遍历子目录中所有符合要求的文件

Python
def get_files_in_directory(directory, fileension):
    # 创建一个字典来存储目录和文件名
    directory_map = {}

    # 遍历目录及其子目录
    for root, dirs, files in os.walk(directory):
        # 将当前目录下的所有文件名添加到字典中
        for file in files:
            if file.endswith(fileension):
                # 获取当前目录名
                fileName = os.path.join(root, file)
                current_dir = root.replace(path, "")
                # current_dir = re.sub(r'\\', '-',os.path.dirname(fileName))
                current_dir = re.sub(r"\\", "-", current_dir)
                # 如果当前目录名不在字典中,则添加它
                if current_dir not in directory_map:
                    directory_map[current_dir] = []
                directory_map[current_dir].append(file)

    return directory_map

将目录和文件名获取后进行拆解

Python
def get_special_file(directory_map, isCopy=False):
    """
    处理特殊文件。

    根据提供的目录映射字典,对每个文件进行处理。如果指定复制,则将文件从原始位置复制到新位置。

    参数:
        directory_map (dict): 一个字典,键为目录名,值为该目录下的文件列表。
        isCopy (bool): 是否复制文件,默认为False。

    返回:
        list: 包含处理过的文件名的列表。
    """
    # 创建一个列表来存储处理过的文件名
    fileNameList = []
    # 遍历目录映射字典中的每个目录和文件
    for directory, files in directory_map.items():
        for file in files:
            # 排除空目录
            if directory != "":
                # 如果目录名末尾没有"-",则添加之
                if directory[-1] != "-":
                    directory += "-"
                # 组合得到文件的完整名
                svgName = directory + file
                # 准备文件的源路径和目标路径
                preSvgName = path + directory.replace("-", "\\") + file
                # 将文件名(不包含扩展名)添加到列表中
                fileNameList.append(svgName.split(".")[0])
                # 准备文件的新路径
                newSvgName = path + svgName
                # 如果指定复制文件,则执行复制操作
                if isCopy:
                    os.system(f"copy {preSvgName} {newSvgName}")

    # 返回处理过的文件名列表
    return fileNameList

将信息写入json

Python
1
2
3
4
5
6
7
 # 将filenameList写入list.json文件,key值为filename
    final_dict = {"data": [{"name": fileName} for fileName in fileNameList]}
    # 转换为 JSON
    json_string = json.dumps(final_dict, ensure_ascii=False)
    with open(f"{path}\\list.json", "w", encoding="utf-8") as file:
        file.write(json_string)
    print(f"{path}\\list.json")

完整的代码如下所示

Python
# 获取目录下所有的dwg文件名和文件路径
import os
import re
import sys
import json


def get_files_in_directory(directory, fileension):
    # 创建一个字典来存储目录和文件名
    directory_map = {}

    # 遍历目录及其子目录
    for root, dirs, files in os.walk(directory):
        # 将当前目录下的所有文件名添加到字典中
        for file in files:
            if file.endswith(fileension):
                # 获取当前目录名
                fileName = os.path.join(root, file)
                current_dir = root.replace(path, "")
                # current_dir = re.sub(r'\\', '-',os.path.dirname(fileName))
                current_dir = re.sub(r"\\", "-", current_dir)
                # 如果当前目录名不在字典中,则添加它
                if current_dir not in directory_map:
                    directory_map[current_dir] = []
                directory_map[current_dir].append(file)

    return directory_map


# 获取当前脚本目录
def get_current_file_path():
    # 通过命令行参数获取当前脚本的绝对路径
    abs_file = sys.argv[0]
    # 将路径中的斜杠替换为反斜杠,以适应Windows系统
    windows_path = abs_file.replace("/", "\\")
    # 去除路径中的脚本文件名,仅保留目录部分
    windows_path = windows_path[: windows_path.rfind("\\")]
    # 返回脚本所在的目录路径
    return windows_path


def get_special_file(directory_map, isCopy=False):
    """
    处理特殊文件。

    根据提供的目录映射字典,对每个文件进行处理。如果指定复制,则将文件从原始位置复制到新位置。

    参数:
        directory_map (dict): 一个字典,键为目录名,值为该目录下的文件列表。
        isCopy (bool): 是否复制文件,默认为False。

    返回:
        list: 包含处理过的文件名的列表。
    """
    # 创建一个列表来存储处理过的文件名
    fileNameList = []
    # 遍历目录映射字典中的每个目录和文件
    for directory, files in directory_map.items():
        for file in files:
            # 排除空目录
            if directory != "":
                # 如果目录名末尾没有"-",则添加之
                if directory[-1] != "-":
                    directory += "-"
                # 组合得到文件的完整名
                svgName = directory + file
                # 准备文件的源路径和目标路径
                preSvgName = path + directory.replace("-", "\\") + file
                # 将文件名(不包含扩展名)添加到列表中
                fileNameList.append(svgName.split(".")[0])
                # 准备文件的新路径
                newSvgName = path + svgName
                # 如果指定复制文件,则执行复制操作
                if isCopy:
                    os.system(f"copy {preSvgName} {newSvgName}")

    # 返回处理过的文件名列表
    return fileNameList


if __name__ == "__main__":
    print(
        """
        fileension: \033[34mdwg,dxf, svg,pdf,bmp,png\033[0m等等
        isCopy: 是否复制文件到当前目录
        获取目录下所有的dwg文件名和文件路径
        """
    )
    # path = get_current_file_path()
    # if path is None:
    path = "E:\\Gitee\\vue\\gallery\\Gallery\\src\\assets\\icons\\"
    # 如果path最后不带\\结尾,则添加\\
    if path[-1] != "\\":
        path += "\\"
    print(path)
    isCopy = input("是否复制文件到当前目录?(y/n)")
    if isCopy == "y":
        isCopy = True
    else:
        isCopy = False
    fileension = input("请输入文件后缀名:")
    if fileension == "":
        fileension = ".svg"

    if fileension[0] != ".":
        fileension = "." + fileension

    directory_map = get_files_in_directory(path, fileension)
    fileNameList = get_special_file(directory_map, isCopy)

    # 将filenameList写入list.json文件,key值为filename
    final_dict = {"data": [{"name": fileName} for fileName in fileNameList]}
    # 转换为 JSON
    json_string = json.dumps(final_dict, ensure_ascii=False)
    with open(f"{path}\\list.json", "w", encoding="utf-8") as file:
        file.write(json_string)
    print(f"{path}\\list.json")

VC获取时间

获取当前时间

Text Only
    SYSTEMTIME localTimeMin;
    GetLocalTime(&localTimeMin);

Plain text

计算两个时间的时间差

Text Only
#include "atltime.h"
#include <iostream>

using std::cout;
using std::endl;
using std::wcout;

int main()
{
    // 获取最小起始时间
    CTime TimeObj;
    SYSTEMTIME sysTimeMin;  
    TimeObj.GetAsSystemTime(sysTimeMin);//获取可传参的时间最小值
    __time64_t time = TimeObj.GetTime(); //此时获取的time=0;

// 最小start time 1970.01.01.08.0.0,小于此值会出现断言失败。
//  CTime startTime = CTime(1970,01,01,08,00,00); 
    CTime startTime = CTime(sysTimeMin.wYear,sysTimeMin.wMonth,sysTimeMin.wDay,sysTimeMin.wHour,sysTimeMin.wMinute,sysTimeMin.wSecond); //最小Base start time 1970.1.2.0.0.0
    cout << "起始时间:";
    cout<<startTime.GetYear()<<"年"<<startTime.GetMonth()<<"月"<<startTime.GetDay()<<"日"<<startTime.GetHour()<<"时"<<startTime.GetMinute()<<"分"<<startTime.GetSecond()<<"秒"<<endl;   

    // 获取当前的本地时间
    SYSTEMTIME sysTime;
    ::GetLocalTime(&sysTime);   
    CTime curTime(sysTime); //curTime精确到秒, 10位

    // 计算当前时间与起始时间之间的时间差(秒)
    CTimeSpan timeSpan;
    timeSpan = curTime - startTime; // 由于设置的是最小起始时间,所以timeSpan与curTime的成员变量存储的值是相同的。

    cout << "时间差:";
    cout << timeSpan.GetDays() << "天" << timeSpan.GetHours() << "小时" << timeSpan.GetMinutes() << "分" << timeSpan.GetSeconds() << "秒" << endl;   
    cout << "总小时数:" << timeSpan.GetTotalHours() << "小时" << endl;
    cout << "总分钟数:" << timeSpan.GetTotalMinutes() << "分钟" << endl;   
    cout << "总秒数:" << timeSpan.GetTotalSeconds() << "秒" <<endl;

    CString strTime;
    // 将当前时间curTime对象格式化为字符串   
    strTime = curTime.Format(_T("%Y-%m-%d %H:%M:%S"));   
    // 输出格式化字符串,由于字符串使用Unicode字符,所以要使用wcout输出   
    wcout<<"日期时间:"<<(LPCTSTR)strTime<<endl;   

    system("pause");
    return 0;
}

Plain text

获取文件的修改时间

Text Only
FILETIME ftCreate, ftModify, ftAccess;
 CString strCreateTime, strModifyTime, strAccessTime;
 CString strFilePath = _T("");

 HANDLE hFile = CreateFile(strFilePath, GENERIC_READ,          // open for reading
  FILE_SHARE_READ,       // share for reading
  NULL,                            // default security
  OPEN_EXISTING,          // existing file only
  FILE_FLAG_BACKUP_SEMANTICS , // normal file
  NULL);

 SYSTEMTIME stLocal;
 if (!GetFileTime(hFile, &ftCreate, &ftAccess, &ftModify))
 {
  return ;
 }

 ZeroMemory(&stLocal, sizeof(SYSTEMTIME));
 FileTimeToSystemTime(&ftCreate, &stLocal);
 strCreateTime.Format("%04d-%02d-%02d %02d:%02d:%02d", stLocal.wYear, stLocal.wMonth, stLocal.wDay,  stLocal.wHour, stLocal.wMinute, stLocal.wSecond);   // 文件创建时间
 ZeroMemory(&stLocal, sizeof(SYSTEMTIME));
 FileTimeToSystemTime(&ftModify, &stLocal);
 strModifyTime.Format("%04d-%02d-%02d %02d:%02d:%02d", stLocal.wYear, stLocal.wMonth, stLocal.wDay,  stLocal.wHour, stLocal.wMinute, stLocal.wSecond); //  文件修改时间
 ZeroMemory(&stLocal, sizeof(SYSTEMTIME));
 FileTimeToSystemTime(&ftAccess, &stLocal);
 strAccessTime.Format("%04d-%02d-%02d %02d:%02d:%02d", stLocal.wYear, stLocal.wMonth, 

Nginx部署VUE项目到本地

下载nginx

下载链接:http://nginx.org/en/download.html

随便找一个,目前安装的是1.16.1

修改nginx配置

进入nginx的 conf/nginx.conf

修改Location的值,设为vue的路径

新建bat脚本,本地启动服务

Bash
1
2
3
4
5
@echo off
start /d "D:\Program Files\Redis-x64-5.0.14.1" redis-server.exe
start /d "E:\Gitee\JAVA\Project\myblog\BlogWeb\target" java -jar BlogWeb-1.0-SNAPSHOT.jar
start /d "E:\download\nginx-1.16.1\nginx-1.16.1" nginx.exe
pause

江河批量装换测试江河批量装换测试

具体代码

Python
import time
import os
from datetime import datetime

# 获取同一目录下的所有PDF文件的绝对路径
def getFileName(filedir):
    file_list = [os.path.join(root, filespath) \
                 for root, dirs, files in os.walk(filedir) \
                 for filespath in files \
                 if str(filespath).endswith('dwg')
                 ]
    return file_list if file_list else []

if __name__ == '__main__':
    path = input("\n请输入文件夹:")
    if path == "":
        path = "."
    if path[-1] != "/":
        path += "/"
    print("文件夹路径为:%s" % (path))
    startime = time.time()
    start_date = datetime.now()
    print("程序开始运行")
    print("程序开始时间:%s" % (start_date))
    dwg_fileName = getFileName(path)
    for dwg_file in dwg_fileName:
        print(dwg_file)
        os.system("E:\\Gitee\\project\\ZwWhForJhPdf\\Setup-x64\\ZwmStartUp.exe " + dwg_file)
    print("程序结束运行")
    endtime = time.time()
    end_date = datetime.now()
    print("程序结束时间:%s" % (end_date))
    print("文件总数为:", len(dwg_fileName))
    print("总耗时:", endtime - startime, "秒")

Windows 编译libcurl,添加openssl和zlib支持

编译zlib

https://zlib.net/

下载地址

编译win32

使用CMake编译程序

img

选中zlibstatic项目,构建zlib的静态库。(win32 release)

在E:\Gitee\Tool\zlib-1.2.13\build\Release下生成了zlibstaticd.lib文件,修改名为zlib.lib

zlib.hzconf.h文件复制到E:\Gitee\Tool\include目录下

编译x64

E:\Gitee\Tool\include\zlib ├─x64 │ ├─include │ │ zconf.h │ │ zlib.h │ └─lib │ zlib.lib └─x86 ├─include │ zconf.h │ zlib.h └─lib zlib.lib

编译openssl

编译步骤:

C++
1
2
3
4
5
6
7
8
9
1.安装ActivePerl-5.16.3.1604-MSWin32-x64-298023.msi
2.下载openssl  https://www.openssl.org/source/,解压,如:E:\GDAL\openssl
3.打开Developer Command Prompt for VS2015命令行工具cd进入E:\GDAL\openssl
4.perl Configure VC-WIN64A no-asm no-shared --prefix=E:\GDAL\dev           
可选项VC-WIN32 | VC-WIN64A | VC-WIN64I | VC-CE--prefix是最终拷贝的目录添加no-shared参数可生成静态库
VC-WIN32生成32位的VC-WIN64A生成64位
5.nmake 
6.nmake test #很慢可选
7.nmake install

CPP

打开x86 Native Tools Command Prompt for VS 2017工具,切换到openssl源码目录

  • perl Configure VC-WIN64A --release no-asm no-shared --prefix="E:\Gitee\Tool\include\openssl\x86"
  • nmake
  • nmake install
  • 新建安装目录E:\Gitee\Tool\include\openssl\x86

img

打开x64 Native Tools Command Prompt for VS 2017工具,切换到openssl源码目录

  • 新建安装目录E:\Gitee\Tool\include\openssl\x64
  • perl Configure VC-WIN64A --release no-asm no-shared --prefix="E:\Gitee\Tool\include\openssl\x64"
  • nmake
  • nmake install
  • 与X86操作类似,不再演示

编译libcurl

编译32位

打开x86 Native Tools Command Prompt for VS 2017工具,切换到E:\Gitee\Tool\curl\winbuild目录。

问题

Text Only
NMAKE : fatal error U1073: 不知道如何生成“..\src\tool_hugehelp.c”

回退到目录:E:\Gitee\Tool\curl,运行buildconf.bat

img

Text Only
nmake /f Makefile.vc mode=static VC=15 MACHINE=x86 DEBUG=no WITH_SSL=static WITH_ZLIB=static WITH_PREFIX=E:\Gitee\Tool\include\curl\x86\ SSL_PATH=E:\Gitee\Tool\include\openssl\x86\ ZLIB_PATH=E:\Gitee\Tool\include\zlib\x86\

选项说明:

  • mode=<static/dll>:编译静态/动态库
  • VC=<10/11/12/14/15/16>:VisualStudio版本
  • MACHINE=<x86/x64>:生成32/64位库
  • DEBUG=<yes/no>:生成debug/release版本
  • WITH_SSL=<static/dll>:openssl的库类型
  • WITH_ZLIB=<static/dll>:zlib的库类型
  • WITH_PREFIX=<path>:编译结果输出路径。必须以反斜杠\结尾
  • SSL_PATH=<path>:openssl路径
  • ZLIB_PATH=<path>:zlib路径
VC版本 _MSC_VER宏的值 VS版本 MSVC toolset version
VC6.0 1200 VS 6.0
VC7.0 1300 VS2002
VC7.1 1310 VS2003
VC8.0 1400 VS2005 80
VC9.0 1500 VS2008 90
VC10.0 1600 VS2010 100
VC11.0 1700 VS2012 110
VC12.0 1800 VS2013 120
VC14.0 1900 VS2015 140
VC15.0 [ 1910 , 1916 ] VS2017 141
VC16.0 [ 1920 , 1929 ] [][1920,1929] VS2019 142
VC17.0 [1930,) VS2022 143

编译64

打开x64 Native Tools Command Prompt for VS 2017工具,切换到E:\Gitee\Tool\curl\winbuild目录。

问题

Text Only
NMAKE : fatal error U1073: 不知道如何生成“..\src\tool_hugehelp.c”

回退到目录:E:\Gitee\Tool\curl

img

Text Only
nmake /f Makefile.vc mode=static VC=15 MACHINE=x64 DEBUG=no WITH_SSL=static WITH_ZLIB=static WITH_PREFIX=E:\Gitee\Tool\include\curl\x64\ SSL_PATH=E:\Gitee\Tool\include\openssl\x64\ ZLIB_PATH=E:\Gitee\Tool\include\zlib\x64\

合并多个库

代码中使用libcurl库,编译的时候,需要同时链接libcurl.libzlib.liblibssl.liblibcrypto.lib4个库。

可以将这4个库文件合并为1个libcurl.lib

编译32位

新建文件夹E:\Gitee\Tool\include\curl\x86\lib_merge

打开x86 Native Tools Command Prompt for VS 2017工具,切换到E:\Gitee\Tool\include\curl\x86\lib_merge目录。

Text Only
lib.exe E:\Gitee\Tool\include\curl\x86\lib\libcurl_a.lib E:\Gitee\Tool\include\openssl\x86\lib\libcrypto.lib E:\Gitee\Tool\include\openssl\x86\lib\libssl.lib E:\Gitee\Tool\include\zlib\x86\lib\zlib.lib   /out:E:\Gitee\Tool\include\curl\x86\lib_merge\libcurl.lib

编译64位

新建文件夹E:\Gitee\Tool\include\curl\x64\lib_merge

打开x64 Native Tools Command Prompt for VS 2017工具,切换到E:\Gitee\Tool\include\curl\x64\lib_merge目录。

Text Only
lib.exe E:\Gitee\Tool\include\curl\x64\lib\libcurl_a.lib E:\Gitee\Tool\include\openssl\x64\lib\libcrypto.lib E:\Gitee\Tool\include\openssl\x64\lib\libssl.lib E:\Gitee\Tool\include\zlib\x64\lib\zlib.lib   /out:E:\Gitee\Tool\include\curl\x64\lib_merge\libcurl.lib

测试代码

Text Only
#include <string.h>
#include <stdlib.h>
#include <stdio.h>

#include <curl/curl.h>


struct memory {
    char* response;
    size_t size;
};

static size_t callback(void *data, size_t size, size_t nmemb, void *userp) {
    size_t realsize = size * nmemb;
    struct memory *mem = (struct memory *)userp;

    char *ptr = (char*)realloc((void*)mem->response, mem->size + realsize + 1);
    if(ptr == NULL) {
        return 0;  /* out of memory! */
    }

    mem->response = ptr;
    memcpy(&(mem->response[mem->size]), data, realsize);
    mem->size += realsize;
    mem->response[mem->size] = '\0';

    return realsize;
}

int main() {
    /* 1. 初始化 */
    CURL* curl = curl_easy_init();
    if (!curl) {
        printf("curl_easy_init failed\n");
        return 1;
    }

    struct memory chunk = {0};
    chunk.response = NULL;
    chunk.size = 0;

    /* 2. 发送请求 */
    curl_easy_setopt(curl, CURLOPT_URL, "https://www.baidu.com");
    curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, callback);
    curl_easy_setopt(curl, CURLOPT_WRITEDATA, (void*)&chunk);
    curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, 0L);
    curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 0L);
    curl_easy_setopt(curl, CURLOPT_SSL_VERIFYSTATUS, 0L);
    curl_easy_perform(curl);

    /* 3. 查看请求返回结果 */
    printf("%s\n", chunk.response);

    /* 4. 清理 */
    if (chunk.response) {
        free(chunk.response);
        chunk.response = NULL;
        chunk.size = 0;
    }
    curl_easy_cleanup(curl);
    curl = NULL;

    return 0;
}

添加如下lib

  • ws2_32.lib
  • wldap32.lib
  • crypt32.lib
  • Normaliz.lib
  • libcurl.lib (如果已经合并,就不需要下面3个了)
  • libcrypto.lib
  • libssl.lib
  • zlib.lib 编译运行程序,输出百度首页的html说明测试通过。 ———————————————— 版权声明:本文为CSDN博主「Leopard-C」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。 原文链接:https://blog.csdn.net/weixin_41701847/article/details/131300414

问题

问题一

Text Only
1
2
3
4
5
6
7
8
error LNK2001: 无法解析的外部符号 __imp__curl_easy_setopt
error LNK2001: 无法解析的外部符号 __imp__curl_easy_init
error LNK2001: 无法解析的外部符号 __imp__curl_easy_cleanup
error LNK2001: 无法解析的外部符号 __imp__curl_slist_append
error LNK2001: 无法解析的外部符号 __imp__curl_slist_free_all
error LNK2001: 无法解析的外部符号 __imp__curl_easy_strerror
error LNK2001: 无法解析的外部符号 __imp__curl_easy_perform
error LNK2001: 无法解析的外部符号 __imp__curl_easy_getinfo
  • 通过在测试程序的“CPP ---> 预处理”中增加 BUILDING_LIBCURL(或者CURL_STATICLIB),问题得以解决。

问题二

Text Only
libcurl.lib(ldap.obj) : error LNK2019: unresolved external symbol __imp__ldap_init referenced in function __ldap_free_urldesc
1>libcurl.lib(ldap.obj) : error LNK2019: unresolved external symbol __imp__ldap_unbind_s referenced in function __ldap_free_urldesc
1>libcurl.lib(ldap.obj) : error LNK2019: unresolved external symbol __imp__ldap_set_option referenced in function __ldap_free_urldesc
1>libcurl.lib(ldap.obj) : error LNK2019: unresolved external symbol __imp__ldap_simple_bind_s referenced in function __ldap_free_urldesc
1>libcurl.lib(ldap.obj) : error LNK2019: unresolved external symbol __imp__ldap_search_s referenced in function __ldap_free_urldesc
1>libcurl.lib(ldap.obj) : error LNK2019: unresolved external symbol __imp__ldap_msgfree referenced in function __ldap_free_urldesc
1>libcurl.lib(ldap.obj) : error LNK2019: unresolved external symbol __imp__ldap_err2string referenced in function __ldap_free_urldesc
1>libcurl.lib(ldap.obj) : error LNK2019: unresolved external symbol __imp__ldap_first_entry referenced in function __ldap_free_urldesc
1>libcurl.lib(ldap.obj) : error LNK2019: unresolved external symbol __imp__ldap_next_entry referenced in function __ldap_free_urldesc
1>libcurl.lib(ldap.obj) : error LNK2019: unresolved external symbol __imp__ldap_first_attribute referenced in function __ldap_free_urldesc
1>libcurl.lib(ldap.obj) : error LNK2019: unresolved external symbol __imp__ldap_next_attribute referenced in function __ldap_free_urldesc
1>libcurl.lib(ldap.obj) : error LNK2019: unresolved external symbol __imp__ldap_get_values_len referenced in function __ldap_free_urldesc
1>libcurl.lib(ldap.obj) : error LNK2019: unresolved external symbol __imp__ldap_value_free_len referenced in function __ldap_free_urldesc
1>libcurl.lib(ldap.obj) : error LNK2019: unresolved external symbol __imp__ldap_get_dn referenced in function __ldap_free_urldesc
1>libcurl.lib(ldap.obj) : error LNK2019: unresolved external symbol __imp__ldap_memfree referenced in function __ldap_free_urldesc
1>libcurl.lib(ldap.obj) : error LNK2019: unresolved external symbol __imp__ber_free referenced in function __ldap_free_urldesc

需要添加Ws2_32.lib和Wldap32.lib。

问题3

Text Only
error LNK2019: 无法解析的外部符号 __imp__IdnToAscii

需要添加Normaliz.lib

问题4

Text Only
无法解析的外部符号 __imp_CertOpenStore解决方案

在 项目属性 - 链接器 - 输入 - 附加依赖项 中加入: Crypt32.lib

参考

How to Build OpenSSL, zlib, and cURL libraries on Windowshttps://developers.refinitiv.com/en/article-catalog/article/how-to-build-openssl--zlib--and-curl-libraries-on-windows)