【Redis】Redis C++使用

一、Redis的自定義網絡協議

1.1 為什么可以編寫出一個自定義的Redis客戶端

???????為什么我們可以編寫出一個自定義的Redis客戶端?因為Redis公開了自己的自定義協議。而對于一些其他軟件的客戶端,我們無法編寫出一個自定義的Redis客戶端,因為他們沒有公開自己的自定義協議,但是我們可以通過一些抓包/逆向的手段猜測其應用層協議是什么樣子的!!

???????在網絡通信過程中,會用到很多的“協議”,比如數據鏈路層的以太網協議,網絡層的IP協議,傳輸層的TCP/UDP協議,應用層的協議更多!!雖然業界中有很多成熟的應用層協議:HTTP等~~但是此處更多的時候,都會“自定義”應用層協議,Redis此處的應用層協議就是自定義的協議(傳輸層還是基于TCP)~~

1.2 RESP protocol spec

RESP 協議的優點:

  1. 簡單好實現
  2. 快速進行解析
  3. 肉眼可讀

傳輸層這里基于TCP,但是又和TCP沒有強耦合。

請求和響應之間的通信模型是一問一答的形式~~(客戶端給服務器發送一個請求,服務器返回一個響應)

???????我們需要根據上述規則進行字符串的編寫,然后將這個字符串寫入到 tcp socket 中。因此,redis客戶端服務器要做的工作是:

  1. 按照上述格式,構造出字符串,往 socket 中寫入
  2. 從 socket 中讀取字符串,按照上述格式解析

二、安裝 redis-plus-plus

???????由于 redis-plus-plus 依賴了 hiredis(C語言版本的redis客戶端庫),所以我們要先下載 hiredis 庫(可以直接使用包管理器來安裝)。

???????但是 redis-plus-plus 本體,只能編譯安裝了。如果是編譯安裝,使用 Ubuntu 比 使用 centos 簡單很多。redis-plus-plus 本身功能比較簡陋,比較原始,寫起來也比較麻煩,實際開發中很少會手寫 makefile。通過程序來生成 makefile,cmake就是一個生成 makefile 的工具。

先來看一下如何使用 CMake 編譯程序:

  1. 創建一個 build 目錄是習慣做法,并非是必須,目的是為了讓編譯生成的臨時文件都放到build下面,避免污染源代碼目錄~
  2. cmake .. 這個操作是生成 makefile,此處的 .. 指向的是剛才 CMakeLists.txt 文件所在的目錄~~
  3. make 進行編譯
  4. make install 把剛才的庫拷貝到系統目錄

三、進行 ping 命令驗證

#include <sw/redis++/redis++.h>

1.包含 redis-plus-plus 的頭文件,如果我們不知道將這個庫下載到哪里,我們可以使用 find 命令進行查找,命令如下所示:

find /XXX(路徑) -name XXX 

2.創建了一個 Redis 對象

