<?xml version="1.0" encoding="UTF-8"?><rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>小白 &#8211; Yishang&#039;s Campfire (梦吃丘)</title>
	<atom:link href="https://lovepikachu.top/tag/%e5%b0%8f%e7%99%bd/feed" rel="self" type="application/rss+xml" />
	<link>https://lovepikachu.top</link>
	<description>最爱皮卡丘</description>
	<lastBuildDate>Mon, 08 Jul 2024 07:59:31 +0000</lastBuildDate>
	<language>zh-Hans</language>
	<sy:updatePeriod>
	hourly	</sy:updatePeriod>
	<sy:updateFrequency>
	1	</sy:updateFrequency>
	<generator>https://wordpress.org/?v=7.1.3</generator>

<image>
	<url>https://lovepikachu.top/wp-content/uploads/2023/08/94072c8a4db13a4475e9987299e84bd3-100x100.png.webp</url>
	<title>小白 &#8211; Yishang&#039;s Campfire (梦吃丘)</title>
	<link>https://lovepikachu.top</link>
	<width>32</width>
	<height>32</height>
</image> 
	<item>
		<title>C++ 学习记录 1</title>
		<link>https://lovepikachu.top/c-plus1.html</link>
					<comments>https://lovepikachu.top/c-plus1.html#respond</comments>
		
		<dc:creator><![CDATA[我叫以赏]]></dc:creator>
		<pubDate>Mon, 08 Jul 2024 07:59:31 +0000</pubDate>
				<category><![CDATA[C++]]></category>
		<category><![CDATA[信息技术]]></category>
		<category><![CDATA[C语言]]></category>
		<category><![CDATA[初学]]></category>
		<category><![CDATA[学习]]></category>
		<category><![CDATA[小白]]></category>
		<category><![CDATA[编程]]></category>
		<category><![CDATA[计算机]]></category>
		<guid isPermaLink="false">https://lovepikachu.top/?p=1478</guid>

					<description><![CDATA[环境 Microsoft Visual Stduio 2022 Community ISO C++ 20 标准 [&#8230;]]]></description>
										<content:encoded><![CDATA[<div class='admonition shadow-sm admonition-warning'><div class='admonition-title'><i class='fa fa-flag'></i> 提示</div><div class='admonition-body'>这是一篇简单的学习记录，没有过多复杂的内容。但是因为是随心记录的，不适合于初学者学习，建议有一定编程基础与计算机知识的人参考。</div></div>
<h1>环境</h1>
<ul>
<li>Microsoft Visual Stduio 2022 Community</li>
<li>ISO C++ 20 标准</li>
<li>vcpkg 包管理器</li>
</ul>
<h1>目标</h1>
<p>制作一个简易的下载器，通过调整请求头 Range 来实现分段下载。此程序考查了：</p>
<ul>
<li>输入、输出</li>
<li>模块、支持库的调用</li>
<li>文件的读写</li>
<li>if 判断语句</li>
<li>for while 循环语句</li>
<li>字符串拼接</li>
<li>命名空间的使用</li>
<li>异常的处理</li>
</ul>
<h1>代码</h1>
<pre class="EnlighterJSRAW" data-enlighter-language="cpp">#include &lt;iostream&gt;
#include &lt;fstream&gt;
#include &lt;string&gt;
#include &lt;thread&gt;
#include &lt;cpr/cpr.h&gt;

static void progress(int per);
static std::string strip(const std::string&amp; str);
static bool download_file(std::string download_url);

// 程序入口点
int main() {
    std::string download_url;
    std::cout &lt;&lt; "欢迎使用没什么用下载器，请输入下载链接：";

    // 不用 std:cin 因为不可以获取空格
    std::getline(std::cin, download_url);
    download_url = strip(download_url);
    std::cout &lt;&lt; "你输入的是：" + download_url + "\n我要开始下载喽亲！" &lt;&lt; std::endl;
    std::cin.sync();  // 清空缓存区 或者使用 std::cin 吃掉换行

    // 请求服务器下载
    try {
        // 判断是否为合法的 URL
        if (download_url.substr(0, 3) != "http") {
            std::cout &lt;&lt; "这不是一个合法的请求。" &lt;&lt; std::endl;
            std::abort();
        }
        download_file(download_url);
    }
    catch (const std::exception&amp; e) {
        std::cout &lt;&lt; "("  &lt;&lt; e.what() &lt;&lt; ")我死了，因为程序运行出现了错误。" &lt;&lt; std::endl;
    }


    return 0;
}

// 绘制进度条
static void progress(int per) {
    std::cout &lt;&lt; "\r进度条：|";
    for (int i = 0; i &lt; per; i++) {
        std::cout &lt;&lt; "*";
    }
    for (int i = 0; i &lt; 100 - per; i++) {
        std::cout &lt;&lt; " ";
    }
    std::cout &lt;&lt; "|" &lt;&lt; per &lt;&lt; "%" &lt;&lt; std::flush;
}

static std::string strip(const std::string&amp; str) {
    size_t start = str.find_first_not_of(" \t\r\n"); // 找到第一个非空白字符的位置
    size_t end = str.find_last_not_of(" \t\r\n");   // 找到最后一个非空白字符的位置
    if (start == std::string::npos) // 如果全是空白字符
        return ""; // 返回空字符串
    return str.substr(start, end - start + 1); // 返回去除首尾空白字符后的子串
}

static bool download_file(std::string download_url) {

    // 取出文件名
    std::string filename = download_url.substr(download_url.rfind("/") + 1);

    // 打开一个文件
    std::ofstream file{ filename , std::ios::binary };

    if (!file.is_open()) {
        std::cout &lt;&lt; "文件不能创建！取消下载！" &lt;&lt; std::endl;
        return false;
    }

    // 输出文件名
    std::cout &lt;&lt; "文件名为 " &lt;&lt; filename &lt;&lt; " " &lt;&lt; std::endl;

    // 建立一个会话
    cpr::Session session;
    session.SetUrl(
        cpr::Url(download_url)
    );

    // 获取文件长度
    size_t file_length = session.GetDownloadFileLength();

    std::cout &lt;&lt; "长度为 " &lt;&lt; file_length &lt;&lt; " B" &lt;&lt; std::endl;

    // 分段下载文件
    size_t response_length = 1024;  // 每次请求大小
    size_t downloaded_length = 0;  // 已经下载的大小

    while (downloaded_length &lt; file_length) {
        session.SetHeader(
            cpr::Header{
                {"Range", "bytes=" + std::to_string(downloaded_length) + "-" + std::to_string(downloaded_length + response_length - 1)}
            }
        );

        cpr::Response response = session.Get();


        // &amp; 代表直接引用 response.header 中的元素，const 设置常量，禁止修改
        /*for (const auto&amp; header : response.header) {
            std::cout &lt;&lt; header.first &lt;&lt; ":" &lt;&lt; header.second &lt;&lt; std::endl;
        }*/

        auto data = response.text.data();
        auto size = std::stoi(response.header["Content-Length"]);
        file.write(data, size);
        downloaded_length += size;
        progress(static_cast&lt;double&gt; (downloaded_length) / file_length * 100);
        
    }

    return true;
}</pre>
<h1>说明</h1>
<ol>
<li>爬虫使用的是 cpr 库，其中的 spr::Response 类中的 text 对象虽然直译为“文本”，但本质就是存在内存的中的一段数据，是 std::string 类型的。使用 <code>response.text.data()</code> 可以获取到对应的指针。通过 <code>response.downloaded_bytes</code> 或者 <code>response.header["Content-Length"]</code> 可以获取到获取的字节大小，从而实现获取原字节数据。</li>
<li>for 循环有两种写法，第一种是<code>for (int i=0;i&lt;100;i++)</code>对于可遍历的对象可以有<code>for (const auto&amp; item: items)</code>。其中 <code>const</code>表示定义为常量，不让后续代码进行破坏。&amp;表示的是直接引用<code>items</code>中的各个项，避免不必要的复制浪费内存。</li>
<li>关于数据类型转化，一般可用<code>(变量类型) 变量</code> 或 <code>变量 (变量类型)</code> 进行强制转化，转化过程会产生一个新的变量（即会有一个新的指针地址）。
<ol>
<li>在 《C++ Primer Plus 第8版 中文版》第 55 页：C++还引入了4个强制类型转换运算符，对数据类型的转换更为严格。例如<code>static_cast&lt;&gt;</code> 可将数值类型转化为另一种数据类型。（个人理解：用于数值间的转化。）</li>
<li>在代码中我使用了 <code>static_cast&lt;double&gt;</code> 对 <code>size_t</code>类型的<code>downloaded_length</code>进行强制转化，传送到接收<code>int</code>类型的函数中，会抛出 <code>warning C4244: “参数”: 从“double”转换到“int”，可能丢失数据</code>的警告。我们可以显式的告诉编译器我知道这个风险，<code>progress((int)(static_cast&lt;double&gt; (downloaded_length) / file_length * 100)); </code>同样，你可以掩耳盗铃在前面加上：<code>#pragma warning(disable: 4244)</code>之后再<code>#<span class="hljs-keyword">pragma</span> <span class="hljs-keyword">warning</span>(default: 4244)</code>。</li>
</ol>
</li>
<li>另外对于初始化数据类型时，使用<code>{}</code>更加安全，准确来说在 C++11 以上的标准中才加入进来的，其不允许窄化变量：<code>int a{1.1};</code>会报错，因为这种转化会丢失浮点位数据。但是使用<code>()</code>与<code>=</code>是允许的。只不过会抛出异常。更多内容查看 《C++ Primer Plus 第8版 中文版》第 54 页。</li>
<li>程序中引用的<code>#include &lt;thread&gt;</code>并未使用，因为只是在测试进度条的时候用来延迟的，忘记删除了，延迟方法<code>std::this_thread::sleep_for(std::chrono::milliseconds(100));</code>。</li>
<li>输入时使用<code>std::getline(std:cin, 变量)</code>之后还需要使用<code>std::cin.sync();</code>或者<code>std::cin.get();</code>来吃掉换行符，因为 <code>std::getline(std:cin, 变量)</code>不会读取换行符，会将其留在缓存区中。注： <code>std::cin.sync()</code>是清空缓存区。</li>
</ol>
<h1>不足</h1>
<ol>
<li>程序没有判断是否支持断点续传</li>
<li>对异常处理不够精确</li>
</ol>
]]></content:encoded>
					
					<wfw:commentRss>https://lovepikachu.top/c-plus1.html/feed</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
	</channel>
</rss>
