【问题标题】:How doing String-Programming in Swift如何在 Swift 中进行字符串编程
【发布时间】:2014-08-24 13:25:36
【问题描述】:

我想念可用的字符串函数,它们易于使用,无需键入奇怪的标识符行。所以我决定建立一个具有有用且可识别的字符串函数的库。

【问题讨论】:

  • 我建议您阅读语言规范的这一章developer.apple.com/library/prerelease/ios/documentation/Swift/…
  • +1 问题和答案,用于从原始帖子中拆分。欢迎使用 StackOverflow!
  • @Nicola:也就是说,我做了什么,但它需要很多时间。这篇文章适用于所有想要轻松使用 Strings 和 Swift 做一些事情而无需阅读数小时的新手。当您阅读我的答案并发现某些内容时,可以更轻松地完成此操作,而不是对此发表评论。谢谢。

标签: string unicode swift


【解决方案1】:

我首先尝试使用 Cocoa String-Functions 来解决这个问题。于是我在操场上试了一下:

import Cocoa

func PartOfString(s: String, start: Int, length: Int) -> String
{
    return s.substringFromIndex(advance(s.startIndex, start - 1)).substringToIndex(advance(s.startIndex, length))
}

PartOfString("HelloaouAOUs.World", 1, 5) --> "Hello"
PartOfString("HelloäöüÄÖÜß…World", 1, 5) --> "Hello"

PartOfString("HelloaouAOUs.World", 1, 18) --> "HelloaouAOUs.World"
PartOfString("HelloäöüÄÖÜß…World", 1, 18) --> "HelloäöüÄÖÜß…World"

PartOfString("HelloaouAOUs.World", 6, 7) --> "aouAOUs"
PartOfString("HelloäöüÄÖÜß…World", 6, 7) --> "äöüÄO"

如果 UnCode 字符在字符串中,则“substringFromIndex”不是起始索引。更糟糕的是,如果 UnCode-Characters 在字符串中,则 Swift-Program 有时会在运行时崩溃,对于这种情况,“substringFromIndex”不是 Start-Index。所以我决定创建一组新的函数来解决这个问题并使用 UnCode-Characters。请注意,文件名也可以包含 UnCode-Characters。所以如果你认为你不需要 UnCode-Characters 你就错了。

如果你想重现这个,你需要我使用的相同字符串,因为从这个网页复制不会重现问题。

var s: String = "HelloäöüÄÖÜß…World"
var t: String = s.stringByAddingPercentEscapesUsingEncoding(NSUTF8StringEncoding)!
var u: String = "Helloa%CC%88o%CC%88u%CC%88A%CC%88O%CC%88U%CC%88%C3%9F%E2%80%A6World".stringByRemovingPercentEncoding!
var b: Bool = (s == u) --> true
PartOfString(s, 6, 7) --> "äöüÄO"

现在你可以明白了,将令人不安的 Canonical-Mapping UniCodes 转换为具有以下功能的兼容代码:

func percentescapesremove (s: String) -> String
{
    return (s.stringByRemovingPercentEncoding!.precomposedStringWithCompatibilityMapping)
}

你会得到的结果是:

var v: String = percentescapesremove(t) --> "HelloäöüÄÖÜß...World"
PartOfString(v, 6, 7) --> "äöüÄÖÜß"
var a: Bool = (s == v) --> false

当您这样做时,“äöüÄÖÜß”看起来不错,您认为一切正常,但请查看已从 UniCode“...”永久转换为非 UniCode“...”的“...”和结果与第一个字符串不同。如果您有 UniCode 文件名,则转换将导致在卷上找不到文件。所以最好只为 scree-output 进行转换并将原始字符串保存在一个保存位置。

上面 PartOfString-Function 的问题是,它在赋值的第一部分生成一个新字符串,并使用这个新字符串和旧字符串的索引,这是行不通的,因为 UniCode 有不同的长度比普通字母长。所以我改进了功能(感谢 Martin R 的帮助):

func NewPartOfString(s: String, start: Int, length: Int) -> String
{
    let t: String = s.substringFromIndex(advance(s.startIndex, start - 1))
    return t.substringToIndex(advance(t.startIndex, length))
}

而且结果是正确的:

NewPartOfString("HelloaouAOUs.World", 1, 5) --> "Hello"
NewPartOfString("HelloäöüÄÖÜß…World", 1, 5) --> "Hello"

NewPartOfString("HelloaouAOUs.World", 1, 18) --> "HelloaouAOUs.World"
NewPartOfString("HelloäöüÄÖÜß…World", 1, 18) --> "HelloäöüÄÖÜß…World"

NewPartOfString("HelloaouAOUs.World", 6, 7) --> "aouAOUs"
NewPartOfString("HelloäöüÄÖÜß…World", 6, 7) --> "äöüÄÖÜß"

在下一步中,我将展示一些可以使用且运行良好的功能。它们都基于整数索引值,从 1 开始,第一个字符结束,最后一个字符的索引与字符串的长度相同。