sw::redis::Redis redis("tcp://127.0.0.1:6379);

3.進行 ping 命令

4.使用 Makefile 編譯程序

編譯程序的時候,需要引入一些庫文件(需要知道這些庫文件的目錄):

  1. redis++ 自己的靜態庫
  2. hiredis 的靜態庫
  3. 線程庫

四、Redis 的通用命令的使用

一覽整個Redis通用命令的使用:

get/set
exists
del
keys
expire/ttl
type

4.1 get/set

???????在 C++ 中,std::string 是可以修改,既能讀,也能寫。但是 StringView 是只讀的(不能修改),針對只讀操作,做很多的優化工作,效率比 std::string 更高。在 C++17 標準庫中,也提供了一個 std::string_view。這里是為了兼容 C++11,14,17,所以自己封裝了一個類型。StringView 中的各個操作和string類似,只不過只是包含了一些只讀方法

???????在 Java 中的 String 就是類似于 StringView 只讀的,Java 中要想使用可修改的字符串,要使用 StringBuilder 或者 StringBuffer。


對于 get 來說,有可能獲取不到元素,這時應該返回什么類型呢??

???????如果直接使用 std::string 來表示,不方便來表現這個 nil(無效值),如果使用 std::string* 來表示,是可以使用 nullptr 表示無效的,但是返回指針又涉及到內存歸誰管~~

???????因此,作者就自己封裝了一個類型,此處的 Optional 可以表示 “非法值” 或者 “無效值”。在 Boost 中,很早就引入了 optional 類型,C++14版本中,就正式歸納標準庫了。

在使用 Optional 類型的時候,有可能出現以下這個錯誤:

???????此處不需要給這個 Optional 類型搞一個 << 重載,只需要把 Optional 里面包含的元素取出來即可~~

void test1(sw::redis::Redis& redis) {std::cout << "get 和 set 的使用" << std::endl;// 清空一下數據庫, 避免之前殘留的數據有干擾. redis.flushall();// 使用 set 設置 keyredis.set("key1", "111");redis.set("key2", "222");redis.set("key3", "333");// 使用 get 獲取到 key 對應的 valueauto value1 = redis.get("key1");// optional 可以隱式轉成 bool 類型, 可以直接在 if 中判定. 如果是無效元素, 就是返回 falseif (value1) {std::cout << "value1=" << value1.value() << std::endl;}auto value2 = redis.get("key2");if (value2) {std::cout << "value2=" << value2.value() << std::endl;}auto value3 = redis.get("key3");if (value3) {std::cout << "value3=" << value3.value() << std::endl;}auto value4 = redis.get("key4");if (value4) {std::cout << "value4=" << value4.value() << std::endl;}
}

4.2 exists

void test2(sw::redis::Redis& redis) {std::cout << "exists" << std::endl;redis.flushall();redis.set("key", "111");redis.set("key3", "111");auto ret = redis.exists("key");std::cout << ret << std::endl;ret = redis.exists("key2");std::cout << ret << std::endl;ret = redis.exists({"key", "key2", "key3"});std::cout << ret << std::endl;
}

???????對于 exists 命令來說,我們可以一次性查看多個鍵值,我們可以使用初始化列表傳參,代碼如下:

redis.exists({"key", "key2", "key3"});

4.3 del

void test3(sw::redis::Redis& redis) {std::cout << "del" << std::endl;// 清除庫非常必要的! redis.flushall();redis.set("key", "111");redis.set("key2", "111");// redis.del("key");auto ret = redis.del({"key", "key2", "key3"});std::cout << ret << std::endl;ret = redis.exists({"key", "key2"});std::cout << ret << std::endl;
}

4.4 keys

???????keys 命令不可以隨便使用,否則會影響其他命令的執行,因為 Redis 是單線程。keys 的返回值有多個。其返回值類型為:

???????這是插入迭代器,插入迭代器的本質是一種“輸出迭代器”,通常,一個輸出迭代器主要表示一個位置。插入迭代器,則是“位置” + “動作”。 插入迭代器總共有三種類型:

???????這里直接使用容器作為參數,keys內部直接操作容器,進行插入不是更好嗎,為什么要通過迭代器呢??

???????因為可以解耦合。?

void test4(sw::redis::Redis& redis) {std::cout << "keys" << std::endl;redis.flushall();redis.set("key", "111");redis.set("key2", "222");redis.set("key3", "333");redis.set("key4", "444");redis.set("key5", "555");redis.set("key6", "666");// keys 的第二個參數, 是一個 "插入迭代器". 咱們需要先準備好一個保存結果的容器. // 接下來再創建一個插入迭代器指向容器的位置. 就可以把 keys 獲取到的結果依次通過剛才的插入迭代器插入到容器的指定位置中了. vector<string> result;auto it = std::back_inserter(result);redis.keys("*", it);printContainer(result);
}

4.5 expire/ttl

void test5(sw::redis::Redis& redis) {using namespace std::chrono_literals;std::cout << "expire and ttl" << std::endl;redis.flushall();redis.set("key", "111");// 10s => std::chrono::seconds(10)redis.expire("key", 10s);std::this_thread::sleep_for(3s);auto time = redis.ttl("key");std::cout << time << std::endl;
}

???????在使用睡眠函數的時候,由于不同系統之間的單位不同,我們更好的選擇是使用線程庫中的睡眠函數:sleep_for。

???????Linux的sleep和Windows的Sleep,都屬于系統函數,是和系統相關的,同樣的功能,在不同系統中可能是完全不同的函數~

4.6 type

void test6(sw::redis::Redis& redis) {std::cout << "type" << std::endl;redis.flushall();redis.set("key", "111");string result = redis.type("key");std::cout << "key: " << result << std::endl;redis.lpush("key2", "111");result = redis.type("key2");std::cout << "key2: " << result << std::endl;redis.hset("key3", "aaa", "111");result = redis.type("key3");std::cout << "key3: " << result << std::endl;redis.sadd("key4", "aaa");result = redis.type("key4");std::cout << "key4: " << result << std::endl;redis.zadd("key5", "呂布", 99);result = redis.type("key5");std::cout << "key5: " << result << std::endl;
}

五、string類型的操作

5.1 get/set

void test1(Redis& redis) {std::cout << "get 和 set" << std::endl;redis.flushall();redis.set("key", "111");auto value = redis.get("key");if (value) {std::cout << "value: " << value.value() << std::endl;}redis.set("key", "222");value = redis.get("key");if (value) {std::cout << "value: " << value.value() << std::endl;}
}

5.2 set帶有超時時間

void test2(Redis& redis) {std::cout << "set 帶有超時時間" << std::endl;redis.flushall();redis.set("key", "111", 10s);std::this_thread::sleep_for(3s);long long time = redis.ttl("key");std::cout << "time: " << time << std::endl;
}

5.3 set NX/XX

void test3(Redis& redis) {std::cout << "set NX 和 XX" << std::endl;redis.flushall();redis.set("key", "111");// set 的重載版本中, 沒有單獨提供 NX 和 XX 的版本, 必須搭配過期時間的版本來使用. redis.set("key", "222", 0s, sw::redis::UpdateType::EXIST);auto value = redis.get("key");if (value) {std::cout << "value: " << value.value() << std::endl;} else {std::cout << "key 不存在!" << std::endl;}
}

5.4 mset

void test4(Redis& redis) {std::cout << "mset" << std::endl;redis.flushall();// 第一種寫法, 使用初始化列表描述多個鍵值對// redis.mset({ std::make_pair("key1", "111"), std::make_pair("key2", "222"), std::make_pair("key3", "333") });// 第二種寫法, 可以把多個鍵值對提前組織到容器中. 以迭代器的形式告訴 msetvector<std::pair<string, string>> keys = {{"key1", "111"},{"key2", "222"},{"key3", "333"}};redis.mset(keys.begin(), keys.end());auto value = redis.get("key1");if (value) {std::cout << "value: " << value.value() << std::endl;}value = redis.get("key2");if (value) {std::cout << "value: " << value.value() << std::endl;}value = redis.get("key3");if (value) {std::cout << "value: " << value.value() << std::endl;}
}

5.5 mget

void test5(Redis& redis) {std::cout << "mget" << std::endl;redis.flushall();vector<std::pair<string, string>> keys = {{"key1", "111"},{"key2", "222"},{"key3", "333"}};redis.mset(keys.begin(), keys.end());vector<sw::redis::OptionalString> result;auto it = std::back_inserter(result);redis.mget({"key1", "key2", "key3", "key4"}, it);printContainerOptional(result);
}

5.6 getrange/setrange

void test6(Redis& redis) {std::cout << "getrange 和 setrange" << std::endl;redis.flushall();redis.set("key", "abcdefghijk");string result = redis.getrange("key", 2, 5);std::cout << "result: " << result << std::endl;redis.setrange("key", 2, "xyz");auto value = redis.get("key");std::cout << "value: " << value.value() << std::endl;
}

5.7 incr/decr

???????incr 和 decr 得到的是 long long 類型(使用這個更多一些),get 得到的是 OptionalString 類型,需要手動轉成數字~C++中把字符串轉成數字,也有很多種方法。

void test7(Redis& redis) {std::cout << "incr 和 decr" << std::endl;redis.flushall();redis.set("key", "100");long long result = redis.incr("key");std::cout << "result: " << result << std::endl;auto value = redis.get("key");std::cout << "value: " << value.value() << std::endl;result = redis.decr("key");std::cout << "result: " << result << std::endl;value = redis.get("key");std::cout << "value: " << value.value() << std::endl;
}

六、list類型的操作

6.1 lpush/prange

void test1(Redis& redis) {std::cout << "lpush 和 lrange" << std::endl;redis.flushall();// 插入單個元素redis.lpush("key", "111");// 插入一組元素, 基于初始化列表redis.lpush("key", {"222", "333", "444"});// 插入一組元素, 基于迭代器vector<string> values = {"555", "666", "777"};redis.lpush("key", values.begin(), values.end());// lrange 獲取到列表中的元素vector<string> results;auto it = std::back_inserter(results);redis.lrange("key", 0, -1, it);printContainer(results);
}

6.2 rpush

void test2(Redis& redis) {std::cout << "rpush" << std::endl;redis.flushall();// 插入單個元素redis.rpush("key", "111");// 插入多個元素, 基于初始化列表redis.rpush("key", {"222", "333", "444"});// 插入多個元素, 基于容器vector<string> values = {"555", "666", "777"};redis.rpush("key", values.begin(), values.end());// 使用 lrange 獲取元素vector<string> results;auto it = std::back_inserter(results);redis.lrange("key", 0, -1, it);printContainer(results);
}

6.3 lpop/rpop

void test3(Redis& redis) {std::cout << "lpop 和 rpop" << std::endl;redis.flushall();// 構造一個 listredis.rpush("key", {"1", "2", "3", "4"});auto result = redis.lpop("key");if (result) {std::cout << "lpop: " << result.value() << std::endl;}result = redis.rpop("key");if (result) {std::cout << "rpop: " << result.value() << std::endl;}
}

6.4 blpop

void test4(Redis& redis) {using namespace std::chrono_literals;std::cout << "blpop" << std::endl;redis.flushall();auto result = redis.blpop({"key", "key2", "key3"}, 10s);if (result) {std::cout << "key:" << result->first << std::endl;std::cout << "elem:" << result->second << std::endl;} else {std::cout << "result 無效!" << std::endl;}
}

6.5 llen

void test5(Redis& redis) {std::cout << "llen" << std::endl;redis.flushall();redis.lpush("key", {"111", "222", "333", "444"});long long len = redis.llen("key");std::cout << "len: " << len << std::endl;
}

對于 redis-plus-plus 這個庫來說,接口風格的設計是非常統一的。

  • 當一個函數參數需要傳遞多個值的時候,往往都是支持初始化列表或者一對迭代器的方式來進行實現的
  • 當一個函數的返回值需要表示多個數據的時候,也往往會借助插入迭代器來實現往一個容器中添加元素的效果
  • 當某些場景涉及到無效值的時候,往往會搭配 std::optional 來使用

七、set類型的操作

7.1 sadd/smembers

void test1(Redis& redis) {std::cout << "sadd 和 smembers" << std::endl;redis.flushall();// 一次添加一個元素redis.sadd("key", "111");// 一次添加多個元素(使用初始化列表)redis.sadd("key", {"222", "333", "444"});// 一次添加多個元素(使用迭代器)set<string> elems = {"555", "666", "777"};redis.sadd("key", elems.begin(), elems.end());// 獲取到上述元素// 此處用來保存 smembers 的結果, 使用 set 可能更合適. vector<string> result;// auto it = std::back_inserter(result);// 由于此處 set 里的元素順序是固定的. 指定一個 result.end() 或者 result.begin() 或者其他位置的迭代器, 都無所謂~~auto it = std::inserter(result, result.end());redis.smembers("key", it);printContainer(result);
}

7.2 sismember

void test2(Redis& redis) {std::cout << "sismember" << std::endl;redis.flushall();redis.sadd("key", {"111", "222", "333", "444"});bool result = redis.sismember("key", "555");std::cout << "result: " << result << std::endl;
}

7.3 scrad

void test3(Redis& redis) {std::cout << "scard" << std::endl;redis.flushall();redis.sadd("key", {"111", "222", "333"});long long result = redis.scard("key");std::cout << "result: " << result << std::endl;
}

7.4 spop

void test4(Redis& redis) {std::cout << "spop" << std::endl;redis.flushall();redis.sadd("key", {"111", "222", "333", "444"});auto result = redis.spop("key");if (result) {std::cout << "result: " << result.value() << std::endl;} else {std::cout << "result 無效!" << std::endl;}
}

7.5 sinter

void test5(Redis& redis) {std::cout << "sinter" << std::endl;redis.flushall();redis.sadd("key1", {"111", "222", "333"});redis.sadd("key2", {"111", "222", "444"});set<string> result;auto it = std::inserter(result, result.end());redis.sinter({"key1", "key2"}, it);printContainer(result);
}

7.6 sinterstore

void test6(Redis& redis) {std::cout << "sinterstore" << std::endl;redis.flushall();redis.sadd("key1", {"111", "222", "333"});redis.sadd("key2", {"111", "222", "444"});long long len = redis.sinterstore("key3", {"key1", "key2"});std::cout << "len: " << len << std::endl;set<string> result;auto it = std::inserter(result, result.end());redis.smembers("key3", it);printContainer(result);
}

八、hash類型的操作

8.1 hset/hget

void test1(Redis& redis) {std::cout << "hset 和 hget" << std::endl;redis.flushall();redis.hset("key", "f1", "111");redis.hset("key", std::make_pair("f2", "222"));// hset 能夠一次性插入多個 field-value 對!!redis.hset("key", {std::make_pair("f3", "333"),std::make_pair("f4", "444")});vector<std::pair<string, string>> fields = {std::make_pair("f5", "555"),std::make_pair("f6", "666")};redis.hset("key", fields.begin(), fields.end());auto result = redis.hget("key", "f3");if (result) {std::cout << "result: " << result.value() << std::endl;} else {std::cout << "result 無效!" << std::endl;}
}

8.2 hexists

void test2(Redis& redis) {std::cout << "hexits" << std::endl;redis.flushall();redis.hset("key", "f1", "111");redis.hset("key", "f2", "222");redis.hset("key", "f3", "333");bool result = redis.hexists("key", "f4");std::cout << "result: " << result << std::endl;
}

8.3 hdel

void test3(Redis& redis) {std::cout << "hdel" << std::endl;redis.flushall();redis.hset("key", "f1", "111");redis.hset("key", "f2", "222");redis.hset("key", "f3", "333");long long result = redis.hdel("key", "f1");std::cout << "result: " << result << std::endl;result = redis.hdel("key", {"f2", "f3"});std::cout << "result: " << result << std::endl;long long len = redis.hlen("key");std::cout << "len: " << len << std::endl;
}

8.4?hkeys/hvals

void test4(Redis& redis) {std::cout << "hkeys 和 hvals" << std::endl;redis.flushall();redis.hset("key", "f1", "111");redis.hset("key", "f2", "222");redis.hset("key", "f3", "333");vector<string> fields;auto itFields = std::back_inserter(fields);redis.hkeys("key", itFields);printContainer(fields);vector<string> values;auto itValues = std::back_inserter(values);redis.hvals("key", itValues);printContainer(values);
}

8.5?hmget/hmset

void test5(Redis& redis) {std::cout << "hmget 和 hmset" << std::endl;redis.flushall();redis.hmset("key", {std::make_pair("f1", "111"),std::make_pair("f2", "222"),std::make_pair("f3", "333")});vector<std::pair<string, string>> pairs = {std::make_pair("f4", "444"),std::make_pair("f5", "555"),std::make_pair("f6", "666")};redis.hmset("key", pairs.begin(), pairs.end());vector<string> values;auto it = std::back_inserter(values);redis.hmget("key", {"f1", "f2", "f3"}, it);printContainer(values);
}

九、zset類型的操作

void test1(Redis& redis) {std::cout << "zadd 和 zrange" << std::endl;redis.flushall();redis.zadd("key", "呂布", 99);redis.zadd("key", {std::make_pair("趙云", 98),std::make_pair("典韋", 97)});vector<std::pair<string, double>> members = {std::make_pair("關羽", 95),std::make_pair("張飛", 93)};redis.zadd("key", members.begin(), members.end());// zrange 支持兩種主要的風格:// 1. 只查詢 member, 不帶 score// 2. 查詢 member 同時帶 score// 關鍵就是看插入迭代器指向的容器的類型. // 指向的容器只是包含一個 string, 就是只查詢 member// 指向的容器包含的是一個 pair, 里面有 string 和 double, 就是查詢 member 同時帶有 scorevector<string> memberResults;auto it = std::back_inserter(memberResults);redis.zrange("key", 0, -1, it);printContainer(memberResults);vector<std::pair<string, double>> membersWithScore;auto it2 = std::back_inserter(membersWithScore);redis.zrange("key", 0, -1, it2);printContainerPair(membersWithScore);
}void test2(Redis& redis) {std::cout << "zcard" << std::endl;redis.flushall();redis.zadd("key", "zhangsan", 90);redis.zadd("key", "lisi", 91);redis.zadd("key", "wangwu", 92);redis.zadd("key", "zhaoliu", 93);long long result = redis.zcard("key");std::cout << "result: " << result << std::endl;
}void test3(Redis& redis) {std::cout << "zrem" << std::endl;redis.flushall();redis.zadd("key", "zhangsan", 90);redis.zadd("key", "lisi", 91);redis.zadd("key", "wangwu", 92);redis.zadd("key", "zhaoliu", 93);redis.zrem("key", "zhangsan");long long result = redis.zcard("key");std::cout << "result: " << result << std::endl;
}void test4(Redis& redis) {std::cout << "zscore" << std::endl;redis.flushall();redis.zadd("key", "zhangsan", 90);redis.zadd("key", "lisi", 91);redis.zadd("key", "wangwu", 92);redis.zadd("key", "zhaoliu", 93);auto score = redis.zscore("key", "zhangsan");if (score) {std::cout << "score: " << score.value() << std::endl;} else {std::cout << "score 無效" << std::endl;}
}void test5(Redis& redis) {std::cout << "zrank" << std::endl;redis.flushall();redis.zadd("key", "zhangsan", 90);redis.zadd("key", "lisi", 91);redis.zadd("key", "wangwu", 92);redis.zadd("key", "zhaoliu", 93);auto rank = redis.zrank("key", "zhaoliu");if (rank) {std::cout << "rank: " << rank.value() << std::endl;} else {std::cout << "rank 無效" << std::endl;}
}

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/diannao/79158.shtml
繁體地址,請注明出處:http://hk.pswp.cn/diannao/79158.shtml
英文地址,請注明出處:http://en.pswp.cn/diannao/79158.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

【軟考系統架構設計師】軟件工程知識點

1、 軟件開發生命周期 軟件定義時期&#xff1a;包括可行性研究和詳細需求分析過程&#xff0c;任務是確定軟件開發工程必須完成的總目標&#xff0c;具體分為問題定義、可行性研究、需求分析等 軟件開發時期&#xff1a;軟件的設計與實現&#xff0c;分為概要設計、詳細設計、…

DeepSeek 與開源:肥沃土壤孕育 AI 碩果

當國產 AI DeepSeek 以其低成本推理和多模態能力在全球范圍內引起轟動時&#xff0c;人們驚嘆于中國技術的迅猛發展&#xff0c;卻很少有人深究這一成就背后的根基。答案其實早已寫在中國開源生態二十多年的發展歷程中。 從倪光南院士提出“以開源打破技術壟斷”的理念&#x…

職坐標:智慧城市未來發展的核心驅動力

內容概要 智慧城市的演進正以顛覆性創新重構人類生存空間&#xff0c;其發展脈絡由物聯網、人工智能與云計算三大技術支柱交織而成。這些技術不僅推動城市治理從經驗決策轉向數據驅動模式&#xff0c;更通過實時感知與智能分析&#xff0c;實現交通、能源等領域的精準調控。以…

vue復習46~90

1.小兔鮮 所有都折疊 ctrl k,ctrl0 所有都展開 ctrl k,ctrlj當前結構渲染5次 <BaseBrandItem v-for"item in 5" :key"item"><BaseBrandItem>2.scoped樣式沖突 結構&#xff1a;只能有一個根元素樣式&#xff1a;全局樣式(默認)&#xff1…

PHP 用 workman 即時通訊,做個簡版QQ

1. workman是什么 &#xff0c;一般應用在那些地方 workerman是一個高性能的PHP socket 服務器框架&#xff0c;workerman基于PHP多進程以及libevent事件輪詢庫&#xff0c;PHP開發者只要實現一兩個接口&#xff0c;便可以開發出自己的網絡應用&#xff0c;例如Rpc服務、聊天室…

【WORD】批量將doc轉為docx

具體步驟進行&#xff1a; 打開Word文檔&#xff0c;按下AltF11快捷鍵&#xff0c;打開VBA編輯器。在VBA編輯器中&#xff0c;左側的“項目資源管理器”窗口會顯示當前打開的Word文檔相關項目。找到您要添加代碼的文檔項目&#xff08;通常以文檔名稱命名&#xff09;&#xf…

【免費】【實測有用】5KPlayer Windows 電腦作為 MacBook 無線擴展屏

總結&#xff1a;使用 5KPlayer 將 Windows 電腦作為 MacBook 無線擴展屏 準備工作 設備要求&#xff1a; MacBook 和 Windows 電腦需連接到同一 Wi-Fi 網絡。【這里有雷&#xff1a;eduroam不會成功&#xff0c;家里的WIFI成功了&#xff0c;需要確認校園網是否可行。】確保…

華為華三模擬器解決兼容問題Win11 24H2 現在使用ENSP的問題解決了

一、Win11 24H2 現在使用ENSP的問題解決了 這個Win11 的 24H2不能使用ENSP的問題已經困擾我們很久了,在之前的文章中,我們也有說明這個問題 之前ENSP肯定啟動會報錯40 當時還建議大家先不要更新到win11的24H2版本,現在終于迎來了更新,不用再擔心了,包括早就升級了24H2版…

嵌入式WebRTC輕量化SDK壓縮至500K-800K ,為嵌入式設備節省Flash資源

一、SDK輕量化的核心技術實現 1、WebRTC庫裁剪與模塊化設計 EasyRTC針對嵌入式設備的資源限制&#xff0c;對原生WebRTC庫進行深度裁剪&#xff0c;僅保留核心通信功能&#xff08;如信令管理、編解碼、網絡傳輸等&#xff09;&#xff0c;移除冗余組件&#xff08;如部分調試…

Maya云渲染工作流,提升渲染速度

在三維動畫與影視特效領域&#xff0c;Autodesk Maya作為行業標桿工具&#xff0c;承載著從角色建模到復雜特效渲染的全流程創作。然而&#xff0c;本地硬件性能不足、渲染周期漫長、跨團隊協作效率低等痛點始終困擾著創作者。渲染101云渲染以彈性算力資源、智能化工作流與全方…

git怎么使遠程分支回退到指定的節點處

git使遠程分支回退到指定的節點 引言場景描述步驟 引言 最近提交代碼的時候&#xff0c;總將分支合并錯&#xff0c;原本要合到A分支&#xff0c;結果合并到了B分支&#xff0c;這樣就導致b分支需要回退到我沒有合并之前的節點處。 本文記錄下怎么將遠程分支回退到指定的節點。…

全網通emotn ui桌面免費嗎?如何開機自啟動

在智能設備的使用中&#xff0c;一款優秀的桌面系統能帶來截然不同的體驗。全網通Emotn UI桌面便是其中的佼佼者&#xff0c;它以完全免費的特性與卓越性能&#xff0c;成為眾多用戶的心頭好。 其簡潔美觀的界面設計如同為設備換上"清新外衣"&#xff0c;常用功能一…

通過微信APPID獲取小程序名稱

進入微信公眾平臺&#xff0c;登錄自己的小程序后臺管理端&#xff0c;在“賬號設置”中找到“第三方設置” 在“第三方設置”頁面中&#xff0c;將頁面拉到最下面&#xff0c;即可通過appid獲取到這個小程序的名稱信息

2025年第十六屆藍橋杯省賽JavaB組真題回顧

第16屆藍橋杯省賽已經結束了&#xff0c;第一次參加也是坐牢了4個小時&#xff0c;現在還是來總結一下吧&#xff08;先聲明以下的解法&#xff0c;大家可以當作一種思路來看&#xff0c;解法不一定是正解&#xff0c;只是給大家提供一種能夠正常想到的思路吧&#xff09; 試題…

深入剖析 Axios 的 POST 請求:何時使用 qs 處理數據

在前端開發中&#xff0c;Axios 是一個廣泛使用的用于發送 HTTP 請求的庫&#xff0c;特別是在處理 POST 請求時&#xff0c;數據的處理方式會直接影響到請求能否正確被后端接收和處理。其中&#xff0c;使用 qs 庫對數據進行處理是一個常見的操作點&#xff0c;本文將深入探討…

通過websocket給服務端發送訂單催單提醒消息

controller層 GetMapping("/reminder/{id}")public Result Remainder(PathVariable("id") Long id){orderService.remainder(id);return Result.success();} 實現類 Overridepublic void remainder(Long id) {Orders ordersDB orderMapper.getById(id);…

ros_note02

note02 節點 ROS2中每一個節點只負責一個單獨的模塊化功能 如&#xff1a;一個節點負責控制車輪轉動&#xff0c;一個節點負責從激光雷達獲取數據&#xff0c;一個節點負責定位 通信方式&#xff1a; 話題&#xff1a;topic服務&#xff1a;services動作&#xff1a;Actio…

使用治療前MR圖像預測腦膜瘤Ki-67的多模態深度學習模型

大家好&#xff0c;我是帶我去滑雪&#xff01; 腦膜瘤是一種常見的腦部腫瘤&#xff0c;Ki-67作為腫瘤細胞增殖的標志物&#xff0c;對于評估腫瘤的生物學行為、預后以及治療方案的制定具有至關重要的作用。然而&#xff0c;傳統的Ki-67檢測依賴于組織學切片和免疫組化染色等方…

【大模型系列篇】深度研究智能體技術演進:從DeepResearch到DeepResearcher,如何重構AI研究范式

DeepResearch 的概念與功能最早由 Google 在 Gemini 系列產品中推出&#xff0c;用于自動化生成結構化研究報告&#xff0c;近期底層依賴模型Gemini升級到了2.5 Pro。而我們常規認知的DeepResearch是由OpenAI推出的一款由優化版的 o3 模型驅動專注于深度研究和分析的AI智能體產…

PostgreSQL 如何查看端口號

PostgreSQL 如何查看端口號 PostgreSQL大多數情況下&#xff0c;默認端口是5432&#xff0c;但某些環境中可能配置為其它端口。 一 基本查詢方法 1.1 psql 命令行工具查詢 -- 方法1&#xff1a;查看當前連接信息&#xff08;包含端口&#xff09; \conninfo-- 方法2&#x…