OpenRefine开源数据清洗软件的GREL语言
OpenRefine开源数据清洗软件的GREL语言 (General Refine Expression Language)
·
GREL (General Refine Expression Language)语言
----------------------------------------------------------------
说明:
e: Expression, 表达式
v: Variable, 变量
p: Pattern, 模式,regex pattern正则表达式
s: String, 字符串型数据
b: Boolean, 布尔型数据
n: Number, 数值型数据
d: Date, 日期型数据
a: Array, 列表型数据
o: Object, 任意类型数据
null: 空类型数据
error: 错误类型
函数或控制语句关键词大小写敏感,以驼峰命名法命名
若在函数中使用正则表达式类型,则使用两个斜杠括起来,例如: /^\d+/
字符串变量采用英文的双引号("")或单引号('')均可
常量: true,false,PI
----------------------------------------------------------------
#Basic(基础)#
GREL中提供了一些简单的编程语言,可以实现对表达式进行一定的操作处理,但GREL语言基本都是单句性质的简单语法,更复杂的操作可以通过嵌套,或者利用Jython的方式来实现。GREL类似于JavaScript语言,基于变量类型来实现字符串操作或数学运算。
例如: value+2.239,当value是一个数时进行的是数学运算操作,当value是文本时进行的是字符串拼接操作。
#Syntax(语法)#
1.圆括号和点符号
GREL中的函数可以写为函数体或点符号两种等效的形式: functionName(arg0, arg1, ...) 或 arg0.functionName(arg1, ...)。
--1: length(trim(value)) 等效于value.trim().length() ,substring(value, 7, 10) 等效于 value.substring(7, 10)。
当需要进行一系列操作时,函数体形式需要一层层的嵌套,而点符号形式则更为简单明了。
GREL变量的属性也可以用点符号来表示,例如某列数据的列名为FirstName,则下面两种等价:
--2: FirstName.cells 等效于 cells["FirstName"]
2.中括号
GREL中的中括号可以用来取子字符串或子列表。
--1: value[1,3]表示取value从第1个(包含)到第3个(不含)的子序列,注意序号是从0起算。
--2: "internationalization"[1,-2] ---> “nternationalizati”,负索引表示从最后倒数。
--3: row.columnNames[5] ---> 第5列的名字
--4: "internationalization".partition("nation")[2] ---> "alization",以nation将前面的分开共3段,取第3段,与[-1]等效果。
GREL中也提供有进行分支控制和循环控制的语句
#Controls(控制语句)#
if(e, eTrue, eFalse) #该语句可以实现简单的分支判断功能。
功能: e是表达式,如果表达式计算为true,则结果为eTrue,否则结果为eFalse。
--1: if("internationalization".length() > 10, "big string", "small string") ---> "big string"
--2: if(mod(37, 2) == 0, "even", "odd") ---> “odd”
--3: if(value==null,"new",value) #将所有的null值替换为某个指定的字符串
GREL中没有提供switch语句,但可以通过将控制语句嵌套起来达到类似于其他语言中switch case的效果:
--1;if(value == 'Place', 'http://www.example.com/Location', if(value == 'Person', 'http://www.example.com/Agent',
if(value == 'Book', 'http://www.example.com/Publication',null)))
with(e1, v, e2) #该语句可以实现简单的串联计算功能。
功能: 计算表达式e1,将计算得到的值绑定到v,然后计算表达式e2并返回计算结果。
--1: with("european union".split(" "), a, a.length()) ---> 2
--2: with("european union".split(" "), a, forEach(a, v, v.length())) ---> [8, 5]
--3: with("european union".split(" "), a, forEach(a, v, v.length()).sum() / a.length()) ---> 6.5
filter(e1, v, etest) #该语句可以实现简单的列表过滤功能。
功能: 计算表达式e1得到一个列表,然后将列表中的每个元素绑定到v,计算条件表达式etest(true或false),最后返回etest为true对应的v值。
--1: filter([ 3, 4, 8, 7, 9 ], v, mod(v, 2) == 1) ---> [ 3, 7, 9 ]
forEach(e1, v, e2) #该语句可以实现简单的列表循环功能。
功能: 计算表达式e1得到一个列表,然后将列表中的每个元素绑定到v,计算表达式e2,将结果保存到结果列表中并返回。
--1: forEach([ 3, 4, 8, 7, 9 ], v, mod(v, 2)) ---> [ 1, 0, 0, 1, 1 ]
forEachIndex(e1, i, v, e2) #该语句可以实现简单的列表循环功能(带索引)。
功能: 计算表达式e1得到一个列表,然后将列表中每个元素的索引绑定到i、值绑定到v,计算表达式e2,将结果保存到结果结果列表中并返回。
--1: forEachIndex([ "anne", "ben", "cindy" ], i, v, (i + 1) + ". " + v).join(", ") ---> 1. anne, 2. ben, 3. cindy
forRange(nfrom, nto, nstep, v, e) #该语句可以实现简单的循环功能。
功能: 从nfrom起始对v以nstep的步长逐步累加,直至≥nto,计算表达式e,将结果保存到结果结果列表中并返回。
forNonBlank(e, v, eNonBlank, eBlank) #该语句可以分支判断后计算的功能,注意虽然带有for但其不是循环语句。
功能: 计算表达式e,如果非空,则将其值绑定到v并计算表达式eNonBlank并返回;否则,如果表达式e计算为空,则计算表达式eBlank并返回。
isBlank(e), isNonBlank(e), isNull(e), isNotNull(e), isNumeric(e), isError(e)
功能: 这些语句可以实现对表达式e的值进行判断的功能,注意不是函数,因此不能像函数那样使用点符号进行链式书写。
--1: isBlank("abc") ---> false
--2: isNonBlank("abc") ---> true
--3: isNull("abc") ---> false
--4: isNotNull("abc") ---> true
--5: isNumeric(2) ---> true
--6: isError(1) ---> false
--7: isError("abc") ---> false
--8: isError(1 / 0) ---> true
#Bool Functions(布尔函数)#
and(b1, b2, ...) (b1).and(b2)...
or(b1, b2, ...) (b1).or(b2)...
not(b)
xor(b1, b2, ...) (b1).xor(b2)...
#String Functions(字符串函数)#
length(s) #返回字符串长度
toString(o, format(optional))
--1: PI.toString("%.3f") ---> 3.141
--2: [2024-10-15T00:00:00Z].toString("MMM-dd-yyyy") ---> "Oct-15-2024"
toLowercase(s)
toUppercase(s)
toTitlecase(s) #首字母大小
trim(s)/strip(s) #删除前后空格
chomp(s, sep) #删除指定字符串(如果有的话)
--1: "barely".chomp("ly") ---> bare
--2: "bare".chomp("ly") ---> bare
startsWith(s, sub) #s是否以sub起始
--1: "food".startsWith("foo") ---> true
--2: "food".startsWith("bar") ---> false
endsWith(s, sub) #s是否以sub结尾
--1: "food".endsWith("ood") ---> true
--2: "food".endsWith("bar") ---> false
contains(s, sub or p) #s是否包含sub或正则表达式p表示的模式
--1: "food".contains("oo") ---> true
--2: "food".contains("ee") ---> false
--3: "rose is a rose".contains(/\s+/) ---> true
substring(s, nfrom, nto(optional)) #从s中截取从nfrom到nto(不含)的字符串,默认省略nto表示到末尾,负数索引表示倒数
--1: "profound".substring(3) ---> "found"
--2: "profound".substring(2, 4) ---> "of"
--3: "profound".substring(0, -1) ---> "profoun"
slice(s, nfrom, nto(optional)) #对于字符串操作等同于substring(),但还可以用于列表类型数据array
get(s, nfrom, nto(optional)) #对于字符串操作等同于substring(),但还可以用于列表类型数据array和命名域类型数据named fields
indexOf(s, sub) #返回sub在s中第一次出现的位置,没有的话返回-1
--1: "internationalization".indexOf("nation") ---> 5
--2: "internationalization".indexOf("world") ---> -1
lastIndexOf(s, sub) #返回sub在s中最后一次出现的位置,没有的话返回-1
--1: "parallel".lastIndexOf("a") ---> 3
replace(s, sfind or pfind, sreplace) #将s中的sfind或pfind全部替换为的sreplace,支持正则表达式
--1: "The cow jumps over the moon and moos".replace("oo", "ee") ---> "The cow jumps over the meen and mees"
--2: "The cow jumps over the moon and moos".replace(/\s+/, "_") ---> "The_cow_jumps_over_the_moon_and_moos"
replaceChars(s, sfind, sreplace) #将s中的sfind字符替换为对应的sreplace中的字符
--1: "Téxt thát was optícálly recógnízéd".replaceChars("áéíóú", "aeiou") ---> "Text that was optically recognized"
find(s, sub or p) #输出s中的sub或符合p模式的列表,支持正则表达式。注意输出为列表,若要获得列表元素,需要使用方括号索引。
--1: "abeadsabmoloei".find(/[aeio]+/) ---> [ "a", "ea", "a", "o", "oei" ]
match(s, p) #将s作为一个整体进行模式匹配,输出s中符合p模式的列表,注意正则表达式中的一对圆括号表示一个模式
--1: "230.22398, 12.3480".match(/(.*)(\d\d\d\d)/) ---> ["230.22398, 12.","3480"]
--2: "230.22398, 12.3480".match(/.*(\d\d\d\d)/) ---> ["3480"]
--3: "230.22398, 12.3480".match(/.*(\d\d\d\d)/)[0] ---> "3480"
--4: "230.22398, 12.3480".match(/.*(\d\d\d\d)/)[0].type() ---> string
--5: "230.22398, 12.3480".match(/.*(\d\d\d\d)/)[0].toNumber().type() ---> number
--6: "hello 123456 goodbye".match(/\d{6}/) ---> null
--7: "hello 123456 goodbye".match(/.*\d{6}.*/) ---> [ ]
--8: "hello 123456 goodbye".match(/.*(\d{6}).*/) ---> [ "123456" ]
--9: "hello 123456 goodbye".match(/(.*)(\d{6})(.*)/) ---> [ "hello ", "123456", " goodbye" ]
#match就像拿个整体的模板一样去套s,然后将符合模板透出来的输出;而find则像拿着小模板在s上滑动,然后将所有符合模板透出来的输出。
toNumber(s) #转换为数值
split(s, s or p sep, b preserveTokens (optional)) #以指定分割标记sep对字符串s进行分割,支持正则表达式,输出为列表类型
--1: "fire,, water, earth, air".split(",",true) ---> [ "fire", "", " water", " earth", " air" ] #保留空分割(null)
--1: "fire,, water, earth, air".split(",",false) ---> [ "fire", " water", " earth", " air" ] #不保留空分割
splitByLengths(s, n1, n2, ...) #以指定分割长度n1,n2,...对字符串s进行分割,支持正则表达式,输出为列表类型
--1: "internationalization".splitByLengths(5, 6, 3) ---> [ "inter", "nation", "ali" ]
smartSplit(s, s or p sep (optional)) #如果不指定sep,默认以换行符分割
splitByCharType(s) #以s中各字符类型改变进行分割
--1: "HenryCTaylor".splitByCharType() ---> [ "H", "enry", "CT", "aylor" ]
--2: "BE1A3E".splitByCharType() ---> [ "BE", "1", "A", "3", "E" ]
partition(s, s or p fragment, b omitFragment (optional)) #以指定片段fragment第一次出现的位置将s分为三段
--1: "internationalization".partition("nation") ---> [ "inter", "nation", "alization" ]
--2: "internationalization".partition("na5tion", true) ---> [ "internationalization", "" ,""]#若s不包含指定片段...
--3: "internationalization".partition("nation", true) ---> [ "inter", "alization" ]#若忽略指定片段,则分为前后两段
--4: "abcdefgh".partition(/c.e/) ---> [“abc”, "cde", defgh” ]#支持正则表达式
rpartition(s, s or p fragment, b omitFragment (optional)) #以指定片段fragment最后一次出现的位置将s分为三段
--1: "parallel".rpartition("a") ---> [ "par", "a", "llel" ]
#Array functions(列表函数)#
length(a) #返回列表纵元素数目
slice(a, n from, n to (optional)) #返回指定区间的子列表
1--: [0, 1, 2, 3, 4].slice(1, 3) ---> [ 1, 2 ]
2--: [ 0, 1, 2, 3, 4].slice(2) ---> [ 2, 3, 4 ]
get(a, n from, n to (optional)) #返回指定区间的子列表或子字符串
1--: value.get(2,999) #若n to省略则返回一个子字符串,若需返回子列表,则可以指定一个很大的n to
2--: with(value,a,a.get(1,a.length()))
inArray(a, s) #判断元素s是否在列表a中
1--: [ 1, 2, 3, 4 ].inArray("3") ---> true
reverse(a) #列表元素逆序排列
1--: [ 0, 1, 2, 3].reverse() ---> [ 3, 2, 1, 0 ]
sort(a) #返回排序后的列表,大小写敏感,大写在前,小写在后
1--: [ "al", "Joe", "Bob", "jim" ].sort() ---> [ "Bob", "Joe", "al", "jim" ]
sum(a) #列表元素求和
1--: [ 2, 1, 0, 3 ].sum() ---> 6
join(a, sep) #返回列表元素连接符连接后的字符串
1--: [ "and", "or", "not" ].join("/") ---> "and/or/not"
uniques(a) #返回列表元素去重后的列表,大小写敏感
1--: [ "al", "Joe", "Bob", "Joe", "Al", "Bob" ].uniques() ---> [ "Joe", "al", "Al", "Bob" ]
#Other functions(其他函数)#
type(o) #返回变量类型
facetCount(choiceValue, s facetExpression, s columnName) #返回指定列中元素计数数目
--1: value.facetCount("value", "Gift") ---> 以Gift列为基础新生成一个Count列,代表Gift列中元素的数目
--2: (value.fingerprint()).facetCount(value.fingerprint(),"Gift")
hasField(o, s name) #数据o是否包含s属性
--1: cell.recon.hasField("match")
--2: cell.hasField("recon.match")
coalesce(o1, o2, o3, ...) #返回第一个非空数据
--1: coalesce(value, "") ---> value #如果value不为空
cross(cell, s projectName (optional), s columnName (optional)) #从projectName项目中的columnName列拉取单元格数据
--1: cell.cross("People","Name").cells["Address"].value[0]
#Date functions(日期函数)#
now() #返回当前时间
1--: now() ---> [date 2022-05-15T17:56:49Z]
2--: now().type() ---> date
toDate(o, b monthFirst, s format1, s format2, ...) #返回指定格式的日期类型数据,monthFirst设置是否月份在前
1--: "11/09".toDate('MM/yy','MMM-yy').toString('yyyy-MM') ---> "2009-11"
2--: "1/4/2012 13:30:00".toDate('d/M/y HH;mm;ss')
日期格式如下所示:
Letter Date or Time Component Presentation Examples
G Era designator Text AD
y Year Year 1996; 96
Y Week year Year 2009; 09
M Month in year Month July; Jul; 07
w Week in year Number 27
W Week in month Number 2
D Day in year Number 189
d Day in month Number 10
F Day of week in month Number 2
E Day name in week Text Tuesday; Tue
u Day number of week
(1=Monday, 7=Sunday) Number 1
a AM/PM marker Text PM
H Hour in day (0-23) Number 0
k Hour in day (1-24) Number 24
K Hour in AM/PM (0-11) Number 0
h Hour in AM/PM (1-12) Number 12
m Minute in hour Number 30
s Second in minute Number 55
S Millisecond Number 978
n Nanosecond Number 789000
z Time zone General time
zone Pacific Standard Time; PST; GMT-08:00
Z Time zone RFC822 time zone -0800
X Time zone ISO 8601 time zone -08; -0800; -08:00
diff(d1, d2, s timeUnit) #以指定的时间段为单位返回两个日期的时间差
--1: diff(("Nov-11".toDate('MMM-yy')), ("Nov-09".toDate('MMM-yy')), "weeks") ---> 104
inc(d, n, s timeUnit) #以指定的时间段为单位对日期增加指定的时间
--1: value.inc(-2,"month") ---> 106
datePart(d, s timeUnit) #获取时间的一部分
--1: value.datePart("year") ---> 2015
--2: value.datePart("month") ---> 2
--3: value.datePart("week") ---> 3 #当月的第几周
--4: value.datePart("hour") ---> 5
--5: value.datePart("minute") ---> 30
--5: value.datePart("sec") ---> 04
--6: value.datePart("ms") ---> 789
--7: value.datePart("time") ---> 1394775004000 #Unix Epoch
#Math functions(数学函数)#
abs(n)/sum(a) #绝对值/列表求和
sin(n)/asin(n)/sinh(n)/cos(n)/acos(n)/cosh(n)/tan(n)/atan(n)/atan2(n1,n2)/tanh(n) #三角函数
randomNumber(n lowerBound, n upperBound) #[lowerBound, upperBound)范围随机整数
ceil(n)/floor(n)/round(n)/even(n)/odd(n) #向上取整/向下取整/四舍五入/最近偶数/最近奇数
combin(n1,n2) #组合
fact(n)/fact(n1,n2) #阶乘/n2到n1的阶乘
degrees(n)/radians(n) #弧度转为度/度转为弧度
pow(n1, n2)/exp(n) #指数/e指数
ln(n)/log(n) #自然指数/以10为底的指数
gcd(n1, n2) #最大公约数
lcm(n1, n2) #最小公倍数
min(n1, n2)/max(n1, n2)/mod(n1, n2)/quotient(n1, n2) #最小数/最大数/余数/商
multinomial(n1, n2 …(optional)) #多项式分布: (n1+n2+...)!/(n1!n2!...)
#Encoding and hashing(编码函数和哈希函数)#
unicode(s) #得到s中各个字符对应unicode编码组成的列表
--1: "Bernice Rubens".unicode() ---> [ 66, 101, 114, 110, 105, 99, 101, 32, 82, 117, 98, 101, 110, 115 ]
unicodeType(s) #得到s中各个字符对应unicode类型组成的列表
--1: "Bernice Rubens".unicodeType() ---> [ "uppercase letter", "lowercase letter", "lowercase letter", "lowercase letter", "lowercase letter", "lowercase letter", "lowercase letter", "space separator", "uppercase letter", "lowercase letter", "lowercase letter", "lowercase letter", "lowercase letter", "lowercase letter" ]
md5(o) #计算指定任意类型变量o的md5哈希值
--1: "internationalization".md5() ---> 2c55a1626e31b4e373ceedaa9adc12a3
sha1(o) #计算指定任意类型变量o的SHA-1哈希值
--1: "internationalization".sha1() ---> cd05286ee0ff8a830dbdc0c24f1cb68b83b0ef36
diff(s1, s2, s timeUnit (optional)) #比较s1和s2两个字符串,返回s2中两者不一样的字符串;也可以用于计算日期差
--1: "cacti".diff("cactus") ---> "us"
escape(s, s mode)/unescape(s, s mode) #转义/不转义字符串s,mode可以为: "html"/"xml"/"csv"/"url"/"javascript"
reinterpret(s, s encoderTarget, s encoderSource) #对s进行重新编码,"GBK"/"UTF-8"
fingerprint(s) #返回s的指纹向量(指纹向量聚类时用到): 删除所有的空白和标点,全部转为小写,按照字母顺序重新排列
--1: "Ruth Prawer Jhabvala".fingerprint() ---> "jhabvala prawer ruth"
ngram(s, n) #返回s的n-gram向量(n-gram向量聚类时用到): 对s中的词按照长度n依次滑动分组切分
--1: "Ruth Prawer Jhabvala".ngram(2) ---> [ "Ruth Prawer", "Prawer Jhabvala" ]
--2: "Ruth Prawer Jhabvala".ngram(4) ---> ["Ruth Prawer Jhabvala"]
ngramFingerprint(s, n) #返回s的n-gram的指纹向量,先进行n-gram分词,然后再去重,按照字母排序
--1: "banana".ngramFingerprint(2) ---> "anbana" #先进行n-gram分词得到ba an na an na,再去重得到ba an na,再排序得到anbana
#Format-based functions(JSON, HTML, XML)(JSON, HTML, XML格式化函数)
jsonize(o) #返回o对应的json格式
parseJson(s) #对json格式字符串进行解析(从而后面就可以利用get()函数来获取对应的值了)
--1: parseJson(" { 'a' : 1 } ").get("a") ---> 1
#对于复杂的json格式的value,可以利用forEach()函数来获取对应的值
{"status":"OK","url":"","language":"english","keywords":[{"text":"York en route","relevance":"0.974363"},{"text":"Anthony Eden","relevance":"0.814394"},{"text":"President Eisenhower","relevance":"0.700189"}]}
--2: forEach(value.parseJson().keywords,v,v.text).join("::") ---> "York en route::Anthony Eden::President Eisenhower"
parseXml(s) #返回s对应的xml形式(自动补全)
parseHtml(s) #返回s对应的HTML形式(自动补全)
--1: value.parseHtml().toString() #通过toString()函数保存为字符串
--2: value.parseHtml().select() #通过select()函数获得Html文档中指定的部分
select(s, element) #返回XML或HTML文档中指定的单元列表,如果存在的话,element遵从jsoup(Java的HTML解析器)选择器的语法
--1: value.parseHtml().select("img.portrait")[0] ---> value中第一个img标签中portrait类对应的部分
--2: value.parseHtml().select("div#content")[0].select("tr").toString() #可以多次使用select()
htmlAttr(s, element)#返回HTML单元的element属性
--1: value.parseHtml().select("a.email")[0].htmlAttr("href") ---> 得到email类对应的超级链接网址
htmlText(element) #返回HTML单元(包括子单元)的所有文字,去除所有HTML标签和换行符
--1: value.parseHtml().select("div.footer")[0].htmlText()
xmlAttr(s, element) #返回XML单元的element属性
xmlText(element) #返回XML单元(包括子单元)的所有文字,去除所有XML标签和换行符
wholeText(element) #返回指定单元(包括子单元)的所有文字,包括所有换行符、空白
--1: value.parseHtml().select("div.footer")[0].wholeText()
innerHtml(element) #返回HTML文档的innerHtml单元内容,从对象的起始位置到终止位置的全部内容,不包括Html标签
innerXml(element) #返回XML文档的innerXML单元内容,只包含子元素内容
ownText(element) #返回文档直接的XML或HTML本级单元内容,不包含子元素内容
#Variables(变量)#
大部分的变量都有属性值,可以用方括号[]或点符号.来获取属性值。
value: 当前行当前列单元的值,可以为空
row: 当前行
row.record=row["record"]: 一行或多行一起构成一条记录,row.record.rowCount表示该记录的行数
cells: 当前行的数据单元: cells["Postal Code"]
cell.recon: 当前数据单元对应的搭配信息
rowIndex: 当前行的索引值
columnName: 当前数据单元的列名,字符串类型
#Row(数据行)#
row.index: 当前行的索引值
row.cells: 当前数据单元所在的行数据,列表类型
--1: row.cells['省份'].value #利用这个可以在当前数据单元获取当前行其他列单元的数据值,row.cells['省份'].value
row.columnNames: 当前项目的列名称,列表类型,通过索引可以取到具体某列的列名
--1: row.columnNames[3]
--2: toString(row.columnNames)
--3: forEach(row.columnNames,v,v).join("; ")
row.starred: 当前行是否打星号
row.flagged: 当前行是否被标记
row.record: 包含当前行的记录
#Cells(数据单元组)# #可以用来获取指定列对应的单元组
--1: cells["Postal Code"].value ---> 指定列Postal Code的对应单元格的值
#Cell(数据单元)# #当前单元格
--1: cell.value/cell.recon/cell.errorMessage
#Reconciliation(数据搭配)#
cell.recon.judgment: matched/new/none
cell.recon.judgmentAction: single/similar/unknown
cell.recon.judgmentHistory: 一个数值,表示时间长度的
cell.recon.matched: 布尔型,表示数据是否搭配
cell.recon.match: 与本单元数据搭配的对象(.id, .name, .type)
cell.recon.best: 与本单元数据搭配得分最高的候选对象(.id, .name, .type, .score)
cell.recon.features: 用来对匹配精度进行评估的搭配特征(.typeMatch, .nameMatch, .nameLevenshtein, .nameWordDistance)
cell.recon.features.typeMatch: 布尔型,表示选择的类型是否搭配
cell.recon.features.nameMatch: 布尔型,表示当前数据单元与选择的字符串是否相同
cell.recon.features.nameLevenshtein: 数值型,表示匹配时的Levenshtein距离,当前单元的值和候选对象差异越大,Levenshtein距离就越大
cell.recon.features.nameWordDistance: 数值型,表示基于词相似性的距离
cell.recon.candidates: 列表型,排名前3的潜在候选对象(.id, .name, .type, .score)
--1: forEach(cell.recon.candidates,v,v.name).join("; ") 将候选对象及其类型合成为一个字符串
#Record(记录)#
row.record.index: 当前行的索引值
row.record.cells: 列表型,给定记录列中的单元列表
row.record.fromRowIndex: 记录中第一行的索引
row.record.toRowIndex: 记录中最后一行的索引+1,也即下一条记录的行索引
row.record.rowCount: 记录的行数
开放原子开发者工作坊旨在鼓励更多人参与开源活动,与志同道合的开发者们相互交流开发经验、分享开发心得、获取前沿技术趋势。工作坊有多种形式的开发者活动,如meetup、训练营等,主打技术交流,干货满满,真诚地邀请各位开发者共同参与!
更多推荐
已为社区贡献1条内容
所有评论(0)