我编写了一个简单的xml解析器。但是演出很糟糕。我不知道问题是什么,我的代码的瓶颈在哪里,这是由Swift 3.1在MacOS Sierra上编译的。
当我在一些XML文件(大约有43000个字符)上测试代码时,解析该文件大约需要4.5分钟!这是我的源代码:

class Parser {

let ignorableChars : [String] = [" ", "\"", "=", "\r", "\n", "\t"]

var i : Int
var xmlString : String
var stack : [Element]

init(xmlString: String) {
    self.i = 0
    self.xmlString = xmlString
    self.stack = [Element]()
}

func ignoreProlog() -> Void {
    while self.xmlString[self.i] != "<" {
        self.i += 1
    }
    self.i += 1

    if self.xmlString[self.i] == "?" {
        self.i += 1
        while self.xmlString[self.i] == "?" && self.xmlString[self.i + 1] == ">" {
            self.i += 1
        }
        self.i += 2
    }
    else {
        self.i -= 1
    }

}

func ignoreMiscChars() -> Void {
    while self.ignorableChars.contains(self.xmlString[self.i]) {
        self.i += 1
    }
}

func extractType() -> String {
    var elementType : String = ""

    while self.ignorableChars.contains(self.xmlString[self.i]) == false && self.xmlString[self.i] != ">" {
        elementType.append(self.xmlString[self.i])
        self.i += 1
    }
    return elementType
}

func extractKey() -> String {
    var attrKey : String = ""

    while self.ignorableChars.contains(self.xmlString[self.i]) == false && self.xmlString[self.i] != "/" && self.xmlString[self.i] != ">" {
        attrKey.append(self.xmlString[self.i])
        self.i += 1
    }
    return attrKey
}

func extractValue() -> String {
    var attrValue : String = ""

    while self.xmlString[self.i] != "\"" {
        attrValue.append(self.xmlString[self.i])
        self.i += 1
    }
    self.i += 1
    return attrValue
}

func extractElement() -> (Element?, String?) {
    while self.xmlString[self.i] != "<" {
        self.i += 1
    }
    self.i += 1

    if self.xmlString[self.i] == "/" {
        self.i += 1
        let elementType = self.extractType()
        return (nil, elementType)
    }

    self.ignoreMiscChars()
    let elementType = self.extractType()

    var attributes = [String:String]()
    while self.xmlString[self.i] != ">" &&  self.xmlString[self.i] != "/"{
        self.ignoreMiscChars()
        let key = self.extractKey()
        if key == "" {
            break
        }
        self.ignoreMiscChars()
        let value = self.extractValue()
        attributes[key] = value
    }

    let element = Element(type: elementType)
    element.attributes = attributes

    if self.xmlString[self.i] == "/" {
        element.isCompleted = true
    }

    return (element, nil)
}

public func xmlParser() -> Element {
    self.ignoreProlog()
    while stack.isEmpty || stack[0].isCompleted == false {
        let element = extractElement()
        if element.0 == nil {
            if element.1 == stack[0].type {
                stack[0].isCompleted = true
                break
            }
            let lastElement = stack.last
            lastElement?.isCompleted = true
            stack.removeLast()
            stack[stack.endIndex - 1].chidren.append(lastElement!)
        }
        else if element.0?.isCompleted == true {
            stack[stack.endIndex - 1].chidren.append(element.0!)
        }
        else {
            stack.append(element.0!)
        }
    }

    let root = stack.last
    stack.removeAll()
    return root!
  }
}

最佳答案

尝试以下快速技巧,以确定字符串订阅(即self.xmlString[self.i])是否是您的瓶颈:

...
var i: Int
let xmlString: [Character]
var stack: [Element]

init(xmlString: String) {
    self.i = 0
    self.xmlString = Array(xmlString.characters)
    self.stack = [Element]()
}
...

自由索引这样的String会很快破坏您的性能,考虑到所有Unicode重操作happening under the hood
相反,像上面的代码一样,将一次转换为Character数组可能会显著提高性能。索引一个数组是一个便宜的O(1)操作(即,恒定时间)。

09-25 18:45