这个函数返回一个字符串的长度:

func len (s: String) -> Int
{
    return (countElements(s)) // This works not really fast, because of UniCode
}

此函数返回字符串中第一个 UniCode-Character 的 UniCode-Number:

func asc (s: String) -> Int
{
    if (s == "")
    {
        return 0
    }
    else
    {
        return (Int(s.unicodeScalars[s.unicodeScalars.startIndex].value))
    }
}

此函数返回给定 UniCode-Number 的 UniCode-Character:

func char (c: Int) -> String
{
    var s: String = String(UnicodeScalar(c))
    return (s)
}

此函数返回字符串的大写表示:

func ucase (s: String) -> String
{
    return (s.uppercaseString)
}

此函数返回字符串的小写表示:

func lcase (s: String) -> String
{
    return (s.lowercaseString)
}

下一个函数给出一个给定长度的字符串的左边部分:

func left (s: String, length: Int) -> String
{
    if (length < 1)
    {
        return ("")
    }
    else
    {
        if (length > len(s))
        {
            return (s)
        }
        else
        {
            return (s.substringToIndex(advance(s.startIndex, length)))
        }
    }
}

下一个函数给出具有给定长度的字符串的右侧部分:

func right (s: String, laenge: Int) -> String
{
    var L: Int = len(s)
    if (L <= laenge)
    {
        return(s)
    }
    else
    {
        if (laenge < 1)
        {
            return ("")
        }
        else
        {
            let t: String = s.substringFromIndex(advance(s.startIndex, L - laenge))
            return t.substringToIndex(advance(t.startIndex, laenge))
        }
    }
}

下一个函数给出了字符串中给定长度的部分:

func mid (s: String, start: Int, laenge: Int) -> String
{
    if (start <= 1)
    {
        return (left(s, laenge))
    }
    else
    {
        var L: Int = len(s)
        if ((start > L) || (laenge < 1))
        {
            return ("")
        }
        else
        {
            if (start + laenge > L)
            {
                let t: String = s.substringFromIndex(advance(s.startIndex, start - 1))
                return t.substringToIndex(advance(t.startIndex, L - start + 1))
            }
            else
            {
                let t: String = s.substringFromIndex(advance(s.startIndex, start - 1))
                return t.substringToIndex(advance(t.startIndex, laenge))
            }
        }
    }
}

更难的是在给定位置获取一个字符,因为我们不能使用“substringFromIndex”和“substringFromIndex”,而“substringFromIndex”不是起始索引。所以想法是逐个字符地跟踪字符串,并获取所需的子字符串。

func CharacterOfString(s: String, index: Int, length: Int) -> String
{
    var c: String = ""
    var i: Int = 0
    for UniCodeChar in s.unicodeScalars
    {
        i = i + 1
        if ((i >= index) && (i < index + length))
        {
            c = c + String(UniCodeChar)
        }
    }
    return (c)
}

但这不适用于包含 UniCode 字符的字符串。以下示例显示了发生的情况:

CharacterOfString("Swift Example Text aouAOUs.", 16, 8) --> "ext aouA"
len(CharacterOfString("Swift Example Text aouAOUs.", 16, 8)) --> 8

CharacterOfString("Swift Example Text äöüÄÖÜß…", 16, 8) --> "ext äö"
len(CharacterOfString("Swift Example Text äöüÄÖÜß…", 16, 8)) --> 6

所以我们看到,结果字符串太短了,因为一个 UniCode-Character 可以包含多个字符。这是因为“ä”可以是一个 UniCode-Character,也可以写成两个“a¨” UniCode-Character。所以我们需要另一种方法来获取有效的子字符串。

解决方案是将 UniCode-String 转换为 UniCode-Characters 数组,并使用数组的索引来获取有效字符。这在所有情况下都适用于在给定索引处获取 UniCode-String 的单个字符:

func indchar (s: String, i: Int) -> String
{
    if ((i < 1) || (i > len(s)))
    {
        return ("")
    }
    else
    {
        return String(Array(s)[i - 1])
    }
}

有了这些知识,我构建了一个函数,它可以得到一个有效的 UniCode-Substring,它具有给定的 Start-Index 和给定的长度:

func substring(s: String, Start: Int, Length: Int) -> String
{
    var L: Int = len(s)
    var UniCode = Array(s)
    var result: String = ""
    var TheEnd: Int = Start + Length - 1

    if ((Start < 1) || (Start > L))
    {
        return ("")
    }
    else
    {
        if ((Length < 0) || (TheEnd > L))
        {
            TheEnd = L
        }
        for var i: Int = Start; i <= TheEnd; ++i
        {
            result = result + String(UniCode[i - 1])
        }
        return (result)
    }
}

下一个函数搜索给定字符串在另一个字符串中的位置:

func position (original: String, search: String, start: Int) -> Int
{
    var index = part(original, start).rangeOfString(search)
    if (index != nil)
    {
        var pos: Int = distance(original.startIndex, index!.startIndex)
        return (pos + start)
    }
    else
    {
        return (0)
    }
}

