首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >如何使用SWIFT5.7的RegexBuilder捕获10件以上的东西?

如何使用SWIFT5.7的RegexBuilder捕获10件以上的东西?
EN

Stack Overflow用户
提问于 2022-09-22 02:08:19
回答 1查看 162关注 0票数 3

假设我有一个文件存储有关人员的信息,其中一行如下所示:

代码语言:javascript
复制
Sweeper 30 1992-09-22 China/Beijing - 0 2020-07-07 Mary/Linda - Pizza/Lemon

从左到右,包括姓名、年龄、出生日期、出生国家、出生城市、子女数、结婚日期(可选)、妻子姓名(可选)、前妻姓名(可选)、最喜欢的食物、最不喜欢的食物。

我想使用SWIFT5.7 RegexBuilder模块从行中获取所有信息,我尝试了:

代码语言:javascript
复制
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”或类似的重载(对于细节不太确定):

代码语言:javascript
复制
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那样方便。

EN

回答 1

Stack Overflow用户

发布于 2022-09-22 05:36:32

作为一个黑客,您可以创建一个通用的CustomConsumingRegexComponent实现

  • 任何由构建器构建的RegexComponent,它总是有一个(Substring, A, B, C ...)元组作为输出
  • ,该转换将元组转换为我们希望的

类型的T

我们基本上可以创建一个regex组件,它接收一些regex并输出我们想要的任何类型的T,本质上是对捕获进行“分组”。

也有可能不进行转换,最终会得到嵌套的元组,但我不喜欢这样。

代码语言:javascript
复制
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中添加一个展望。

代码语言:javascript
复制
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之外的事情,所以这里不会发生回溯,整个匹配都失败了。

票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/73808600

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档