假设我有一个文件存储有关人员的信息,其中一行如下所示:
Sweeper 30 1992-09-22 China/Beijing - 0 2020-07-07 Mary/Linda - Pizza/Lemon从左到右,包括姓名、年龄、出生日期、出生国家、出生城市、子女数、结婚日期(可选)、妻子姓名(可选)、前妻姓名(可选)、最喜欢的食物、最不喜欢的食物。
我想使用SWIFT5.7 RegexBuilder模块从行中获取所有信息,我尝试了:
let regex = Regex {
/([a-zA-Z ]+)/ // Name
" "
TryCapture { OneOrMore(.digit) } transform: { Int($0) } // Age
" "
Capture(.iso8601Date(timeZone: .gmt)) // Date of Birth
" "
/([a-zA-Z ]+)/ // Country of Birth
"/"
/([a-zA-Z ]+)/ // City of Birth
" - "
TryCapture { OneOrMore(.digit) } transform: { Int($0) } // Children Count
Optionally {
" "
Capture(.iso8601Date(timeZone: .gmt)) // Date of Marriage
Optionally {
" "
/([a-zA-Z ]+)/ // Wife
Optionally {
"/"
/([a-zA-Z ]+)/ // Ex-wife
}
}
}
" - "
/([a-zA-Z ]+)/ // Favourite food
"/"
/([a-zA-Z ]+)/ // Least Favourite Food
}然而,Swift说,它无法在合理的时间内输入检查。
我知道发生这种情况的原因是因为RegexComponentBuilder ( regex组件的结果生成器)只有10“C”或类似的重载(对于细节不太确定):
static func buildPartialBlock<W0, W1, C1, C2, C3, C4, C5, C6, C7, C8, C9, C10, R0, R1>(
accumulated: R0,
next: R1) -> Regex<(Substring, C1, C2, C3, C4, C5, C6, C7, C8, C9, C10)> where R0 : RegexComponent, R1 : RegexComponent, R0.RegexOutput == (W0, C1, C2, C3), R1.RegexOutput == (W1, C4, C5, C6, C7, C8, C9, C10
)如果我把所需的所有Optionally部件都做好了,错误信息就会变得更加明显。
不明确地使用“buildPartialBlock(累计:下一步:)”
SwiftUI也有类似的问题,视图生成器中的视图数不能超过10,在这种情况下,您只需使用Group将一些视图变成单个视图。你能在RegexBuilder中做类似的事情吗?让其中的一些捕获成为一个单独的捕获?它似乎与AnyRegexOutput有关,但我不知道如何使用它。
如何解决此编译器错误?
为了避免XY问题:
我有一个数据文件,其中的数据格式非常随意,也就是说,根本不像CSV或JSON那样具有机器可读性。行是以各种格式编写的。随机分隔符用于随机位置。
然后文件中的另一行将具有相同的信息,但格式化方式不同。
我想要做的是将这个奇怪的格式化文件转换成一个易于使用的格式,比如CSV。我决定用SWIFT5.7 RegexBuilder API来实现这一点。我会在文件中找到一行,编写与该行匹配的regex,将与regex匹配的文件的所有行转换为CSV,然后进行漂洗和重复。
因此,我希望避免使用多个regexes来解析单个行,因为这意味着我将编写更多的regexes。
我不确定像ANTLR4这样的解析器是否能解决我的问题。考虑到文件格式化的随机性,我需要对解析器进行大量更改,导致文件一次又一次地生成。我不认为这会像使用RegexBuilder那样方便。
发布于 2022-09-22 05:36:32
作为一个黑客,您可以创建一个通用的CustomConsumingRegexComponent实现
RegexComponent,它总是有一个(Substring, A, B, C ...)元组作为输出类型的T。
我们基本上可以创建一个regex组件,它接收一些regex并输出我们想要的任何类型的T,本质上是对捕获进行“分组”。
也有可能不进行转换,最终会得到嵌套的元组,但我不喜欢这样。
struct Group<RegexOutput, Component: RegexComponent>: CustomConsumingRegexComponent {
let component: () -> Component
let transform: (Component.RegexOutput) -> RegexOutput
init(@RegexComponentBuilder _ regexBuilder: @escaping () -> Component, transform: @escaping (Component.RegexOutput) -> RegexOutput) {
component = regexBuilder
self.transform = transform
}
func consuming(_ input: String, startingAt index: String.Index, in bounds: Range<String.Index>) throws -> (upperBound: String.Index, output: RegexOutput)? {
let innerRegex = Regex(component)
guard let match = input[index...].prefixMatch(of: innerRegex) else { return nil }
let upperBound = match.range.upperBound
let output = match.output
let transformedOutput = transform(output)
return (upperBound, transformedOutput)
}
}之所以这只是一次黑客攻击,是因为Group内部的正则表达式实际上并不知道Group之外的内容,因此Group中的量词不会回溯到试图匹配Group之外的内容。
例如,要修复问题中的代码,我可以将所有与婚姻相关的信息放到Group中,但是我必须在Group中添加一个展望。
struct Marriage {
let marriageDate: Date
let wife: Substring?
let exWife: Substring?
}
let r = Regex {
/([a-zA-Z ]+)/ // Name
" "
TryCapture { OneOrMore(.digit) } transform: { Int($0) } // Age
" "
Capture(.iso8601Date(timeZone: .gmt)) // Date of Birth
" "
/([a-zA-Z ]+)/ // Country of Birth
"/"
/([a-zA-Z ]+)/ // City of Birth
" - "
TryCapture { OneOrMore(.digit) } transform: { Int($0) } // Children Count
Optionally {
" "
Capture(Group {
Capture(.iso8601Date(timeZone: .gmt)) // Date of Marriage
Optionally {
" "
/([a-zA-Z ]+)/ // Wife
Optionally {
"/"
/([a-zA-Z ]+)/ // Ex-wife
}
}
Lookahead(" - ")
} transform: { (_, date, wife, exWife) in
Marriage(marriageDate: date, wife: wife, exWife: exWife as? Substring) // unwrap the double optional
})
}
" - "
/([a-zA-Z ]+)/ // Favourite food
"/"
/([a-zA-Z ]+)/ // Least Favourite Food
}如果没有前瞻性,就会发生这样的情况:
最内部的[a-zA-Z ]+将匹配Linda,以及后面的空格,从而导致" - "不匹配。通常情况下,这会导致回溯,但是由于Group内部的东西不知道Group之外的事情,所以这里不会发生回溯,整个匹配都失败了。
https://stackoverflow.com/questions/73808600
复制相似问题