国产亚洲精品福利在线无卡一,国产精久久一区二区三区,亚洲精品无码国模,精品久久久久久无码专区不卡

當前位置: 首頁 > news >正文

套網站模板軟件wix網站制作

套網站模板軟件,wix網站制作,涿鹿做網站wl17581,建設銀行網站轉賬使用RedisTemplate執(zhí)行l(wèi)ua腳本 在開發(fā)中,我們經常需要與Redis數據庫進行交互,而Redis是一個基于內存的高性能鍵值存儲數據庫,它支持多種數據結構,并提供了豐富的命令接口。在某些情況下,我們可能需要執(zhí)行一些復雜的邏…

使用RedisTemplate執(zhí)行l(wèi)ua腳本

在開發(fā)中,我們經常需要與Redis數據庫進行交互,而Redis是一個基于內存的高性能鍵值存儲數據庫,它支持多種數據結構,并提供了豐富的命令接口。在某些情況下,我們可能需要執(zhí)行一些復雜的邏輯操作,這時可以使用Lua腳本來實現(xiàn)這些邏輯,而Redis提供了執(zhí)行Lua腳本的功能。在Spring應用程序中,我們可以使用RedisTemplate來執(zhí)行Lua腳本。

為什么使用Lua腳本

Redis本身提供了許多命令,可以完成各種操作,但有時候我們需要執(zhí)行一些比較復雜的邏輯操作,這時使用Lua腳本可以幫助我們在一次網絡往返中完成多個命令操作,減少了網絡開銷,提高了執(zhí)行效率。此外,Lua腳本在Redis服務器端執(zhí)行,可以減少客戶端與服務器之間的通信次數,提高了性能。

結合Redis和lua腳本語言的特性,如果在Redis里遇到如下需求,就可以引入lua腳本。

  • 重復執(zhí)行相同類型的命令,比如要緩存1到1000的數字到內存里。
  • 在高并發(fā)場景下減少網絡調用的開銷,一次性執(zhí)行多條命令。
  • Redis會將lua腳本作為一個整體來執(zhí)行,天然具有原子性。

使用RedisTemplate執(zhí)行Lua腳本

在Spring應用程序中,我們可以通過RedisTemplate來執(zhí)行Lua腳本。RedisTemplate是Spring Data Redis提供的用于與Redis數據庫進行交互的模板類,它封裝了Redis的各種操作,并提供了方便的方法來執(zhí)行Lua腳本。

以下是使用RedisTemplate執(zhí)行Lua腳本的一般步驟:

  1. 添加Spring Data Redis依賴: 首先,確保你的Spring Boot項目中已經添加了Spring Data Redis依賴。你可以在項目的pom.xml文件中添加以下依賴:
<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
  1. 配置RedisTemplate: 在Spring Boot項目的配置文件中(例如application.properties或application.yml)配置Redis連接信息和RedisTemplate。以下是一個示例配置:
spring.redis.host=localhost
spring.redis.port=6379
spring.redis.password=your_redis_password

在Java代碼中,你可以配置RedisTemplate bean,如下所示:

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.StringRedisSerializer;@Configuration
public class RedisConfig {@Beanpublic RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory connectionFactory) {RedisTemplate<String, Object> template = new RedisTemplate<>();template.setConnectionFactory(connectionFactory);template.setKeySerializer(new StringRedisSerializer());template.setValueSerializer(new StringRedisSerializer()); // 根據需要設置值的序列化器template.setEnableTransactionSupport(true); // 支持事務template.afterPropertiesSet();return template;}
}
  1. 執(zhí)行Lua腳本: 現(xiàn)在,你可以在Spring Boot服務中使用RedisTemplate執(zhí)行Lua腳本。以下是一個示例:
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.script.DefaultRedisScript;
import org.springframework.data.redis.core.script.RedisScript;
import org.springframework.stereotype.Service;import java.util.Collections;
import java.util.List;@Service
public class LuaScriptService {@Autowiredprivate RedisTemplate<String, Object> redisTemplate;public String executeLuaScript() {// Lua腳本內容String luaScript = "return 'Hello, Lua!'";// 創(chuàng)建RedisScript對象RedisScript<String> script = new DefaultRedisScript<>(luaScript, String.class);// 執(zhí)行Lua腳本String result = redisTemplate.execute(script, Collections.emptyList());return result;}
}

在這個示例中,我們首先定義了一個Lua腳本字符串,并使用DefaultRedisScript創(chuàng)建了一個RedisScript對象。然后,我們使用RedisTemplate的execute方法執(zhí)行Lua腳本,并傳遞一個空參數列表。

這只是一個簡單的示例,你可以根據需要編寫更復雜的Lua腳本,并使用RedisTemplate來執(zhí)行它們。需要確保在執(zhí)行Lua腳本時使用正確的參數和數據類型,以便與Redis進行正確的交互。

在這里插入圖片描述

-- 此腳本的功能是:將傳入的key增加1,并且如果是第一次操作這個key,則給其設置傳入的過期時間。
local results = {}
-- 這里的 for 循環(huán)中出現(xiàn)了兩個循環(huán)變量,分別表示索引和值;ipars 是只遍歷存在于數組里面的元素;ARGV[idx]表示取參數值
for idx,key in ipairs(KEYS) dolocal value = tonumber(ARGV[idx])local total = redis.call('INCR', key)table.insert(results, total)if total == 1 thenredis.call('EXPIRE', key, value)end
end
return results

文件讀取第一種方式

@Configuration
public class RedisScriptConfig {@Beanpublic RedisScript<List<Long>> batchIncrWithExpireScript() {Resource scriptSource = new ClassPathResource("redis-scripts/batchIncrWithExpire.lua");String script;try {script = StreamUtils.copyToString(scriptSource.getInputStream(), StandardCharsets.UTF_8);} catch (IOException e) {throw new IllegalStateException("Unable to load lua script", e);}return new DefaultRedisScript(script, List.class);}}
@Component
@Slf4j
public class RedisUtils {@Resource(name = "batchIncrWithExpireScript")private RedisScript<List<Long>> batchIncrWithExpireScript;public void batchIncrWithExpire(Map<String, String> keyValueMap) {// 準備鍵和值的列表List<String> keys = new ArrayList<>(keyValueMap.keySet());List<String> values = keyValueMap.values().stream().toList();// 執(zhí)行Lua腳本List<Long> results = stringRedisTemplate.execute(batchIncrWithExpireScript, keys, values.toArray());}}

文件讀取第二種方式

要在Spring Boot項目中運行一個Lua腳本文件,你可以按照以下步驟進行操作:

