精华内容
下载资源
问答
  • Lua 字符串

    2021-01-03 03:25:45
    Lua 字符串 字符串或串(String)是由数字、字母、下划线组成的一串字符。 Lua 语言中字符串可以使用以下三种方式来表示: 单引号间的一串字符。 双引号间的一串字符。 [[和]]间的一串字符。 以上三种方式的字符串实例...
  • Lua字符串

    2019-10-23 17:08:16
    Lua字符串 字符串或串(String)是由数字、字母、下划线组成的一串字符。 Lua语言中字符串可以使用以下三种方式来表示: 单引号间的一串字符。 双引号间的一串字符。 [[和]]间的一串字符。 以上三种方式的字符串...

    Lua字符串

    字符串或串(String)是由数字、字母、下划线组成的一串字符。
    Lua语言中字符串可以使用以下三种方式来表示:

    • 单引号间的一串字符。
    • 双引号间的一串字符。
    • [[和]]间的一串字符。

    以上三种方式的字符串实例如下:

    string1 = "Lua"
    print("\"字符串 1 是\"",string1)
    string2 = 'runoob.com'
    print("字符串 2 是",string2)
    
    string3 = [["Lua 教程"]]
    print("字符串 3 是",string3)
    

    以上代码执行输出结果为:

    "字符串 1 是"    Lua
    字符串 2 是    runoob.com
    字符串 3"Lua 教程"
    

    转义字符用于表示不能直接显示的字符,比如后退键,回车键,等。如在字符串转换双引号可以使用“\”。所有的转义字符和对应的意义:

    转义字符 意义 ASCII码值(十进制)
    \a 响铃(BEL) 007
    \b 退格(BS),将当前位置移到前一列 008
    \f 换页(FF),将当前位置移到下页开头 012
    \n 换行(LF),将当前位置移到下一行开头 010
    \r 回车(CR),将当前位置移到本行开头 013
    \t 水平制表(HT)(跳到下一个TAB位置) 009
    \v 垂直制表(VT) 011
    \ 代表一个反斜线字符"" 092
    \‘ 代表一个单引号(撇号)字符 039
    \’‘ 代表一个双引号字符 034
    \0 空字符(NULL) 000
    \ddd 1到3位八进制数所代表的任意字符 三位八进制
    \xhh 1到2位十六进制所代表的任意字符 二位十六进制

    字符串操作

    Lua 提供了很多的方法来支持字符串的操作:

    序号 方法&用途
    1 string.upper(argument):字符串全部转为大写字母。
    2 string.lower(argument):字符串全部转为小写字母。
    3 string.gsub(mainString,findString,replaceString,num)在字符串中替换。mainString 为要操作的字符串, findString 为被替换的字符,replaceString 要替换的字符,num 替换次数(可以忽略,则全部替换),如:string.gsub(“aaaa”,“a”,“z”,3); 结果为:zzza 3
    4 string.find (str, substr, [init, [end]]) 在一个指定的目标字符串中搜索指定的内容(第三个参数为索引),返回其具体位置。不存在则返回 nil。如:string.find(“Hello Lua user”, “Lua”, 1) 结果为:7 9
    5 string.reverse(arg)字符串反转 如 string.reverse(“Lua”) 结果为:auL
    6 string.format(…) 返回一个类似printf的格式化字符串 如 string.format(“the value is:%d”,4) 结果为:the value is:4
    7 string.char(arg) 和 string.byte(arg[,int]) char 将整型数字转成字符并连接, byte 转换字符为整数值(可以指定某个字符,默认第一个字符)。 如 string.char(97,98,99,100);string.byte(“ABCD”,4);string.byte(“ABCD”); 结果分别为:abcd 68 65
    8 string.len(arg)计算字符串长度。如:string.len(“abc”) 结果为:3
    9 string.rep(string, n) 返回字符串string的n个拷贝 如 string.rep(“abcd”,2) 结果为:abcdabcd
    10 … 链接两个字符串 如 print(“www.runoob.”…“com”) 结果为:www.runoob.com
    11 string.gmatch(str, pattern) 回一个迭代器函数,每一次调用这个函数,返回一个在字符串 str 找到的下一个符合 pattern 描述的子串。如果参数 pattern 描述的字符串没有找到,迭代函数返回nil。
    12 string.match(str, pattern, init) string.match()只寻找源字串str中的第一个配对. 参数init可选, 指定搜寻过程的起点, 默认为1。 在成功配对时, 函数将返回配对表达式中的所有捕获结果; 如果没有设置捕获标记, 则返回整个配对字符串. 当没有成功的配对时, 返回nil。

    11例子:

    > for word in string.gmatch("Hello Lua user", "%a+") do print(word) end
    Hello
    Lua
    user
    

    12例子:

    > = string.match("I have 2 questions for you.", "%d+ %a+")
    2 questions
    
    > = string.format("%d, %q", string.match("I have 2 questions for you.", "(%d+) (%a+)"))
    2, "questions"
    

    字符串大小写转换

    以下实例演示了如何对字符串大小写进行转换:

    string1 = "Lua";
    print(string.upper(string1))
    print(string.lower(string1))
    

    以上代码执行结果为:

    LUA
    lua
    
    字符串查找与反转

    以下实例演示了如何对字符串进行查找与反转操作:

    string = "Lua Tutorial"
    -- 查找字符串
    print(string.find(string,"Tutorial"))
    reversedString = string.reverse(string)
    print("新字符串为",reversedString)
    

    以上代码执行结果为:

    5    12
    新字符串为    lairotuT auL
    
    字符串格式化

    Lua 提供了 string.format() 函数来生成具有特定格式的字符串, 函数的第一个参数是格式 , 之后是对应格式中每个代号的各种数据。

    由于格式字符串的存在, 使得产生的长字符串可读性大大提高了。这个函数的格式很像 C 语言中的 printf()。

    以下实例演示了如何对字符串进行格式化操作:

    格式字符串可能包含以下的转义码:

    • %c - 接受一个数字, 并将其转化为ASCII码表中对应的字符
    • %d, %i - 接受一个数字并将其转化为有符号的整数格式
    • %o - 接受一个数字并将其转化为八进制数格式
    • %u - 接受一个数字并将其转化为无符号整数格式
    • %x - 接受一个数字并将其转化为十六进制数格式, 使用小写字母
    • %X - 接受一个数字并将其转化为十六进制数格式, 使用大写字母
    • %e - 接受一个数字并将其转化为科学记数法格式, 使用小写字母e
    • %E - 接受一个数字并将其转化为科学记数法格式, 使用大写字母E
    • %f - 接受一个数字并将其转化为浮点数格式
    • %g(%G) - 接受一个数字并将其转化为%e(%E, 对应%G)及%f中较短的一种格式
    • %q - 接受一个字符串并将其转化为可安全被Lua编译器读入的格式
    • %s - 接受一个字符串并按照给定的参数格式化该字符串

    为进一步细化格式, 可以在%号后添加参数. 参数将以如下的顺序读入:

    • (1) 符号: 一个+号表示其后的数字转义符将让正数显示正号. 默认情况下只有负数显示符号.
    • (2) 占位符: 一个0, 在后面指定了字串宽度时占位用. 不填时的默认占位符是空格.
    • (3) 对齐标识: 在指定了字串宽度时, 默认为右对齐, 增加-号可以改为左对齐.
    • (4) 宽度数值
    • (5) 小数位数/字串裁切: 在宽度数值后增加的小数部分n, 若后接f(浮点数转义符, 如%6.3f)则设定该浮点数的小数只保留n位, 若后接s(字符串转义符, 如%5.3s)则设定该字符串只显示前n位.
    string1 = "Lua"
    string2 = "Tutorial"
    number1 = 10
    number2 = 20
    -- 基本字符串格式化
    print(string.format("基本格式化 %s %s",string1,string2))
    -- 日期格式化
    date = 2; month = 1; year = 2014
    print(string.format("日期格式化 %02d/%02d/%03d", date, month, year))
    -- 十进制格式化
    print(string.format("%.4f",1/3))
    

    以上代码执行结果为:

    基本格式化 Lua Tutorial
    日期格式化 02/01/2014
    0.3333
    

    其他例子:

    string.format("%c", 83)                 -- 输出S
    string.format("%+d", 17.0)              -- 输出+17
    string.format("%05d", 17)               -- 输出00017
    string.format("%o", 17)                 -- 输出21
    string.format("%u", 3.14)               -- 输出3
    string.format("%x", 13)                 -- 输出d
    string.format("%X", 13)                 -- 输出D
    string.format("%e", 1000)               -- 输出1.000000e+03
    string.format("%E", 1000)               -- 输出1.000000E+03
    string.format("%6.3f", 13)              -- 输出13.000
    string.format("%q", "One\nTwo")         -- 输出"One\
                                            --   Two"
    string.format("%s", "monkey")           -- 输出monkey
    string.format("%10s", "monkey")         -- 输出    monkey
    string.format("%5.3s", "monkey")        -- 输出  mon
    
    字符与整数相互转换

    以下实例演示了字符与整数相互转换:

    -- 字符转换
    -- 转换第一个字符
    print(string.byte("Lua"))
    -- 转换第三个字符
    print(string.byte("Lua",3))
    -- 转换末尾第一个字符
    print(string.byte("Lua",-1))
    -- 第二个字符
    print(string.byte("Lua",2))
    -- 转换末尾第二个字符
    print(string.byte("Lua",-2))
    
    -- 整数 ASCII 码转换为字符
    print(string.char(97))
    

    以上代码执行结果为:

    76
    97
    97
    117
    117
    a
    
    其他常用函数

    以下实例演示了其他字符串操作,如计算字符串长度,字符串连接,字符串复制等:

    string1 = "www."
    string2 = "runoob"
    string3 = ".com"
    -- 使用 .. 进行字符串连接
    print("连接字符串",string1..string2..string3)
    
    -- 字符串长度
    print("字符串长度 ",string.len(string2))
    
    -- 字符串复制 2 次
    repeatedString = string.rep(string2,2)
    print(repeatedString)
    

    以上代码执行结果为:

    连接字符串    www.runoob.com
    字符串长度     6
    runoobrunoob
    
    匹配模式

    Lua 中的匹配模式直接用常规的字符串来描述。 它用于模式匹配函数 string.find, string.gmatch, string.gsub, string.match。

    你还可以在模式串中使用字符类。

    字符类指可以匹配一个特定字符集合内任何字符的模式项。比如,字符类 %d 匹配任意数字。所以你可以使用模式串 %d%d/%d%d/%d%d%d%d 搜索 dd/mm/yyyy 格式的日期:

    s = "Deadline is 30/05/1999, firm"
    date = "%d%d/%d%d/%d%d%d%d"
    print(string.sub(s, string.find(s, date)))    --> 30/05/1999
    

    下面的表列出了Lua支持的所有字符类:

    单个字符(除 ^$()%.[]*±? 外): 与该字符自身配对

    • .(点): 与任何字符配对
    • %a: 与任何字母配对
    • %c: 与任何控制符配对(例如\n)
    • %d: 与任何数字配对
    • %l: 与任何小写字母配对
    • %p: 与任何标点(punctuation)配对
    • %s: 与空白字符配对
    • %u: 与任何大写字母配对
    • %w: 与任何字母/数字配对
    • %x: 与任何十六进制数配对
    • %z: 与任何代表0的字符配对
    • %x(此处x是非字母非数字字符): 与字符x配对. 主要用来处理表达式中有功能的字符(^$()%.[]*±?)的配对问题, 例如%%与%配对
    • [数个字符类]: 与任何[]中包含的字符类配对. 例如[%w_]与任何字母/数字, 或下划线符号(_)配对
    • [ ^数个字符类]: 与任何不包含在[]中的字符类配对. 例如[^%s]与任何非空白字符配对

    当上述的字符类用大写书写时, 表示与非此字符类的任何字符配对. 例如, %S表示与任何非空白字符配对.例如,’%A’非字母的字符:

    > print(string.gsub("hello, up-down!", "%A", "."))
    hello..up.down.    4
    

    数字4不是字符串结果的一部分,他是gsub返回的第二个结果,代表发生替换的次数。

    在模式匹配中有一些特殊字符,他们有特殊的意义,Lua中的特殊字符如下:

    ( ) . % + - * ? [ ^ $
    

    ‘%’ 用作特殊字符的转义字符,因此 ‘%.’ 匹配点;’%%’ 匹配字符 ‘%’。转义字符 '%'不仅可以用来转义特殊字符,还可以用于所有的非字母的字符。

    模式条目可以是:

    • 单个字符类匹配该类别中任意单个字符;
    • 单个字符类跟一个 ‘*’, 将匹配零或多个该类的字符。 这个条目总是匹配尽可能长的串;
    • 单个字符类跟一个 ‘+’, 将匹配一或更多个该类的字符。 这个条目总是匹配尽可能长的串;
    • 单个字符类跟一个 ‘-’, 将匹配零或更多个该类的字符。 和 ‘*’ 不同, 这个条目总是匹配尽可能短的串;
    • 单个字符类跟一个 ‘?’, 将匹配零或一个该类的字符。 只要有可能,它会匹配一个;
    • %n, 这里的 n 可以从 1 到 9; 这个条目匹配一个等于 n 号捕获物(后面有描述)的子串。
    • %bxy, 这里的 x 和 y 是两个明确的字符; 这个条目匹配以 x 开始 y 结束, 且其中 x 和 y 保持 平衡 的字符串。 意思是,如果从左到右读这个字符串,对每次读到一个 x 就 +1 ,读到一个 y 就 -1, 最终结束处的那个 y 是第一个记数到 0 的 y。 举个例子,条目 %b() 可以匹配到括号平衡的表达式。
    • %f[set], 指 边境模式; 这个条目会匹配到一个位于 set 内某个字符之前的一个空串, 且这个位置的前一个字符不属于 set 。 集合 set 的含义如前面所述。 匹配出的那个空串之开始和结束点的计算就看成该处有个字符 ‘\0’ 一样。

    模式:

    模式 指一个模式条目的序列。 在模式最前面加上符号 ‘^’ 将锚定从字符串的开始处做匹配。 在模式最后面加上符号 ‘KaTeX parse error: Expected group after '^' at position 24: …锚定到字符串的结尾。 如果 '^̲' 和 '’ 出现在其它位置,它们均没有特殊含义,只表示自身。

    捕获:

    模式可以在内部用小括号括起一个子模式; 这些子模式被称为 捕获物。 当匹配成功时,由 捕获物 匹配到的字符串中的子串被保存起来用于未来的用途。 捕获物以它们左括号的次序来编号。 例如,对于模式 “(a*(.)%w(%s*))” , 字符串中匹配到 “a*(.)%w(%s*)” 的部分保存在第一个捕获物中 (因此是编号 1 ); 由 “.” 匹配到的字符是 2 号捕获物, 匹配到 “%s*” 的那部分是 3 号。

    作为一个特例,空的捕获 () 将捕获到当前字符串的位置(它是一个数字)。 例如,如果将模式 “()aa()” 作用到字符串 “flaaap” 上,将产生两个捕获物: 3 和 5 。

    展开全文
  • lua字符串

    2015-06-08 17:20:00
    Lua字符串中的合法字符可以是任何的1字节数据,这包括了C语言中表示字符串结束的'\0'字符,也就是说Lua字符串在内部将以带长度的内存块的形式存储,存储的是二进制数据,解释器遇到'\0'字符并不会截断数据。...

           本文内容基于版本:Lua 5.3.0

    概述


           Lua字符串中的合法字符可以是任何的1字节数据,这包括了C语言中表示字符串结束的'\0'字符,也就是说Lua字符串在内部将以带长度的内存块的形式存储,存储的是二进制数据,解释器遇到'\0'字符并不会截断数据。同时在和C语言交互时,Lua又能保证为每个内部储存的字符串末尾添加'\0'字符以兼容C库函数,这使得Lua的字符串应用范围相当广泛。

           Lua字符串一旦被创建,就不可被改写。Lua的值对象若为字符串类型,则它将以引用方式存在。字符串对象属于需要被垃圾收集器管理的对象,也就是说一个字符串一旦没有被任何地方引用就可以回收它。

           Lua管理及操作字符串的方式和C语言不太相同,通过阅读其实现代码,可以加深对Lua字符串的理解,从而能更为高效的使用它。

    TString结构


    • TString结构的声明

           Lua字符串对应的C结构为TString,该类型定义在lobject.h中。

    // lobject.h
    /*
    ** Common Header for all collectable objects (in macro form, to be
    ** included in other objects)
    */
    #define CommonHeader    GCObject *next; lu_byte tt; lu_byte marked
    
    // lobject.h
    /*
    ** Header for string value; string bytes follow the end of this structure
    ** (aligned according to 'UTString'; see next).
    */
    typedef struct TString {
      CommonHeader;
      lu_byte extra;  /* reserved words for short strings; "has hash" for longs */
      unsigned int hash;
      size_t len;  /* number of characters in string */
      struct TString *hnext;  /* linked list for hash table */
    } TString;

           CommonHeader : 用于GC的信息。

           extra : 用于记录辅助信息。对于短字符串,该字段用来标记字符串是否为保留字,用于词法分析器中对保留字的快速判断;对于长字符串,该字段将用于惰性求哈希值的策略(第一次用到才进行哈希)。

           hash : 记录字符串的hash值,可以用来加快字符串的匹配和查找。

           len : 由于Lua并不以'\0'字符结尾来识别字符串的长度,因此需要一个len域来记录其长度。

           hnext : hash table中相同hash值的字符串将串成一个列表,hnext域为指向下一个列表节点的指针。

    TString存储结构图

           Lua字符串的数据内容部分并未分配独立的内存来存储,而是直接追加在TString结构的后面TString存储结构如下图:

          

             Lua字符串对象 = TString结构 + 实际字符串数据
            • TString结构 = GCObject *指针 + 字符串信息数据

    短字符串和长字符串


    • 长短字符串的划分

           字符串将以两种内部形式保存在lua_State中:短字符串及长字符串。Lua中每个基本内建类型都对应了一个宏定义,其中字符串类型对应于LUA_TSTRING宏定义。对于长短字符串,Lua在LUA_TSTRING宏上扩展了两个小类型LUA_TSHRSTRLUA_TLNGSTR,这两个类型在类型字节高四位存放0和1加以区别。这两个小类型为内部使用,不为外部API所见,因此对于最终用户来说,他们只见到LUA_TSTRING一种类型。

    // lua.h
    /*
    ** basic types
    */
    #define LUA_TNONE             (-1)
    
    #define LUA_TNIL              0
    #define LUA_TBOOLEAN          1
    #define LUA_TLIGHTUSERDATA    2
    #define LUA_TNUMBER           3
    #define LUA_TSTRING           4
    #define LUA_TTABLE            5
    #define LUA_TFUNCTION         6
    #define LUA_TUSERDATA         7
    #define LUA_TTHREAD           8
    
    #define LUA_NUMTAGS           9
    
    // lobject.h
    /* Variant tags for strings */
    #define LUA_TSHRSTR    (LUA_TSTRING | (0 << 4))  /* short strings */
    #define LUA_TLNGSTR    (LUA_TSTRING | (1 << 4))  /* long strings */

           长短字符串的界限是由定义在luaconf.h中的宏LUAI_MAXSHORTLEN来决定的,其默认设置为40(字节)。在Lua的设计中,元方法名和保留字必须是短字符串,所以短字符串长度不得短于最长的元方法__newindex和保留字function的长度,也就是说LUAI_MAXSHORTLEN最小不可以设置低于10(字节)。

    // luaconf.h
    /*
    @@ LUAI_MAXSHORTLEN is the maximum length for short strings, that is, ** strings that are internalized. (Cannot be smaller than reserved words ** or tags for metamethods, as these strings must be internalized; ** #("function") = 8, #("__newindex") = 10.) */ #define LUAI_MAXSHORTLEN 40

    • 字符串创建的函数调用图

          

           抛开短字符串的内部化过程来看,创建字符串最终调用的都是createstrobj函数,该函数创建一个可被GC管理的对象,并将字符串内容拷贝到其中。

    // lgc.c
    /*
    ** create a new collectable object (with given type and size) and link
    ** it to 'allgc' list.
    */
    GCObject *luaC_newobj (lua_State *L, int tt, size_t sz) {
      global_State *g = G(L);
      GCObject *o = cast(GCObject *, luaM_newobject(L, novariant(tt), sz));
      o->marked = luaC_white(g);
      o->tt = tt;
      // 放入GC对象列表
    o
    ->next = g->allgc; g->allgc = o; return o; } // lstring.c /* ** creates a new string object */ static TString *createstrobj (lua_State *L, const char *str, size_t l, int tag, unsigned int h) { TString *ts; GCObject *o; size_t totalsize; /* total size of TString object */ totalsize = sizelstring(l); o = luaC_newobj(L, tag, totalsize); ts = gco2ts(o); ts->len = l; ts->hash = h; ts->extra = 0; memcpy(getaddrstr(ts), str, l * sizeof(char)); getaddrstr(ts)[l] = '\0'; /* ending 0 */ return ts; }

    字符串的哈希算法


    • 哈希算法

           Lua中字符串的哈希算法可以在luaS_hash函数中查看到。对于比较长的字符串(32字节以上),为了加快哈希过程,计算字符串哈希值是跳跃进行的。跳跃的步长(step)是由LUAI_HASHLIMIT宏控制的。

    // lstring.c
    /*
    ** Lua will use at most ~(2^LUAI_HASHLIMIT) bytes from a string to
    ** compute its hash
    */
    #if !defined(LUAI_HASHLIMIT)
    #define LUAI_HASHLIMIT        5
    #endif
    
    // lstring.h
    LUAI_FUNC unsigned int luaS_hash (const char *str, size_t l, unsigned int seed);
    
    // lstring.c
    unsigned int luaS_hash (const char *str, size_t l, unsigned int seed) {
      unsigned int h = seed ^ cast(unsigned int, l);
      size_t l1;
      size_t step = (l >> LUAI_HASHLIMIT) + 1;
      for (l1 = l; l1 >= step; l1 -= step)
        h = h ^ ((h<<5) + (h>>2) + cast_byte(str[l1 - 1]));
      return h;
    }

           • str   : 待哈希的字符串;

           • l    : 待哈希的字符串长度(字符数);

           • seed  : 哈希算法随机种子;

    • 随机种子

           Hash DoS攻击:攻击者构造出上千万个拥有相同哈希值的不同字符串,用来数十倍地降低Lua从外部压入字符串到内部字符串表的效率。当Lua用于大量依赖字符串处理的服务(例如HTTP)的处理时,输入的字符串将不可控制, 很容易被人恶意利用 。

           为了防止Hash DoS攻击的发生,Lua一方面将长字符串独立出来,大文本的输入字符串将不再通过哈希内部化进入全局字符串表中;另一方面使用一个随机种子用于字符串哈希值的计算,使得攻击者无法轻易构造出拥有相同哈希值的不同字符串

           随机种子是在创建虚拟机的global_State(全局状态机)时构造并存储在global_State中的。随机种子也是使用luaS_hash函数生成,它利用内存地址随机性以及一个用户可配置的一个随机量(luai_makeseed宏)同时来决定。

           用户可以在luaconf.h中配置luai_makeseed来定义自己的随机方法,Lua默认是利用time函数获取系统当前时间来构造随机种子。luai_makeseed的默认行为有可能给调试带来一些困扰: 由于字符串hash值的不同,程序每次运行过程中的内部布局将有一些细微变化,不过字符串池使用的是开散列算法, 这个影响将非常小。如果用户希望让嵌入Lua的程序每次运行都严格一致,那么可以自定义luai_makeseed函数来实现。

    // lstate.c
    /*
    ** a macro to help the creation of a unique random seed when a state is
    ** created; the seed is used to randomize hashes.
    */
    #if !defined(luai_makeseed)
    #include <time.h>
    #define luai_makeseed()        cast(unsigned int, time(NULL))
    #endif
    
    // lstate.c
    /*
    ** Compute an initial seed as random as possible. Rely on Address Space
    ** Layout Randomization (if present) to increase randomness..
    */
    #define addbuff(b,p,e) \
      { size_t t = cast(size_t, e); \
        memcpy(buff + p, &t, sizeof(t)); p += sizeof(t); }
    
    static unsigned int makeseed (lua_State *L) {
      char buff[4 * sizeof(size_t)];
      unsigned int h = luai_makeseed();
      int p = 0;
      addbuff(buff, p, L);  /* heap variable */
      addbuff(buff, p, &h);  /* local variable */
      addbuff(buff, p, luaO_nilobject);  /* global variable */
      addbuff(buff, p, &lua_newstate);  /* public function */
      lua_assert(p == sizeof(buff));
      return luaS_hash(buff, p, h);
    }
    
    // lstate.c
    LUA_API lua_State *lua_newstate (lua_Alloc f, void *ud) {
      int i;
      lua_State *L;
      global_State *g;
      LG *l = cast(LG *, (*f)(ud, NULL, LUA_TTHREAD, sizeof(LG)));
      if (l == NULL) return NULL;
      L = &l->l.l;
      g = &l->g;
      ......
      g->seed = makeseed(L);
      ......
      return L;
    }

     短字符串的内部化


           Lua中所有的短字符串均被存放在全局状态表(global_State)的strt域中,strtstringtable的简写,它是一个哈希表。

           相同的短字符串在同一个lua_State中将只存在唯一一份实例,这被称为字符串的内部化。合并相同的字符串可以大量减少内存占用,缩短比较字符串的时间。由于相同的字符串只需要保存一份在内存中,当用这个字符串做键匹配时,比较字符串只需要比较地址是否相同就够了,而不必逐字节比较。下面将着重对stringtable进行分析。

    • stringtable结构类型

    // lstate.h
    typedef struct stringtable {
      TString **hash;
      int nuse;  /* number of elements */
      int size;
    } stringtable;

           • hash  : 字符串开散列算法哈希表,hash是一维数组指针,其中数组元素类型为TString *(指向TString类型对象指针),它并不是一个二维数组(数组元素类型为TString)指针;

           • nuse  : 字符串表当前字符串数量;

           • size : 字符串表最大字符串数量;

    • stringtable存储结构图

    • 短字符串内部化(散列过程描述)

           首先求得传入短字符串的哈希值,然后将该哈希值与stringtable大小取模,从而得到该字符串在stringtable中存放位置(相同哈希值的字符串链表);接着从该字符串链表的第一个位置开始,将链表中每个字符串与传入字符串比较字符串内容,如果相等说明传入字符串已经在表中使用;如果不相等说明不是同一个字符串,继续往后查找。如果字符串链表中都没有查找到,那么需要创建一个新的字符串。创建过程中,碰到哈希值相同的字符串,简单地串在同一个哈希位的链表上即可。简单地用一句话描述开散列的哈希过程:传入字符串被放入字符串表的时候,先检查一下表中有没有相同的字符串,如果有则复用已有的字符串,如果没有则创建一个新的字符串

           由于Lua的垃圾回收过程是分步完成的, 而向stringtable添加新字符串在垃圾回收的任何步骤之间都可能发生,所以这个过程中需要检查表中的字符串是否已经死掉(标记为可垃圾回收):有可能在标记完字符串死掉后, 在下个步骤中又产生了相同的字符串导致这个字符串复活。

    // lstring.c
    /*
    ** checks whether short string exists and reuses it or creates a new one */ static TString *internshrstr (lua_State *L, const char *str, size_t l) { TString *ts; global_State *g = G(L); // 计算传入字符串哈希值
    unsigned
    int h = luaS_hash(str, l, g->seed);   // 找到目标位置字符串链表
    TString
    **list = &g->strt.hash[lmod(h, g->strt.size)];   // 在字符串链表搜索传入字符串
    for (ts = *list; ts != NULL; ts = ts->hnext) { if (l == ts->len && (memcmp(str, getstr(ts), l * sizeof(char)) == 0)) { /* found! */ if (isdead(g, ts)) /* dead (but not collected yet)? */ changewhite(ts); /* resurrect it */ return ts; } } if (g->strt.nuse >= g->strt.size && g->strt.size <= MAX_INT/2) { luaS_resize(L, g->strt.size * 2); list = &g->strt.hash[lmod(h, g->strt.size)]; /* recompute with new size */ } // 没有找到创建新的字符串
    ts
    = createstrobj(L, str, l, LUA_TSHRSTR, h); ts->hnext = *list; *list = ts; g->strt.nuse++; return ts; }

     • stringtable的扩大及字符串的重新哈希

           当stringtable中的字符串数量(stringtable.muse域)超过预定容量(stringtable.size域)时,说明stringtable太拥挤,许多字符串可能都哈希到同一个维度中去,这将会降低stringtable的遍历效率。这个时候需要调用luaS_resize方法将stringtable的哈希链表数组扩大,重新排列所有字符串的位置。

    // lstring.h
    LUAI_FUNC void luaS_resize (lua_State *L, int newsize);
    
    // lstring.c
    /*
    ** resizes the string table
    */
    void luaS_resize (lua_State *L, int newsize) {
      int i;
      // 取得全局stringtable
    stringtable
    *tb = &G(L)->strt; if (newsize > tb->size) { /* grow table if needed */   // 如果stringtable的新容量大于旧容量,重新分配
    luaM_reallocvector
    (L, tb
    ->hash, tb->size, newsize, TString *); for (i = tb->size; i < newsize; i++) tb->hash[i] = NULL; } // 根据新容量进行重新哈希
    for (i = 0; i < tb->size; i++) { /* rehash */ TString *p = tb->hash[i]; tb->hash[i] = NULL; // 将每个哈希链表中的元素哈希到新的位置(头插法)
    while (p) { /* for each node in the list */ TString *hnext = p->hnext; /* save next */ unsigned int h = lmod(p->hash, newsize); /* new position */ p->hnext = tb->hash[h]; /* chain it */ tb->hash[h] = p; p = hnext; } } // 如果stringtable的新容量小于旧容量,那么要减小表的长度
    if (newsize < tb->size) { /* shrink table if needed */ /* vanishing slice should be empty */ lua_assert(tb->hash[newsize] == NULL && tb->hash[tb->size - 1] == NULL); luaM_reallocvector(L, tb->hash, tb->size, newsize, TString *); } tb->size = newsize; }

            stringtable初始大小由宏MINSTRTABSIZE控制,默认是64,用户可以在luaconf.h重新定义MINSTRTABSIZE宏来改变默认大小。在为stringtable初次分配空间的时候,调用的也是luaS_resize方法,将stringtable空间由0调整到MINSTRTABSIZE的大小。

    // llimits.h
    /* minimum size for the string table (must be power of 2) */
    #if !defined(MINSTRTABSIZE)
    #define MINSTRTABSIZE    64    /* minimum size for "predefined" strings */
    #endif
    
    // lstate.c
    /*
    ** open parts of the state that may cause memory-allocation errors.
    ** ('g->version' != NULL flags that the state was completely build)
    */
    static void f_luaopen (lua_State *L, void *ud) {
      global_State *g = G(L);
      UNUSED(ud);
      stack_init(L, L);  /* init stack */
      init_registry(L, g);
      luaS_resize(L, MINSTRTABSIZE);  /* initial size of string table */
      ...
    }
    
    // lstate.c
    LUA_API lua_State *lua_newstate (lua_Alloc f, void *ud) {
      int i;
      lua_State *L;
      global_State *g;
      LG *l = cast(LG *, (*f)(ud, NULL, LUA_TTHREAD, sizeof(LG)));
      if (l == NULL) return NULL;
      L = &l->l.l;
      g = &l->g;
      ...
      g->strt.size = g->strt.nuse = 0;
      g->strt.hash = NULL;

    ...
    if (luaD_rawrunprotected(L, f_luaopen, NULL) != LUA_OK) { /* memory allocation error: free partial state */ close_state(L); L = NULL; } return L; }

            stringtable在字符串内部化的过程中扩大的策略和STL中的vector比较类似:当空间不足时,大小扩大为当前空间的两倍大小。

    // lstring.c
    /*
    ** checks whether short string exists and reuses it or creates a new one
    */
    static TString *internshrstr (lua_State *L, const char *str, size_t l) {
      TString *ts;
      global_State *g = G(L);
      unsigned int h = luaS_hash(str, l, g->seed);
      TString **list = &g->strt.hash[lmod(h, g->strt.size)];
      ...
    if (g->strt.nuse >= g->strt.size && g->strt.size <= MAX_INT/2) { luaS_resize(L, g->strt.size * 2); list = &g->strt.hash[lmod(h, g->strt.size)]; /* recompute with new size */ } ...
    return ts; }

    字符串的比较操作


           由于长短字符串实现的不同,在比较两个字符串是否相同时,需要区分长短字符串。在进行字符串比较操作时,首先子类型不同(长短字符串)的字符串自然不是相同的字符串,然后如果子类型相同,那么根据长短字符串使用不同策略进行比较。

    // lvm.c
    /*
    ** Main operation for equality of Lua values; return 't1 == t2'. 
    ** L == NULL means raw equality (no metamethods)
    */
    int luaV_equalobj (lua_State *L, const TValue *t1, const TValue *t2) {
      const TValue *tm;
      // 如果类型(含子类型)不同
    if (ttype(t1) != ttype(t2)) { /* not the same variant? */ // 如果大类型不同或大类型不是数字类型
    if (ttnov(t1) != ttnov(t2) || ttnov(t1) != LUA_TNUMBER) return 0; /* only numbers can be equal with different variants */ else { /* two numbers with different variants */ lua_Number n1, n2; /* compare them as floats */ lua_assert(ttisnumber(t1) && ttisnumber(t2)); cast_void(tofloat(t1, &n1)); cast_void(tofloat(t2, &n2)); return luai_numeq(n1, n2); } } /* values have same type and same variant */ switch (ttype(t1)) { case LUA_TNIL: return 1; ...
    // 根据子类型不同,用不同字符串比较策略进行比较
    case LUA_TSHRSTR: return eqshrstr(tsvalue(t1), tsvalue(t2)); case LUA_TLNGSTR: return luaS_eqlngstr(tsvalue(t1), tsvalue(t2)); ...
    default: return gcvalue(t1) == gcvalue(t2); } if (tm == NULL) /* no TM? */ return 0; /* objects are different */ luaT_callTM(L, tm, t1, t2, L->top, 1); /* call TM */ return !l_isfalse(L->top); }

     • 短字符串的比较策略

           短字符串由于经过内部化操作,所以不必进行字符串内容比较,仅需比较对象地址是否相等即可。Lua使用一个宏eqshrstr来高效地实现这个操作:

    // lstring.h
    /*
    ** equality for short strings, which are always internalized
    */
    #define eqshrstr(a,b)    check_exp((a)->tt == LUA_TSHRSTR, (a) == (b))

     • 长字符串的比较策略

           首先对象地址相等的两个长字符串属于同一个实例,因此它们是相等的;然后对象地址不相等的情况下,当字符串长度不同时, 自然是不同的字符串 ,而长度相同 时, 则需要进行逐字节比较。

    // lstring.h
    LUAI_FUNC int luaS_eqlngstr (TString *a, TString *b);
    
    // lstring.c
    /*
    ** equality for long strings
    */
    int luaS_eqlngstr (TString *a, TString *b) {
      size_t len = a->len;
      lua_assert(a->tt == LUA_TLNGSTR && b->tt == LUA_TLNGSTR);
      return (a == b) ||  /* same instance or... */
        ((len == b->len) &&  /* equal length and ... */
         (memcmp(getstr(a), getstr(b), len) == 0));  /* equal contents */
    }

     

    转载于:https://www.cnblogs.com/heartchord/p/4561308.html

    展开全文
  • LUA字符串

    2021-04-18 20:21:02
    lua语言中字符串是一串字节组成的序列 trait Immutable value lua语言中,字符串是不可变值。 Length operator # 操作符获取长度 > a = "hello" > print(#a) 5 cat … 进行字符串连接 > "Hello...

    字符串

    lua语言中字符串是一串字节组成的序列

    trait

    • Immutable value

      lua语言中,字符串是不可变值。

    • Length operator

      # 操作符获取长度

    > a = "hello"
    > print(#a)
    5
    
    • cat

      … 进行字符串连接

    > "Hello" .. " World"
    Hello World
    
    • 多行字符串
    > page = [[
    >> <html>
    >>   <body>hello</body>
    >> </html>
    >> ]]
    > print(page)
    <html>
      <body>hello</body>
    </html>
    
    • type coercion
    > tonumber("  -3 ")
    -3
    > tonumber("1001", 2)
    9
    > tostring(10) == "10"
    true
    > tostring(10) == 10
    false
    > "result is " .. 3
    result is 3
    

    string 标准库

    详细参考🖐

    > string.rep("abc", 3)
    abcabcabc
    > string.reverse("A Long line!")
    !enil gnoL A
    > string.lower("A Long Line!")
    a long line!
    > string.upper("A Long Line!")
    A LONG LINE!
    > string.sub("hello!", 1, -3)
    hell
    > string.char(97)
    a
    > string.byte("abc", 1,-1)
    97	98	99
    > string.format("pi = %.4f", math.pi)
    pi = 3.1416
    > string.find("hello world", "wor")
    7	9
    > string.gsub("hello world", "l", ".")
    he..o wor.d	3
    
    展开全文
  • lua字符串操作

    2019-02-22 17:34:18
    文章目录lua字符串操作阿拉伯数字转中文字符串中提取中文数字英文,删除特殊字符 lua字符串操作 阿拉伯数字转中文 function GameFunCtion.NumberToString(szNum) ---阿拉伯数字转中文大写 local szChMoney = "...

    lua字符串操作

    阿拉伯数字转中文

    function GameFunCtion.NumberToString(szNum)
        ---阿拉伯数字转中文大写
        local szChMoney = ""
        local iLen = 0
        local iNum = 0
        local iAddZero = 0
        local hzUnit = {"", "十", "百", "千", "万", "十", "百", "千", "亿","十", "百", "千", "万", "十", "百", "千"}
        local hzNum = {"零", "一", "二", "三", "四", "五", "六", "七", "八", "九"}
        if nil == tonumber(szNum) then
            return tostring(szNum)
        end
        iLen =string.len(szNum)
        if iLen > 10 or iLen == 0 or tonumber(szNum) < 0 then
            return tostring(szNum)
        end
        for i = 1, iLen  do
            iNum = string.sub(szNum,i,i)
            if iNum == 0 and i ~= iLen then
                iAddZero = iAddZero + 1
            else
                if iAddZero > 0 then
                    szChMoney = szChMoney..hzNum[1]
                end
                szChMoney = szChMoney..hzNum[iNum + 1] --//转换为相应的数字
                iAddZero = 0
            end
            if (iAddZero < 4) and (0 == (iLen - i) % 4 or 0 ~= tonumber(iNum)) then
                szChMoney = szChMoney..hzUnit[iLen-i+1]
            end
        end
        local function removeZero(num)
            --去掉末尾多余的 零
            num = tostring(num)
            local szLen = string.len(num)
            local zero_num = 0
            for i = szLen, 1, -3 do
                szNum = string.sub(num,i-2,i)
                if szNum == hzNum[1] then
                    zero_num = zero_num + 1
                else
                    break
                end
            end
            num = string.sub(num, 1,szLen - zero_num * 3)
            szNum = string.sub(num, 1,6)
            --- 开头的 "一十" 转成 "十" , 贴近人的读法
            if szNum == hzNum[2]..hzUnit[2] then
                num = string.sub(num, 4, string.len(num))
            end
            return num
        end
        return removeZero(szChMoney)
    end
    

    字符串中提取中文数字英文,删除特殊字符

    function GameFunCtion.filter_spec_chars(s)--保留中文数字英文,删除特殊字符
        local ss = {}
        local k = 1
        while true do
            if k > #s then break end
            local c = string.byte(s,k)
            if not c then break end
            if c<192 then
                if (c>=48 and c<=57) or (c>= 65 and c<=90) or (c>=97 and c<=122) then
                    table.insert(ss, string.char(c))
                end
                k = k + 1
            elseif c<224 then
                k = k + 2
            elseif c<240 then
                if c>=228 and c<=233 then
                    local c1 = string.byte(s,k+1)
                    local c2 = string.byte(s,k+2)
                    if c1 and c2 then
                        local a1,a2,a3,a4 = 128,191,128,191
                        if c == 228 then a1 = 184
                        elseif c == 233 then a2,a4 = 190,c1 ~= 190 and 191 or 165
                        end
                        if c1>=a1 and c1<=a2 and c2>=a3 and c2<=a4 then
                            table.insert(ss, string.char(c,c1,c2))
                        end
                    end
                end
                k = k + 3
            elseif c<248 then
                k = k + 4
            elseif c<252 then
                k = k + 5
            elseif c<254 then
                k = k + 6
            end
        end
        return table.concat(ss)
    end
    
    展开全文
  • (Lua 笔记):Lua 字符串

    2020-09-09 16:43:57
    Lua 字符串 字符串操作 字符串截取 字符串大小写转换 字符串查找与反转 字符串格式化 字符与整数相互转换 Lua 字符串 Lua 语言中字符串可以使用以下三种方式来表示: 单引号间的一串字符。 双引号间的一...
  • C#类装lua字符串

    2017-03-07 09:49:16
    将c#类转换成lua字符串数据
  • Lua字符串操作

    2020-03-11 14:43:22
    Lua字符串操作 --Lua字符串 --[[ 1、stirng.upper(str) 转化为大写 2、string.lower(str) 转化为小写 3、string.gsub(mainStr,findStr,replaceStr,num) 替换字符 print(string.gsub("aaaa","a","z",3)) 4、...
  • lua 字符串转16进制

    千次阅读 2020-09-22 17:52:53
    lua字符串16进制
  • Lua字符串模板 概述 Lust是一个基于Terrence Parr的Lua模板系统。 Lust旨在使复杂字符串合成问题的简单表达成为可能。 它特别适合从分层数据结构生成字符串。 Lust本身包含用于编写模板的语言和用于将模板应用于数据...
  • Lua 字符串的使用

    2017-10-30 19:35:33
    Lua 字符串 字符串或串(String)是由数字、字母、下划线组成的一串字符。Lua 语言中字符串可以使用以下三种方式来表示: 单引号间的一串字符。 双引号间的一串字符。 [[和]]间的一串字符。 字符串操作 ...
  • 一、Lua字符串介绍 Lua语言中的字符串是一串字节组成的序列。在Lua语言中,字符使用8个比特位来存储 Lua语言中的字符串可以存储包括空字符在内的所有数值代码,这意味着我们可以在字符串中存储任意的二进制数据 ...
  • Lua字符串操作基础

    2020-09-27 17:18:55
    下面我简单总结一下lua字符串的操作 之后就是一些方法的总结 小写转大写 string.upper(a) 大写转小写:string.lower(a) 字符串中替换:string.gsub(mainString,findString,replaceString,num) string.find (str, ...
  • Lua 字符串常用操作

    2019-05-05 19:10:40
    Lua 字符串三种声明 字符串或串(String)是由数字、字母、下划线组成的一串字符。 Lua 语言中字符串可以使用以下三种方式来表示: 单引号间的一串字符。(单引号里面用双引号不用添加转义符) 双引号间的一串字符。 [...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 3,908
精华内容 1,563
关键字:

lua字符串