如果给定的字符代码是数字 (0-9),则此函数看起来:

func number (n: Int) -> Bool
{
    return ((n >= 48) & (n <= 57)) // "0" to "9"
}

现在显示了基本的字符串操作,但是数字呢?数字如何转换为字符串,反之亦然?让我们看一下将字符串转换为数字。请不要“!”在第二行中,用于获取 Int 而不是可选的 Int。

var s: String = "123" --> "123"
var v: Int = s.toInt() --> (123)
var v: Int = s.toInt()! --> 123

但这不起作用,如果字符串包含一些字符:

var s: String = "123." --> "123."
var v: Int = s.toInt()! --> Will result in a Runtime Error, because s.toInt() = nil

所以我决定构建一个更小的函数来获取字符串的值:

func val (s: String) -> Int
{
    var p: Int = 0
    var sign: Int = 0

    if (indchar(s, 1) == "-")
    {
        sign = 1
        p = 1
    }
    while(number(asc(indchar(s, p + 1))))
    {
        p = p + 1
    }
    if (p > sign)
    {
        return (left(s, p).toInt()!)
    }
    else
    {
        return (0)
    }
}

现在结果是正确的并且不会产生运行时错误:

var s: String = "123." --> "123."
var v: Int = val(s) --> 123

现在浮点数也一样:

func realval (s: String) -> Double
{
    var r: Double = 0
    var p: Int = 1
    var a: Int = asc(indchar(s, p))
    if (indchar(s, 1) == "-")
    {
        p = 2
    }
    while ((a != 44) && (a != 46) && ((a >= 48) & (a <= 57)))
    {
        p = p + 1
        a = asc(indchar(s, p))
    }
    if (p >= len(s)) // Integer Number
    {
        r = Double(val(s))
    }
    else // Number with fractional part
    {
        var mantissa: Int = val(substring(s, p + 1, -1))
        var fract: Double = 0
        while (mantissa != 0)
        {
            fract = (fract / 10) + (Double(mantissa % 10) / 10)
            mantissa = mantissa / 10
            p = p + 1
        }
        r = Double(val(s)) + fract
        p = p + 1
    }
    a = asc(indchar(s, p))
    if ((a == 69) || (a == 101)) // Exponent
    {
        var exp: Int = val(substring(s, p + 1, -1))
        if (exp != 0)
        {
            for var i: Int = 1; i <= abs(exp); ++i
            {
                if (exp > 0)
                {
                    r = r * 10
                }
                else
                {
                    r = r / 10
                }
            }
        }
    }
    return (r)
}

这适用于带指数的浮点数:

var s: String = "123.456e3"
var t: String = "123.456e-3"
var v: Double = realval(s) --> 123456
var w: Double = realval(t) --> 0.123456

从整数生成字符串要简单得多:

func str (n: Int) -> String
{
    return (String(n))
}

浮点变量的字符串不能与 String(n) 一起使用,但可以使用:

func strreal (n: Double) -> String
{
    return ("\(n)")
}

【讨论】:

  • 我无法重现您关于 println(PartOfString("HelloäöüÄÖÜß…World", 6, 7)) 的初始声明,它会在我的应用程序中返回正确的结果 "äöüÄÖÜß"。但是创建一个字符串的所有字符的数组只是为了提取一个子字符串或单个字符对于非常长的字符串是非常低效的。
  • 我也怀疑是否值得将转换“重新编程”为 int 或 real 值。如果您不喜欢 toInt() 的严格行为,那么您仍然可以使用来自 Cocoa 的 intValue() / doubleValue(),例如let i = (s as NSString).doubleValue.
  • 我使用了 Unicode 字符串 ""Hello%C3%A4%C3%B6%C3%BC%C3%84%C3%96%C3%9C%C3%9F...World"。当从这个网页复制它时,它可以工作,但不是原来的。原因是相同光学外观的不同编码。如果你想复制它,请使用:PartOfString("Hello%C3%A4%C3%B6% C3%BC%C3%84%C3%96%C3%9C%C3%9F...World".stringByRemovingPercentEncoding!.decomposedStringWithCanonicalMapping, 6, 8)
  • 如果你使用substringWithRange,它似乎可以正常工作(就像这个问题的一些答案stackoverflow.com/questions/24029163/…):let startIndex = advance(s.startIndex, start-1); let endIndex = advance(startIndex, length); return = s.substringWithRange(startIndex ..&lt; endIndex)
  • 您的 PartOfString 函数的问题是 s.substringFromIndex() 返回一个 new 字符串,因此您无法在该新字符串上安全地使用 s.startIndex,请参阅最后一部分stackoverflow.com/a/24056932/1187415.
猜你喜欢
  • 2016-10-11
  • 1970-01-01
  • 2017-06-17
  • 1970-01-01
  • 1970-01-01
  • 2014-03-27
  • 2011-06-24
  • 2011-10-06
  • 2012-12-23
相关资源
最近更新 更多