  1. 創(chuàng)建Lua腳本文件: 首先,創(chuàng)建一個包含你的Lua腳本的文件(例如,myscript.lua),并將其保存在項目的合適位置。在這個文件中,你可以編寫你的Lua腳本代碼。
  2. 加載Lua腳本文件: 在Spring Boot服務中,你需要加載Lua腳本文件并將其內容傳遞給RedisTemplate來執(zhí)行。你可以使用Java的文件讀取方法來加載Lua腳本文件的內容。
  3. 執(zhí)行Lua腳本: 使用RedisTemplate執(zhí)行加載的Lua腳本內容。你可以使用DefaultRedisScript來創(chuàng)建RedisScript對象,并在執(zhí)行時傳遞適當的參數。
    以下是示例代碼,演示如何加載并執(zhí)行Lua腳本文件:
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.io.ClassPathResource;
import org.springframework.core.io.Resource;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.script.DefaultRedisScript;
import org.springframework.data.redis.core.script.RedisScript;
import org.springframework.stereotype.Service;import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.util.Collections;
import java.util.List;@Service
public class LuaScriptFileService {@Autowiredprivate RedisTemplate<String, Object> redisTemplate;public String executeLuaScriptFromFile() throws IOException {// 加載Lua腳本文件Resource resource = new ClassPathResource("redis-scripts/batchIncrWithExpire.lua");String luaScript = new String(resource.getInputStream().readAllBytes(), StandardCharsets.UTF_8);// 創(chuàng)建RedisScript對象RedisScript<String> script = new DefaultRedisScript<>(luaScript, String.class);// 執(zhí)行Lua腳本String result = redisTemplate.execute(script, Collections.emptyList());return result;}
}

在這個示例中,我們首先加載Lua腳本文件的內容并將其存儲在luaScript字符串中。然后,我們使用DefaultRedisScript創(chuàng)建了RedisScript對象,并在執(zhí)行時傳遞了一個空參數列表。你需要替換path/to/myscript.lua為你的Lua腳本文件的實際路徑。

現(xiàn)在,你可以在Spring Boot服務中調用executeLuaScriptFromFile方法來執(zhí)行Lua腳本文件中的內容。

請確保Lua腳本文件的路徑和文件名正確,并且具有適當的訪問權限。此外,根據需要,你可以傳遞參數給Lua腳本,并在Lua腳本中使用KEYSARGV來引用它們。

文件讀取第三種方式

你可以直接使用DefaultRedisScript來讀取Lua腳本文件,而不需要手動加載文件內容。以下是如何使用DefaultRedisScript來執(zhí)行Lua腳本文件的示例:

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.script.DefaultRedisScript;
import org.springframework.data.redis.core.script.RedisScript;
import org.springframework.stereotype.Service;import java.util.Collections;
import java.util.List;@Service
public class LuaScriptFileService {@Autowiredprivate RedisTemplate<String, Object> redisTemplate;public String executeLuaScriptFromFile() {Resource scriptSource = new ClassPathResource("redis-scripts/batchIncrWithExpire.lua");RedisScriptList<Long> script = RedisScript.of(scriptSource, List.class);// 執(zhí)行Lua腳本String result = redisTemplate.execute(script, Collections.emptyList());return result;}
}

在這個示例中,我們通過將Lua腳本文件的路徑傳遞給DefaultRedisScript的構造函數來創(chuàng)建了RedisScript對象。然后,我們可以使用execute方法來執(zhí)行Lua腳本文件中的內容。這種方法更簡潔,省去了手動加載文件內容的步驟。

確保將"redis-scripts/batchIncrWithExpire.lua"替換為你實際的Lua腳本文件路徑。此外,根據需要,你可以傳遞參數給Lua腳本,并在Lua腳本中使用KEYSARGV來引用它們。

用一篇文章讓你學會腳本語言Lua

Lua的數據結構、關鍵字、注釋

任何一門語言都提供了不同類型的數據結構,那么 Lua 中都有哪些數據結構呢?

