已经完成了键的修改,没把加""的都加上了, 但值要如何正则给加上""呢?
func TestMakeToJsonStr(t *testing.T) {
str := `{label :用户 id,searchType:1,hide:1,disabled:1,required:1,options:1:yes:tag-info,2:no:tag-danger}`
// 替换所有空格
// 处理 key 未加""的内容字段
replace, _ := gregex.Replace(`\s`, []byte(""), []byte(str))
replace, _ = gregex.Replace(`label|"label"`, []byte(`"label"`), replace)
replace, _ = gregex.Replace(`fieldType|"fieldType"`, []byte(`"fieldType"`), replace)
replace, _ = gregex.Replace(`searchType|"searchType"`, []byte(`"searchType"`), replace)
replace, _ = gregex.Replace(`editHide|"editHide"`, []byte(`"editHide"`), replace)
replace, _ = gregex.Replace(`addHide|"addHide"`, []byte(`"addHide"`), replace)
replace, _ = gregex.Replace(`hide|"hide"`, []byte(`"hide"`), replace)
replace, _ = gregex.Replace(`disabled|"disabled"`, []byte(`"disabled"`), replace)
replace, _ = gregex.Replace(`required|"required"`, []byte(`"required"`), replace)
replace, _ = gregex.Replace(`comment|"comment"`, []byte(`"comment"`), replace)
replace, _ = gregex.Replace(`options|"options"`, []byte(`"options"`), replace)
// 处理值未加个""的字段
fmt.Println(string(replace))
}
现在的执行结果
{"label":用户 id,"searchType":1,"hide":1,"disabled":1,"required":1,"options":1:yes:tag-info,2:no:tag-danger}
注明 键就是那几个是固定的
1
sutra 2022-07-24 14:50:49 +08:00
Go's stdlib regexp engine is RE2 which does not support lookaround (e.g. the ?! negative lookahead operator).
难啊。 |
2
sutra 2022-07-24 16:20:05 +08:00 1
|
3
sutra 2022-07-24 16:43:15 +08:00
搞个第三方库,支持 ?! 就行了。
|
4
sutra 2022-07-24 16:44:12 +08:00
使用 regexp2 的 go 版本见上面 Java 版本后面的回复,v2ex 不让我贴太多 URL 。
|
5
wxf666 2022-07-24 16:44:29 +08:00
#1 说这是 golang ?在 regex101 看了下,支持的正则特性有点少。。
反正不要求通用方案,那就随便写咯 『模式匹配』 \s*"?(options)"?\s*:\s*"?(.*?)"?\s*(})|\s*"?(\w+)"?\s*:\s*"?(.*?)"?\s*(,) 『全部替换为』 \"$1$4\":\"$2$5\"$3$6 『要求』 1. 除了 options 外,其他键的值不包含『,』(锚定键值对结束) 2. options 键在最后一位,其值不包含『}』(锚定键值对结束) 3. 头尾裹上『"』后(若已有则不裹)仍符合 json 字符串规范 (如:『 key: 他说"xxx",我不以为然』变成『"key": "他说"xxx",我不以为然"』会出问题) 『例子』 原文: { "label" : 用户 id , searchType : "1" , "hide" : 1 , disabled : "1" , "required" : "1" , options:1:yes:tag-info,2:no:tag-danger } 替换后: {"label":"用户 id","searchType":"1","hide":"1","disabled":"1","required":"1","options":"1:yes:tag-info,2:no:tag-danger"} |
6
morri OP @sutra
@wxf666 谢谢 我后面改改 现在这样写的 ``` func makeToJsonStr(str string) string { // 替换所有空格 replace, _ := gregex.Replace(`\s`, []byte(""), []byte(str)) // 处理 key 未加""的内容字段 replace, _ = gregex.Replace(`label|"label"`, []byte(`"label"`), replace) replace, _ = gregex.Replace(`fieldType|"fieldType"`, []byte(`"fieldType"`), replace) replace, _ = gregex.Replace(`searchType|"searchType"`, []byte(`"searchType"`), replace) replace, _ = gregex.Replace(`editHide|"editHide"`, []byte(`"editHide"`), replace) replace, _ = gregex.Replace(`addHide|"addHide"`, []byte(`"addHide"`), replace) replace, _ = gregex.Replace(`hide|"hide"`, []byte(`"hide"`), replace) replace, _ = gregex.Replace(`disabled|"disabled"`, []byte(`"disabled"`), replace) replace, _ = gregex.Replace(`required|"required"`, []byte(`"required"`), replace) replace, _ = gregex.Replace(`comment|"comment"`, []byte(`"comment"`), replace) replace, _ = gregex.Replace(`options|"options"`, []byte(`"options"`), replace) // 处理值未加个""的字段 doAdd := func(temp string) []string { defer func() { if r := recover(); r != nil { panic(r) } }() strs := make([]string, 0) for _, i := range gstr.Split(temp, ",") { i = gstr.TrimAll(i) if i == "" { continue } begin := gstr.Split(i, ":")[0] end := gstr.Split(i, ":")[1] end = gstr.Replace(end, `"`, "") strs = append(strs, fmt.Sprintf(`%s:"%s"`, begin, end)) } return strs } temp := string(replace) temp = temp[1 : len(temp)-1] strs := make([]string, 0) if !gstr.Contains(temp, `"options":`) { strs = append(strs, doAdd(temp)...) } else { t := gstr.Split(temp, `"options":`) strs = append(strs, doAdd(t[0])...) t[1] = gstr.Replace(t[1], `"`, "") t[1] = fmt.Sprintf(`"%s"`, t[1]) strs = append(strs, fmt.Sprintf(`"options":%s`, t[1])) } return fmt.Sprintf(`{%s}`, strings.Join(strs, ",")) } ``` |
7
wxf666 2022-07-24 17:35:03 +08:00 1
@morri 临时学了下 golang ,看起来运行没问题
『源码』 package main import ( "fmt" "regexp" ) func main() { str := `{ "label" : {label} , searchType : "hide_222" , "hide" : 333 disabled , disabled : "required" , "required" : "options" , options:1:yes:tag-info,2:no:tag-danger }` re := regexp.MustCompile(`\s*"?(options)"?\s*:\s*"?(.*?)"?\s*(})|\s*"?(\w+)"?\s*:\s*"?(.*?)"?\s*(,)`) fmt.Println(re.ReplaceAllString(str, `"$1$4":"$2$5"$3$6`)) } 『输出』 {"label":"{label}","searchType":"hide_222","hide":"333 disabled","disabled":"required","required":"options","options":"1:yes:tag-info,2:no:tag-danger"} |
10
wxf666 2022-07-24 19:27:41 +08:00
@morri 那你要给出各字段值的特点才行啊
否则产生的歧义,怕是连人工都分不清: {options:1:yes:tag-info,2:no:tag-danger,label:用户 id,searchType:1,hide:1,disabled:1,required:1} 也可以认为是: {"options": "1:yes:tag-info,2:no:tag-danger,label:用户 id,searchType:1,hide:1,disabled:1,required:1"} |
11
morri OP @wxf666
最特别的值就是 `options:"1:yes:tag-info,2:no:tag-danger"` 因为这个 key 如果有出现那么它的值格式是这样的`value1:label1:class1,value2,label2,class2...` 如果是在不好判断,就让 `options` 的值必须带 “” 双引号吧。让程序单独判断一下.. |
12
wxf666 2022-07-24 20:16:34 +08:00 1
@morri 你试试这个:
\s*"?(options)"?\s*:\s*"?((?:[^,]*?:[^,]*?:[^,]*?,?)*)"?\s*([,}])|\s*"?(\w+)"?\s*:\s*"?(.*?)"?\s*([,}]) 『要求』 1. options 键的值,为若干个以『,』分隔的 value:label:class (每个字段都不包含『,』) 2. 其他键的值不包含『,』『}』 3. 头尾裹上『"』后(若已有则不裹)仍符合 json 字符串规范 |
14
sutra 2022-07-24 22:35:13 +08:00
只要有了 ?! ,且 option 的值里不包含其它 key: 就行。
|
15
Kisesy 2022-07-24 22:49:46 +08:00
我也遇到了一些奇特的格式,比如键没有引号,但是值有单引号,还有一种键和值都是单引号的
各式各样的不规范编码 |
16
wxf666 2022-07-25 00:24:22 +08:00
@sutra 我看 regex101 说,golang 的正则不支持断言,条件子组也不支持。你这是用了第三方库是嘛
每匹配一个字前,都要看看后面的是否是一个关键字,我总觉得性能消耗会大一点 另外,我老怀疑你『?!:』写错了…… |
17
356693212 2022-07-25 00:58:33 +08:00
1. 抽取 `{` 和 `}` 中的值
2. `,` 分割 key 和 value 3. 头尾加 `"` 4. value 为 `{` .goto 1. 5. 值转为 json |
19
lmshl 2022-07-25 11:11:32 +08:00
如果这东西生成有规律,建议一步到位写 parser
|
20
sutra 2022-07-25 12:14:02 +08:00
@wxf666 我怀疑你怀疑得是对的。于是我修了一下。
var keys = "label|fieldType|searchType|editHide|addHide|hide|disabled|required|comment|options"; var regex = "(?<key>" + keys + ")\\s*:(?<value>(?:(?!(" + keys + "):).)*)(?<delimiter>[\\,\\}])"; var replacement = "\"${key}\":\"${value}\"${delimiter}"; |
21
ericmzhu 2022-07-25 12:47:49 +08:00
我觉得自己先解析字符串为 Json Data ,在输出成 string 好弄点
|
22
ysc3839 2022-07-25 15:48:06 +08:00
感觉原数据挺规整的,自己写个解析器也不难吧?网上有许多 JSON 解析器的教程,跟着写一个就好了。
|
23
FYFX 2022-07-25 16:23:29 +08:00
我感觉也是写个 parser 比较好,用正则处理多重嵌套和换行感觉容易出问题,看描述大概也就写个 tokenizer ,然后对 token 判断一下类型确认是否加双引号就行
|
24
joesonw 2022-07-25 18:20:49 +08:00 via iPhone
/([\w\d_]+\s*\:/
|
25
wxf666 2022-07-25 19:15:20 +08:00
|
26
FYFX 2022-07-25 20:26:11 +08:00
@wxf666 按照 OP 说法,它这个是关键字固定的,而且 options 是特殊的其实应该在写 tokenizer 的时候做处理,应该是 5 个 token, "{","options",":","1:yes:tag-info,2:no:tag-danger","}",解析到 options 的时候做个特殊处理,在碰到下个关键字或者右花括号之前的塞到一个 token ,后面在 parser 再解析吧(也可以这步解析完作为一个特殊的 token),然后文法就和普通的 json 差不多了 ,大概长这样吧
object ::= "{" member ("," member )* "}" member ::= keyword ":" (object|primary) | "options": options_value options_value ::= number ":" string ("," number":"string)* keyword ::= label|searchType|... primary ::= number|string 我其实也是新手,最近在看 craftinginterpreters |
28
FYFX 2022-07-25 21:37:59 +08:00
@wxf666 因为这不是规范的 json ,正常来讲这段应该是在两个引号直接的字符串,我只是觉得这么做后面 paser 写起来会简单点,当然你也可以在 paser 阶段把 options 后面那段拼起来
|
29
wxf666 2022-07-26 06:08:21 +08:00
@FYFX 我这方面没啥经验。但总感觉,你的『解析 options 的值』这个步骤,适合放到语法分析中
如你所说,『在碰到下个<keyword, 关键字>或者<}, 右花括号>之前……』, 即『在碰到两种 Token 之前……』? 另外,放到语法分析中,后续若想解析成 下列形式,也更容易些?(可扩展性强些?) 1. {"options": {"1": "a:aa", "2": "b:bb"}} 2. {"options": ["1:a:aa", "2:b:bb"]} 好吧,如果放到词法分析中,要打算用啥方法解析 Token 呢? NFA/DFA ?应该不够用吧(也就是,三型文法的正则表达式,无法胜任了) LL/LR/SLR/LALR ?(我瞅瞅去) |
30
FYFX 2022-07-26 09:59:34 +08:00 via iPad
@wxf666 我只手写过 Scanner ,也就一个 while 循环从头扫到尾 http://www.craftinginterpreters.com/scanning.html
|
31
lmshl 2022-07-26 11:50:40 +08:00
我写了一堆电子垃圾,勉强能解析但应该无法适应更多情况了,建议批判就行,别学
面对这种数据格式,我建议打爆数据上游的狗头,让它改成标准 JSON 输出 @wxf666 https://gist.github.com/mingyang91/06f4a489c313a16b9285dd375a565808 |
32
wxf666 2022-07-26 17:38:13 +08:00
@lmshl 瞅了瞅,没用过 scala ,只能大概看得懂
这是扩展了自带正则库的文法规则,其递归下降去匹配? int 、bool 、double 、string 是词法分析,其余是语法分析?(感觉全说成是语法分析也无不可?) 全部是三型文法?(因为没有递归?如:options ::= "options" ":" (options_tuple ("," options_tuple)* | options)) 也是高度依赖 tuple 有明确不同于其他文法的规则(要是 {options: opt1: val1, opt2: val2, label: user_id} 就完蛋了) 估计上游不好好用 json 库,自己手动拼接去了,真的是搞事情 |
33
lmshl 2022-07-26 17:46:17 +08:00
@wxf666 可以有递归,但是它这个文法里数组没有起止符,会和其他规则有冲突,如果是写标准 JSON parser 的话,代码能比这还少。
最妖孽的就是他给的这个 options |
34
wxf666 2022-07-26 20:31:27 +08:00
@lmshl 懂的,都用递归下降了,肯定可以支持递归文法嘛
上条回复我是说,你当前代码里,没有用到递归文法,所以应该是三型文法 仅从『是否匹配』角度说,是可用『正则表达式』描述 你的文法 的(每个文法的结果处理函数就算了) |