  • nil:空
  • boolean:布爾類型,分別是 true 和 false
  • number:數值型,整型和浮點型都屬于 number
  • string:字符串
  • table:表
  • function:函數
  • userdata:用戶數據
  • thread:線程

Lua 總共提供了以上 8 種數據類型,目前只需要知道一下即可,后面會一點一點介紹。


然后是 Lua 的關鍵字,總共有 22 個,如下所示。

and break do else elseif end false 
goto for function if in local nil 
not or repeat return then true until while

這些關鍵字顯然基本上都見過,后續(xù)會慢慢遇到。


最后是 Lua 的注釋,Lua 也分單行注釋和多行注釋,單行注釋和 SQL 一樣以 -- 開頭,多行注釋則以 --[[ 開頭、]] 結尾,里面寫注釋。

-- 這是單行注釋--[[這是多行注釋并且開頭的 -- 和 [[ 之間不可以有空格,結尾是兩個 ]]]--[[這也是多行注釋不是兩行單行注釋--]]

以上我們對 Lua 便有了一個初步認識,下面來學習 Lua 的數據結構。

Lua 的數值

Lua 的數值類型為 number,無論是整數還是浮點型,類型都是 number。

-- Lua 和 Python 類似,在創(chuàng)建變量時不需要指定類型,解釋器會自動根據賦的值來判斷
a = 123
b = 3.14
print(a, b)  -- 123	3.14-- Lua中,每一行語句的結尾也不需要加分號,直接換行即可
-- 當然加分號也可以,跟 Python 是類似的
c = 123;
d = 456
print(c, d)  -- 123	456-- 并且在 Python 中,如果加上了分號,那么兩行賦值可以寫一行
-- 比如 e = 1; f = 2,這在 Lua 中也是可以的
e = 1; f = 2
print(e, f)  -- 1	2-- 但 Lua 更加彪悍,不加分號也可以
-- 如果在 Python 中這么寫,則肯定是報錯的
g = 3 h = 4
print(g, h)  -- 3	4-- 但是我們不建議將多行賦值語句寫在同一行里面,最好要分行寫
-- 如果寫在同一行,那么應該使用 Lua 的多元賦值
a, b = 1, 2
print(a, b)  -- 1	2

可能有人發(fā)現(xiàn)了,我們在最上面已經創(chuàng)建 a 和 b 這兩個變量了,但是最后又創(chuàng)建了一次,這一點和 Python 類似,可以創(chuàng)建多個同名變量。比如創(chuàng)建 a = 1,然后又創(chuàng)建 a = 2,這是允許的,只不過相當于發(fā)生了更新,將 a 的值由 1 變成了 2,當然即便賦值為其它類型也沒問題。比如先創(chuàng)建 a = 數值,然后再將 a 的值換成字符串,這一點和 Python 一樣,因為在 Lua 中,全局變量是通過 table、也就是"表"來存儲的。

這個 table 后面會詳細說,你暫時可以理解為哈希表,或者當成 Python 的字典,而且 Python 中全局變量也是通過字典存儲的。

我們通過 Lua 的數值,演示了在 Lua 中如何創(chuàng)建一個變量,并且還介紹了 Lua 中全局變量的存儲方式。然后是整數和浮點數的區(qū)分,既然它們的類型都是 number,那要怎么區(qū)分呢?

a = 123
b = 123.  -- . 后面不寫東西的話,默認是 .0
c = .123  -- . 前面不寫東西的話,默認是 0.
print(a, b, c)  -- 123	123.0	0.123-- Lua 中,可以使用 type 函數檢測變量的類型
print(type(a))  -- number
print(type(b))  -- number
print(type(c))  -- number-- 這個 type 是內置的,它檢測的是 lua 中的基礎類型
-- 而我們說 Lua 不區(qū)分整型和浮點型,如果想精確區(qū)分的話,那么可以使用 math.type
-- 整型是 integer,浮點型是 float
print(math.type(a))  -- integer
print(math.type(b))  -- float
print(math.type(c))  -- float
-- 如果一個數值中出現(xiàn)了小數點,那么 math.type 得到的就是 float

使用 type 和 math.type 得到的都是一個字符串,另外我們是直接使用的 math.type,這個 math 類似于一個外部包。比如 Python 也有 math 包,只不過在 Lua 中不需要像 Python 那樣顯式導入,直接用即可,包括后面處理字符串用的包也是如此。

整數和浮點數之間的比較

整數和浮點數可以比較:

print(3 == 3.0)  -- true
print(-3 == -3.0)  -- true-- 我們看到,如果小數點后面是 0,那么是相等的,這一點和 Python 一樣
-- 另外 Lua 也支持科學計數法
print(3e3)  -- 3000.0-- Lua 中 a ^ b 表示 a 的 b 次方
-- 如果運算中出現(xiàn)了浮點數,或者發(fā)生了冪運算,那么結果就是浮點
print(3 ^ 2)  -- 9.0
print(3 * 3)  -- 9-- Lua 也支持 16 進制
print(0x61)  -- 97

算術運算

算數運算沒啥好說的,如果是兩個整數運算,那么結果還是整數,如果出現(xiàn)了浮點數,那么結果為浮點數。

print(1 + 2, 1 + 2.)  -- 3 3.0
print(1 * 2, 1 * 2.)  -- 2 2.0
print(1 - 2, 1 - 2.)  -- -1 -1.0
print(13 % 5, 13. % 5)  -- 3 3.0

但是除法例外,兩個數相除的結果一定為浮點數。

print(3 / 2, 4 / 2, 4 / 2.)  -- 1.5	2.0	2.0

Lua 還提供了地板除,會對商向下取整,這是在 Lua5.3 中引入的。

print(3 // 2, 4 // 2)  -- 1	2
-- 另外,如果里面出現(xiàn)了浮點,那么即使是地板除,也一樣會得到小數
print(4 // 2.)  -- 2.0-- 雖然是浮點,但結果是 1.0, 相當于還是有向下取整的效果
print(4 // 2.5)  -- 1.0

當然 Lua 還有冪運算,使用 ^ 表示。

print(3 ^ 4)  -- 81.0
print(3e4)  -- 30000.0

只要出現(xiàn)了冪運算,得到的一定是浮點數。

位運算

數值之間還有關系運算,但比較簡單,這里就不贅述了。只是需要注意:不等于在其它語言中都是 !=,而在 Lua 中是 ~=。

位運算和主流編程語言也是比較類似,尤其是 Python,感覺 Lua 的很多設計都和 Python 比較相似。

-- 按位與 &
print(15 & 20)  -- 4
-- 按位或 |
print(15 | 20)  -- 31
-- 按位異或 ~, 在其它語言中是 ^,在 Lua 中是 ~
print(15 ~ 20)  -- 27
-- 取反, 取反的話也是 ~
print(~20)  --  -21-- 左移
print(2 << 3)  -- 16
-- 右移
print(16 >> 2)  -- 4

以上這些操作符是在 5.3 當中才提供的,如果是之前的版本,則不能使用這些操作符。

數學庫

Lua 也提供了一個數學庫,叫做 math,里面定義了一些用于計算的函數,比如:sin、cos、tan、asin、floor、ceil 等等。

在這里插入圖片描述

這個在用的時候直接通過 IDE 提示,或者查詢文檔即可,這里就不演示了。

Lua 的字符串

下面我們看看 Lua 的字符串,字符串既可以使用雙引號、也可以使用單引號。注意:Lua 的字符串是不可變量,不能本地修改,如果想修改只能創(chuàng)建新的字符串。

name = "komeiji satori"
print(name) -- komeiji satori-- 使用 # 可以獲取其長度
print(#name, #"name") -- 14	4-- 使用 .. 可以將兩個字符串連接起來
print("aa" .. "bb") -- aabb
print("name: " .. name) -- name: komeiji satori-- .. 的兩邊可以沒有空格,但為了規(guī)范,建議前后保留一個空格
-- 另外 .. 前后還可以跟數字,會將數字轉成字符串
print("abc" .. 3, 3 .. 4, 3 .. "abc") -- abc3	34	3abc
-- 另外如果 .. 的前面是數字的話,那么 .. 的前面必須有空格
-- 也就是寫成類似于 3 .. 的形式,不可以寫 3..
-- 因為 3 后面如果直接出現(xiàn)了 . 那么這個 . 會被當成小數點來解釋

另外如果 .. 的前面是數字的話,那么 .. 的前面必須有空格,也就是寫成類似于 3 .. 的形式,不可以寫 3..。因為 3 后面如果直接出現(xiàn)了 .,那么這個 . 會被當成小數點來解釋。

Lua 內部也支持多行字符串,使用[[]]表示。

msg = [[你好呀你在什么地方呀你吃了嗎
]]print(msg)
--[[你好呀你在什么地方呀你吃了嗎]]

字符串和數值的轉換

Lua 中字符串可以和數值相加,也可以相互轉換。

-- 如果字符串和整數運算,那么得到的是浮點數
-- 你可以認為只有整數和整數運算才有可能得到整數,而字符串不是整數
print("10" + 2)  -- 12.0
print("10.1" + 2)  -- 12.1-- 調用 tonumber 函數可以將字符串顯式地轉為整數
print(type(tonumber("10")))  -- number
print(tonumber("10") + 2)  -- 12-- 如果轉化失敗,那么結果為 nil
print(tonumber("ff"))  -- nil-- 當然有些時候我們的數字未必是 10 進制,比如上面的 ff,它可以是 16 進制
-- 如果需要進制,那么就給 tonumber 多傳遞一個參數即可
print(tonumber("ff", 16))  -- 255
print(tonumber("11101", 2))  -- 29
print(tonumber("777", 8))  -- 511-- 8 進制,允許出現(xiàn)的最大數是 7,所以轉化失敗,結果為 nil
print(tonumber("778", 8))  -- nil-- 數值轉成字符串,則是 tostring
print(tostring(100) == "100")  -- true
print(tostring(100) == 100)  -- false
print(tostring(3.14) == "3.14")  -- true

所以數值和字符串是可以相加的,當然相減也可以,會將字符串轉成浮點數。也可以判斷是否相等或者不相等,這個時候會根據類型判斷,不會隱式轉化了,由于兩者類型不一樣,直接不相等。但兩者無法比較大小,只能判斷是否相等或者不等,因為 2 < 15 但 “2” > “15”,所以為了避免混淆,在比較的時候 Lua 不會隱式轉換、加上類型不同也無法比較大小,因此直接拋異常。

字符串標準庫

Lua 處理字符串還可以使用一個叫 string 的標準庫,這個標準庫也是內嵌在解釋器里面,我們直接通過 string.xxx 即可使用。下面就來看看 string 這個標準庫都提供了哪些函數吧,補充一下 Lua 的字符串是以字節(jié)為單位的,不是以字符為單位的。因此 string 的大部分函數不適合處理中文(除了少數例外),如果要處理中文,可以使用后面介紹的 utf8。

-- 查看字符串的長度
print(string.len("abc"), #"abc")  -- 3	3
-- 一個漢字占三個字節(jié),默認是以字節(jié)為單位的,計算的是字節(jié)的個數
print(string.len("古明地覺"), #"古明地覺")  -- 12	12-- 重復字符串 n 次
print(string.rep("abc", 3))  -- abcabcabc
-- 如果是單純的重復字符串的話,也可以對中文操作,因為不涉及字符的局部截取
print(string.rep("古明地覺", 3))  -- 古明地覺古明地覺古明地覺-- 字符串變小寫,可以用于中文,但是沒意義
print(string.lower("aBc"))  -- abc
print(string.lower("古明地覺"))  -- 古明地覺-- 同理還有轉大寫
print(string.upper("aBc"))  -- ABC
print(string.upper("古明地覺"))  -- 古明地覺-- 字符串翻轉,這個不適合中文
print(string.reverse("abc"))  -- cba
print(string.reverse("古明地覺"))  -- ��謎厘椏�
-- 我們看到中文出現(xiàn)了亂碼,原因就是這個翻轉是以字節(jié)為單位從后向前翻轉
-- 而漢字占 3 個字節(jié),需要以 3 個字節(jié)為單位翻轉-- 字符串截取,注意:Lua 中索引是從 1 開始的
-- 結尾也可以寫成 -1,并且字符串截取包含首尾兩端
print(string.sub("abcd", 1, -1))  -- abcd
print(string.sub("abcd", 2, -2))  -- bc
-- 可以只指定開頭,不指定結尾,但是不可以開頭結尾都不指定
print(string.sub("abcd", 2))  -- bcd-- 同樣不適合中文,除非你能準確計算字節(jié)的數量
print(string.sub("古明地覺", 1, 3))  -- 古
print(string.sub("古明地覺", 1, 4))  -- 古�
-- 超出范圍,就為空字符串
print(string.sub("古明地覺", 100, 400) == "")  -- true-- 將數字轉成字符
print(string.char(97))  -- a
-- 如果是多個數字,那么在轉化成字符之后會自動拼接成字符串
print(string.char(97, 98, 99))  -- abc-- 字符轉成數字,默認只轉換第 1 個
print(string.byte("abc"))  -- 97
-- 可以手動指定轉換第幾個字符
print(string.byte("abc", 2))  -- 98
print(string.byte("abc", -1))  -- 99
-- 超出范圍,那么返回 nil
print(string.byte("abc", 10) == nil)   -- nil
-- 轉換多個字符也是可以的,這里轉化索引為 1 到 -1 之間的所有字符
print(string.byte("abc", 1, -1))  -- 97	98	99
-- 越界也沒事,有幾個就轉化幾個
print(string.byte("abc", 1, 10))  -- 97	98	99
-- 另外,這里是返回了多個值,我們也可以用多個變量去接收
a, b, c = string.byte("abc", 1, 10)
print(a, b, c)  -- 97	98	99-- 關乎 Lua 返回值,由于涉及到了函數,我們后面會說
-- 字符串的格式化,格式化的風格類似于 C
print(string.format("name = %s, age = %d, number = %03d", "古明地覺", 17, 1))  -- name = 古明地覺, age = 17, number = 001-- 字符串的查找,會返回兩個值,分別是開始位置和結束位置
print(string.find("abcdef", "de"))  -- 4	5
-- 不存在則為 nil
print(string.find("abcdef", "xx"))  -- nil-- 字符串的全局替換,這個替換可以用中文,返回替換之后的字符串和替換的個數
print(string.gsub("古名地覺 名名 那么可愛", "名", "明"))  -- 古明地覺 明明 那么可愛	3
-- 我們同樣可以使用返回值去接
new_str, count = string.gsub("古名地覺 名名 那么可愛", "名", "明")
print(new_str)  -- 古明地覺 明明 那么可愛

關于處理 ASCII 字符,string 庫為我們提供了以上的支持,可以看到支持的東西還是比較少的,因為 Lua 的源碼總共才兩萬多行,這就決定了它沒辦法提供過多的功能。Lua 主要是用來和別的語言結合的,并且 string 庫提供的東西也不少了。

下面來看看 utf-8,我們說 string 庫不是用來處理 unicode 字符的,如果處理 unicode 字符的話,需要使用 utf8 這個庫。

-- Lua 中存儲 unicode 字符使用的編碼是 utf-8
-- 計算長度
print(utf8.len("古明地覺"))  -- 4-- 類似于 string.byte,這兩個可以通用
print(utf8.codepoint("古明地覺", 1, -1))  -- 21476	26126	22320	35273-- 類似于 string.char,這兩個可以通用
print(utf8.char(21476, 26126, 22320, 35273))  -- 古明地覺-- 截取,使用 string.sub,但不同字符占的字節(jié)大小可能不一樣,這時候怎么截取呢
-- 可以通過 utf8.offset 計算出,偏移到第 n 個字符的字節(jié)量
print(string.sub("古明地覺", utf8.offset("古明地覺", 2)))  -- 明地覺
print(string.sub("古明地覺", utf8.offset("古明地覺", -2)))  -- 地覺-- 遍歷,遍歷使用了 for 循環(huán),我們后面說,現(xiàn)在先看一下
for i, c in utf8.codes("古a明b地c覺") doprint(i, c, utf8.char(c))--[[1	21476	古4	97	    a5	26126	明8	98	    b9	22320	地12	99	    c13	35273	覺]]
end

以上便是 Lua 處理字符串的一些操作, 盡管功能提供的不是非常的全面,但這與 Lua 的定位有關。

Lua的控制結構

控制結構主要有兩種:條件語句和循環(huán)語句。

條件語句

-- 單獨的 if
if condition thenstatement
end-- if 和 else
if condition thenstatement
elsestatement
end-- if elseif else
-- 注意:是 elseif,不是 else if,else 和 if 之間需要連起來
if condition thenstatement
elseif condition thenstatement
elseif condition thenstatement
elsestatement
end    

if 和 elseif 后面必須加上一個 then,類似于 Python 中必須加上一個冒號一樣,但是 else 則不需要 then。另外每個 if 語句,在結尾處必須有一個 end,來標志這個 if 語句塊的結束。不過既然結尾有 end,那么 Lua 中也就不需要縮進了,但 Python 則是必須嚴格遵循縮進規(guī)范,而 Lua 則不被縮進約束。但還是那句話,為了代碼的可讀性還是建議按照 Python 的規(guī)范來編寫。

a = 85if a < 60 thenprint("不及格")
elseif a < 85 thenprint("及格")
elseif a < 100 thenprint("優(yōu)秀")
elseif a == 100 thenprint("滿分")
elseprint("無效的分數")
end
-- 優(yōu)秀

循環(huán)while語句

while condition dostatement
end

repeat … until

repeat … until 說白點就是一直重復做,直到滿足某個條件停下來。

i =1 
sum = 0-- 不斷的執(zhí)行 sum = sum + 1 和 i = i + 1,直到滿足 i >= 10 的時候停下來
repeatsum = sum + ii = i + 1
until i >= 10print(string.format("sum = %d", sum))  -- sum = 45

循環(huán) for 語句

break

break 用于跳出循環(huán)體,可以用于 for、while、repeat,注意:沒有 continue。

and 和 or

如果需要多個判斷條件,那么可以使用 and 和 or 進行連接。

username = "zpli"
password = "123456"if username == "zpli" and password == "123456" thenprint("歡迎來到避難小屋")
elseprint("用戶名密碼不對")
end
-- 歡迎來到避難小屋-- 另外 Lua 中還有 not 表示取反,得到布爾值
-- 這里著重強調一點,在 Lua 中只有 false 和 nil 才為假,其它全部為真
-- 這里和 Python 不一樣,在 Python 中 0、"" 是假,但在 Lua 中是真
print(not 0)  -- false
print(not "")  -- false
print(not not "")  -- true
-- 0 和 "" 為真,所以使用 not 得到假,兩個 not 得到真

以上我們就介紹了 Lua 的控制結構,比較簡單。

Lua的表

下面來看看 Lua 的表(Table),表是 Lua 語言中最主要(事實上也是唯一)的數據結構,表既可以當做數組來用,也可以當成哈希表來用。這個和 Python 的字典非常類似,比如我們之前查看變量類型的 math.type,本質上就是以字符串 “type” 來檢索表 math。而在 Python 中,比如調用 math.sin,本質也是從 math 模塊的屬性字典里面查找 key 為 “sin” 對應的 value。

然后看看在 Lua 中如何創(chuàng)建表。

-- 類似于 Python 的字典,Lua 中創(chuàng)建表直接使用大括號即可
t = {}
-- 返回的是表的一個引用
print(t)  -- table: 00000000010b9160
-- 類型為 table
print(type(t) == "table")  -- true

在這里我們需要介紹一下 Lua 的變量,在 Lua 中分為全局變量和局部變量,這兩者我們會在函數中細說??傊壳皠?chuàng)建的都是全局變量,其有一個特點:

-- 對于沒有創(chuàng)建的變量,可以直接打印,結果是一個 nil
print(a)  -- nil-- c 這個變量沒有創(chuàng)建,因此是 nil,那么 d 也是 nil
d = c
print(d)  -- nil-- 所以我們看到程序中明明沒有這個變量,但是卻可以使用,只不過結果為 nil
-- 那如果我們將一個已經存在的變量賦值為 nil,是不是等于沒有創(chuàng)建這個變量呢?
-- 答案是正確的,如果將一個變量賦值為 nil,那么代表這個變量對應的內存就會被回收
name = "shiina mashiro"
name = nil  -- "shiina mashiro" 這個字符串會被回收

之所以介紹全局變量這個特性,是因為在表中,nil 是一個大坑,我們往下看。

a = {}a["name"] = "古明地覺"
a["age"] = 16-- 打印 a 只是返回一個引用
print(a)  -- table: 00000000000290e0
print(a["name"], a["age"])  -- 古明地覺	16-- 更改表的元素
-- table 類似于哈希表,key 是不重復的,所以重復賦值相當于更新
a["age"] = a["age"] + 1
print(a["age"])  -- 17-- 全局變量也是通過 table 存儲的,我們可以給一個變量不斷地賦值,賦上不同類型的值
a["age"] = 18
print(a["age"])  -- 18
a["age"] = "十六"
print(a["age"])  -- 十六-- 創(chuàng)建 table 返回的是一個引用
b = a
-- 此時的 b 和 a 指向的是同一個 table,修改 b 會影響到 a
b["name"] = "satori"
print(a["name"])  -- satori-- 賦值為 nil,等價于回收對象
a = nil 
-- 但是只將 a 賦值為nil,顯然還不夠,因為還有 b 在指向上面的 table
b = nil 
-- 這樣的話,table 就被回收了

Lua 的 table 既可以做哈希表,也可以當做數組,有興趣可以看 Lua 的源代碼,非常的精簡。下面來看看 table 如何當成數組來使用:

a = {}for i = 1, 10 doa[i] = i * 2
endprint(a[3])  -- 6-- table 的底層是一個結構體,里面實現(xiàn)了哈希表和數組兩種結構
-- 如果 key 是整型,那么會通過數組的方式來存儲,如果不是,會使用哈希表來存儲
-- 注意:如果當成數組使用,那么索引也是從 1 開始的-- 此時是通過哈希表存儲的
a["x"] = 233
print(a["x"])  -- 233-- 除了a["x"]這種方式,還可以使用a.x,這兩者在 Lua 中是等價的
print(a.x)  -- 233-- a["name"] 和 a.name 是等價的,但是和 a[name] 不是等價的
-- 因為 name 是一個變量,而 name = "x",所以結果是 a["x"] 或者 a.x
a["name"] = "椎名真白"
name = "x"
print(a["name"], a.name, a[name])  -- 椎名真白	椎名真白	233

然后是關于整數和浮點數的一個坑,來看一下。

a = {}a[2] = 123
print(a[2.0])  -- 123a[2.0] = 456
print(a[2])  -- 456-- 所以這兩者是等價的,因為 2.0 會被隱式轉化為 2,事實上在 Python 的字典中也有類似的現(xiàn)象
-- d = {}; d[True] = 1; d[1] = 2; d[1.0] = 3; print(d)
-- 上面那行代碼在 Python 里面執(zhí)行一下,看看會發(fā)生什么-- 但對于字符串則不一樣,因為 2 和 "2" 不相等
a = {}
a[2] = 123
a["2"] = 456
print(a[2], a["2"])  -- 123	456-- 如果訪問表中一個不存在的 key 呢?
print(a["xxx"])  -- nil-- 我們看到得到的是一個 nil
-- 顯然我們想到了,如果將一個 key 對應的值顯式地賦值為 nil,那么也等價于刪除這個元素
a[2] = nil 

表構造器

估計有人目前對 table 即可以當數組又可以當哈希表會感到困惑,別著急我們會慢慢說。目前創(chuàng)建表的時候,都是創(chuàng)建了一張空表,其實在創(chuàng)建的時候是可以指定元素的。

a = {"a", "b", "c" }
print(a[1], a[2], a[3])  -- a	b	c
-- 我們看到如果不指定 key,那么表的元素是通過數組存儲的,這種存儲方式叫做 "列表式(list-style)"
-- 索引默認是 1 2 3 4...-- 此外,還可以這么創(chuàng)建
b = {name="mashiro", age=18 }
print(b["name"], b["age"])  -- mashiro	18
-- 第二種方式是通過哈希表存儲的,這種存儲方式叫做"記錄式(record-style)"-- 但如果存儲的 key 是數字或者特殊字符,那么需要使用 [] 包起來
b = {["+"] = "add", [3] = "xxx"}  -- 必須使用 ["+"] 和 [3],不能是單獨的 + 和 3
-- 同理獲取也只能是 b["+"] 和 b[3],不可以是 b.+ 和 b.3
print(b["+"], b[3])  -- add xxx-- 表也是可以嵌套的
a["table"] = b
print(a["table"]["+"])  -- add-- 此外,兩種方式也可以混合使用
mix = {'a', name='mashiro', 'b', age=18 }
print(mix[1], mix[2])  -- a	b
print(mix["name"], mix["age"])  -- mashiro	18-- 這里有必要詳細說明一下,即使是混合使用
-- 如果沒有顯式地指定 key、也就是列表式,那么會以數組的形式存儲,索引默認是 1 2 3...
-- 所以 a[1] 是 'a', a[2] 是 'b'-- 如果是這種情況呢?
mix = {'a', [2] = 1 }
print(mix[2])  -- 1
mix = {'a', 'b', [2] = 1 }
print(mix[2])  -- b
-- 解釋一下,首先對于單個標量來說,默認就是用數組存儲的,索引就是 1 2 3...
-- 但我們在通過記錄式設置的時候,對應的 key 使用的如果也是數組的索引,那么記錄式中設置的值會被頂掉
--[[
比如:mix = {'a', [2] = 1 }, 數組的最大索引是 1,所以 [2] = 1 是沒有問題的
但是 mix = {'a', 'b', [2] = 1 },數組最大索引是 2,所以 [2] = 1 會被頂掉,因為沖突了
]]-- 事實上 mix = {'a', 'b', [2] = 1 } 這種方式就等價于 mix = {[1] = 'a', [2] = 'b', [2] = 1 }
-- 如果 key 是整型,那么也通過數組存儲, 否則通過哈希表存儲
-- 只不過我們手動指定 [2] = 1 會先創(chuàng)建,然后被 [2] = 'b' 頂掉了
a = {'a', [1] = 1 }
print(a[1])  -- 'a'
a = {[1] = 1, 'a'}
print(a[1])  -- 'a'
-- 無論順序如何,a[1] 都會是 'a'

估計有人還有疑問,那就是a = {}; a[1] = 1; a[100] = 100或者a = {1, [100] = 100},如果這樣創(chuàng)建的話,那中間的元素是什么?因為我們說 key 是整型則以數組存儲,而數組又是連續(xù)的存儲的空間,而我們只創(chuàng)建了兩個元素,索引分別是 1 和 100,那么其它元素是以什么形式存在呢?帶著這些疑問,我們先往下看。

數組、列表和序列

現(xiàn)在我們知道了如果想表示常見的數組、或者列表,那么只需要使用整型作為索引即可。而且在 Lua 的 table 中,可以使用任意數字作為索引,只不過默認是從 1 開始的,Lua 中很多其它機制也遵循此慣例。

但是table的長度怎么算呢?我們知道對字符串可以使用 #,同理對 table 也是如此。

a = {1, 2, 3, name = 'mashiro', 'a' }
print(#a)  -- 4
-- 但是我們看到結果為 4,可明明里面有 5 個元素啊
-- 因為 # 計算的是索引為整型的元素的個數,更準確的說 # 計算的是使用數組存儲的元素的個數a = {[0] = 1, 2, 3, 4, [-1]=5}
print(#a)  -- 3
-- 此時的結果是 3,因為 0 和 -1 雖然是整型,但它們并沒有存儲在數組里
-- 因為 Lua 索引默認從 1 開始,如果想要被存儲的數組里面,那么索引必須大于 0a = {1, 2, [3.0]="xxx", [4.1] = "aaa" }
print(#a)  -- 3
-- 這里同樣是 3,因為 3.0 會被隱式轉化為 3,因此數組里面有 3 個元素,但是 4.1 不會

所以我們看到,# 計算的是存儲在數組里面的元素,也就是 table 中索引為正整數的元素,但真的是這樣嗎?

首先對于數組中存在nil的 table,使用 # 獲取長度是不可靠的,它只適用于數組中所有元素都不為 nil 的 table。事實上,將 # 應用于獲取 table 長度一直飽受爭議,以前很多人建議如果數組中存在 nil,那么使用 # 操作符直接拋出異常,或者說擴展一下 # 的語義。然而這些建議都是說起來容易做起來難,主要是在 Lua 中數組實際上是一個 table,而 table 的長度不是很好理解。

我們舉例說明:

a = {1, 2, 3, 4 }
a[2] = nil
-- 很容易得出這是一個長度為 4,第二個元素為 nil 的 table
print(#a)  -- 4-- 但是下面這個例子呢?沒錯,就是我們之前說的
b = {}
b[1] = 1
b[100] = 100
-- 是否應該認為這是一個具有 100 個元素,其中 98 個元素為 nil 的 table 呢?
-- 如果我們再將 a[100] 設置成 nil,該列表長度又是多少呢?是 100、99 還是 1 呢
print(#b)  -- 1
-- Lua 作者的想法是,像 C 語言使用 \0 作為字符串的結束一樣,Lua 可以使用 nil 來隱式地表示 table 的結束
-- 可問題是 a 的第二個元素也是 nil 啊,為什么長度是 4 呢-- 總之在 table 中出現(xiàn)了 nil,那么 # 的結果是不可控的
-- 有可能你多加一個 nil,結果就變了。當然,不要去探究它的規(guī)律,因為這沒有意義
-- 總之不要在 table 中寫 nil,在 table 中寫 nil 是原罪。不管是列表式、還是記錄式,都不要寫 nil,因為設置為 nil,就表示刪除這個元素-- 回到 b 這個 table 中,我們說它的長度為 1
print(#b)  -- 1
-- 但是數組中確實存在索引為 100 的元素
print(b[100])  -- 100

所以對 b 這個 table,其中數組到底是怎么存儲的,其實沒必要糾結,就當成索引為 2 到索引為 99 的元素全部是 nil 即可,但計算長度的時候是不準的,總之 table 中最好不要出現(xiàn) nil。

遍歷表

我們可以使用 for 循環(huán)去遍歷 table。

a = {"a", "b", name="mashiro", "c", age=18, "d" }-- for 循環(huán)除了 for i = start, end, step 這種方式之外,還可以作用在表上面
-- 只不過需要使用 pairs 將 table 包起來:for k, v in pairs(t)
for index, value in pairs(a) doprint(index, value)--[[1	    a2	    b3	    c4	    dage	    18name    mashiro]]
end
-- 這里的 for 循環(huán)中出現(xiàn)了兩個循環(huán)變量,分別表示索引和值
-- 如果只有一個變量,那么得到的是索引,或者哈希表的 key
-- 然后遍歷的時候先遍歷數組(按照索引從小到大輸出),然后遍歷哈希表(不保證順序)-- 除了 pairs,還有 ipairs,ipars 是只遍歷存在于數組里面的元素
a = {[4] = "a", [3] = "b", name="mashiro", "c", age=18, [2] = "d" }
for index, value in ipairs(a) doprint(index, value)--[[1	c2	d3	b4	a]]
end
-- 打印按照索引從小到大打印,但是不建議這么創(chuàng)建table

如果 table 中出現(xiàn)了 nil,那么使用 for 循環(huán)去遍歷會發(fā)生什么奇特的現(xiàn)象呢?

-- 不過在此之前,還是先來看一個坑向的
a = {[3] = 1, 'a', 'b', 'c' }
-- 這個時候 a[3] 是多少呢?
print(a[3])  -- c
-- 我們說只要是列表式,都是從 1 開始,所以 [3] = 1 最終會被 [3] = 'c' 所頂掉
-- 上面的賦值等價于 a = {[3] = 1, [1] = 'a', [2] = 'b', [3] = 'c'}
-- 因為如果不指定 key,那么 Lua 會按照 1 2 3 4 ··· 自動給一個 key(準確來說是索引),因為它們存在數組中-- 再來看看 table 中出現(xiàn)了 nil,for 循環(huán)會如何表現(xiàn)
a = {'a', nil, 'b', 'c' }
print(#a)  -- 4for index, value in ipairs(a) doprint(index, value)--[[1   a]]
end
-- 長度雖然是 4(當然我們知道這不準),但在遍歷的時候一旦遇到 nil 就會終止遍歷
-- 當然這個 nil 要是數組中的 nil,不是哈希表中的 nil
-- 但如果是 pairs,那么會遍歷值不為 nil 的所有記錄
a = {'a', nil, 'b', 'c', name=nil, age=18}
for index, value in pairs(a) doprint(index, value)--[[1	a3	b4	cage	18]]
end
-- 但我們看到值 "b" 對應的索引是 3,盡管前面的是 nil,但畢竟占了一個坑,所以 "b" 對應的索引是 3-- 當然我們還可以使用獲取長度、數值遍歷的方式,當然前提是 table 中不能出現(xiàn) nil
a = {'a', 'b', 123, 'xx' }
for idx = 1, #a doprint(a[idx])--[[ab123xx]]
end

表標準庫

表的標準庫提供一些函數,用于對表進行操作,注意:這個標準庫也叫 table。

a = {10, 20, 30 }
print(a[1], a[2], a[3])  -- 10	20	30-- 使用 table.insert 可以插入一個值
-- 接收參數為:table 插入位置 插入的值
table.insert(a, 2, "xxx")
print(a[1], a[2], a[3], a[4])  -- 10   xxx	20	30
-- 如果不指定位置,那么默認會添加在結尾
-- 此時傳遞兩個參數即可:table 插入的值
table.insert(a, "古明地覺")
print(a[#a])  -- 古明地覺-- 既然有 insert,那么就會有 remove
-- 接收參數:table 移除的元素的位置(索引)
print(a[1], a[2], a[3], a[4], a[5])  -- 10	xxx	20	30
table.remove(a, 3)
print(a[1], a[2], a[3], a[4], a[5])  -- 10	xxx	30	古明地覺    nil-- 我們看到使用 remove 之后,后面的元素會依次向前移動,因此無需擔心會出現(xiàn) nil 什么的
-- 不過這也說明了,remove 的效率不是很高,因為涉及到元素的移動
-- 但 table 中的函數都是 C 實現(xiàn)的,也是很快的,因此也不用太擔心-- 另外在 lua5.3 中,還提供了一個 move 函數
-- table.move(table, start, end, target),表示將 table 中 [start, end] 之間的元素移動到索引為 target 的位置上
-- 也是 start 位置的元素跑到 target 處,start + 1 -> target + 1、 end -> target + end - start
t = {1, 2, 3, 4}
table.move(t, 2, #t, 3)
print(t[1], t[2], t[3], t[4], t[5])  -- 1	2	2	3	4
-- 很好理解,{1 2 3 4} 中索引為 [2, #t],移動到索引為 3 的位置上,因此結果是1 2 2 3 4,結果會多出一個-- 這里的 move 實際上是將一個值從一個地方拷貝 copy 到另一個地方
-- 另外,我們除了可以將元素移動到 table 本身之外,還可以移動到另一個 table
t1 = {"a", "b", "c", "d" }
t2 = {"x", "y" }
-- 表示將 t1 中 [2, #t1] 的元素移動到 t2 中索引為 2 的地方
table.move(t1, 2, #t1, 2, t2)
for idx = 1, #t2 doprint(t2[idx])--[[xbcd]]
end-- table 標準庫中還提供了 concat 函數,會將表里面的元素拼接起來
a = {1, 2, "xxx", 3, "aaa" }
print(table.concat(a))  -- 12xxx3aaa

來個思考題吧

a = "b"
b = "a"t = {a = "b", [a] = b }
print(t.a, t[a], t[t.b], t[t[b]])-- 上面的 print 會打印出什么呢?我們分析一下,首先看 t 這個表,其中 a = "b" 無需多說
-- 關鍵是 [a] = b,我們說 a 和 b 都是變量,并且 a = "b"、b = "a", 所以結果等價于 ["b"] = "a", 即:b = "a"
-- 因此這里的 t 可以看做是 {a = "b", b = "a"}-- 那么 t.a 顯然是 "b",t[a]等于t["b"],因此結果是 "a"
-- t.b 結果是 "a",那么 t[t.b] 等于是 t["a"],所以結果是 "b"
-- t[b] -> t["a"] -> "b",那么 t[t[b]] -> t["b"] -> "a",因此結果是 "a"
-- 所以 print 會打印出: "b" "a" "b" "a"-- 下個問題
a = {}
a.a = a
print(a)  -- table: 0000000000d98ef0
print(a.a)  -- table: 0000000000d98ef0
print(a.a.a)  -- table: 0000000000d98ef0-- 打印的都是一樣的,我們說 Lua 中的 table 返回的一個引用
-- a.a = a,本身顯然陷入了套娃的狀態(tài)

以上就是 Lua 的表,總的來說并不復雜,只是要注意里面不要出現(xiàn) nil 就好。然后 table 采用了兩種數據結構:數組和哈希表,它即可以當成數組來用,也可以當成哈希表來用,當然也可以混合使用。如果 key 是整數,那么存在數組中,否則存在哈希表中。

http://aloenet.com.cn/news/43155.html

相關文章:

  • 西寧網站建設的公司哪家好免費二級域名建站
  • 百度wap網站建設cdq百度指數
  • 惠州企業(yè)自助建站自己怎么創(chuàng)建網站
  • 臨沂購買模板建站網頁首頁設計圖片
  • 深圳教育網站設計公司鄭州seo顧問培訓
  • java做網站沒有php好嗎360瀏覽器網頁版入口
  • 網站查詢信息愛站網關鍵字挖掘
  • wordpress建設網站石家莊全網seo
  • 美國網站服務器營銷活動推廣策劃
  • 做網站seo優(yōu)化線上營銷策略都有哪些
  • 西安手機定制網站建設電商網站平臺有哪些
  • 廣州品牌網站建設seo網站推廣與優(yōu)化方案
  • 中企動力科技股份有限公司網站官網2020十大網絡熱詞
  • 新手引導做的差的網站免費建站軟件
  • 動態(tài)網站完整版百度pc網頁版
  • ??诜慨a網站建設最近發(fā)生的熱點事件
  • 廣州市公司網站建設公司在線培訓app
  • 網站 建設需求上海aso蘋果關鍵詞優(yōu)化
  • 網站域名 設置快速網站seo效果
  • 怎樣做網站首頁圖片變換seo研究中心培訓機構
  • 做俄羅斯外貿的網站設計網址域名ip查詢
  • 潮汕網站建設antnw網頁設計需要學什么
  • 網站加速服務武漢seo網絡優(yōu)化公司
  • 做網站的學什么代碼海外推廣服務
  • 麗水微信網站建設哪家好滄州網絡推廣公司
  • 建立網站的詳細步驟營銷模式有哪些 新型
  • 香港空間建網站百度一下百度網頁版
  • html網站要怎么做簡單制作html靜態(tài)網頁
  • 眉山市住房和城鄉(xiāng)建設局網站西安推廣平臺排行榜
  • 還有哪些免費的網站可以做H5優(yōu)化推廣什么意思