Swift: извлечь подстроку из строки

#swift #find #substring

#swift #Найти #подстрока

Вопрос:

У меня есть строка, которая может быть любой из следующих:

 tempString = "What? The ID: 54673 Over there"
tempString = "Jump... ID: 4561E how high"
  

В принципе, я хочу извлечь идентификатор из пяти символов: из строки. Я могу сделать это с большинством языков, найдя ID:, добавив к нему 4, чтобы добраться до начала ID:, а затем взяв следующие пять символов.

Например, в Excel:

 =MID("Jump... ID: 4561E how high",FIND("ID:","Jump... ID: 4561E how high") 4, 5)
  

Would = 4561E.

Как нам это сделать с помощью Swift?

Комментарии:

1. Пожалуйста, добавьте свою собственную попытку решения этой

Ответ №1:

Метод регулярных выражений для поиска первых 5 символов из [0-9] или [A-F] после «ID: » (используются шестнадцатеричные буквы, только если это не тот случай, вы можете использовать A-Z).

 if let range = string.range(of: "(?<=ID: )[0-9A-F]{5}", options: .regularExpression) {
    let id = string[range]  // 
    // if you need a String instead of a substring
    let stringID = String(string[range])
}
  

редактировать / обновлять:

Глядя на ваш ответ, похоже, что требования в любом случае полностью отличаются от вашего исходного сообщения, чтобы найти соединение IF из 8 шестнадцатеричных символов, за которыми следует дефис, затем 4 шестнадцатеричных символа, за которыми следует дефис (3 раза), чем 12 шестнадцатеричных символов, вы можете использовать следующее регулярное выражение "([0-9a-f]{8}-)([0-9a-f]{4}-){3}([0-9a-f]{12})" :


 let dataString = """
{
  "@odata.context": "$metadata#GeoFences(Points())/$entity",
  "ID": "2b2a2abc-5962-4290-92b4-773025ffd50b",
  "Points": {
    "POINT_TYPE": "F",
    "POINT_NUM": 0,
    "LATITUDE": 32.92197686725423,
    "LONGITUDE": -117.04306932263532,
    "parent_ID": "2b2a2abc-5962-4290-92b4-773025ffd50b"
  },
  "GEOFENCE_NAME": "New Fence",
  "GEOFENCE_TYPE": "O",
  "PRIVACY": "X",
  "CENTER_LAT": 32.92043316309709,
  "CENTER_LONG": -117.04286922250975,
  "ZOOM_LAT": 0.006238797350533787,
  "ZOOM_LONG": 0.005345531926053582,
  "PATH_TOLERANCE": 5,
  "ENTRANCE_TOLERANCE": 5
}
"""
  

 if let range = dataString.range(of: "([0-9a-f]{8}-)([0-9a-f]{4}-){3}([0-9a-f]{12})", options: .regularExpression) {
    let id = dataString[range]  // 4561E
    print("ID:", id)
    // if you need a String instead of a substring
    let stringID = String(dataString[range])
    print("stringID:", stringID)
}
  

Это выведет

Идентификатор: 2b2a2abc-5962-4290- 92b4-773025ffd50b

stringID: 2b2a2abc-5962-4290- 92b4-773025ffd50b


Обратите внимание, что ваш код приведет к "2b2a2abc-5962-4290-92b4-773025ffd50


редактировать /обновление2:

Учитывая, что ваша строка имеет формат JSON, вы можете просто декодировать свой идентификатор строки:

 struct Root: Codable {
    let id: String
    enum CodingKeys: String, CodingKey {
        case id = "ID"
    }
}
do {
    let id = try JSONDecoder().decode(Root.self, from: Data(dataString.utf8)).id
    print(id)  // "2b2a2abc-5962-4290-92b4-773025ffd50b"
} catch {
    print(error)
}
  

Если вам нужно расшифровать все ваши данные:

 struct Root: Codable {
    let odataContext, id: String
    let points: Points
    let geofenceName, geofenceType, privacy: String
    let centerLat, centerLong, zoomLat, zoomLong: Double
    let pathTolerance, entranceTolerance: Int

    enum CodingKeys: String, CodingKey {
        case odataContext = "@odata.context", id = "ID", points = "Points", geofenceName = "GEOFENCE_NAME", geofenceType = "GEOFENCE_TYPE", privacy = "PRIVACY", centerLat = "CENTER_LAT", centerLong = "CENTER_LONG", zoomLat = "ZOOM_LAT", zoomLong = "ZOOM_LONG", pathTolerance = "PATH_TOLERANCE", entranceTolerance = "ENTRANCE_TOLERANCE"
    }
}
  

 struct Points: Codable {
    let pointType: String
    let pointNum: Int
    let latitude, longitude: Double
    let parentID: String

    enum CodingKeys: String, CodingKey {
        case pointType = "POINT_TYPE", pointNum = "POINT_NUM", latitude = "LATITUDE", longitude = "LONGITUDE", parentID = "parent_ID"
    }
}
  

 do {
    let root = try JSONDecoder().decode(Root.self, from: Data(dataString.utf8))
    print("ID:", root.id)  // ID: 2b2a2abc-5962-4290-92b4-773025ffd50b
    print("Root:", root)   // Root: Root(odataContext: "$metadata#GeoFences(Points())/$entity", id: "2b2a2abc-5962-4290-92b4-773025ffd50b", points: __lldb_expr_111.Points(pointType: "F", pointNum: 0, latitude: 32.92197686725423, longitude: -117.04306932263532, parentID: "2b2a2abc-5962-4290-92b4-773025ffd50b"), geofenceName: "New Fence", geofenceType: "O", privacy: "X", centerLat: 32.92043316309709, centerLong: -117.04286922250975, zoomLat: 0.0062387973505337885, zoomLong: 0.005345531926053582, pathTolerance: 5, entranceTolerance: 5)
} catch {
    print(error)
}
  

Комментарии:

1. Спасибо. Моя репутация слишком мала, чтобы показывать мои голоса, но я работаю над этим.

2. Спасибо, что вставили расшифровку. Я обновлю код, чтобы использовать его.

Ответ №2:

Вы можете использовать range(of:) в строке, чтобы найти range значение подстроки, а затем использовать upperBound это range значение для индексации исходной строки, чтобы получить подстроку, начинающуюся с этой точки. Наконец, prefix(5) позволяет захватить первые 5 символов этой подстроки и String() превращает Substring.Subsequence обратно в обычный String :

 if let range = tempString.range(of: "ID: ") {
    let ident = String(tempString[range.upperBound...].prefix(5))
    print(ident)
}
  

Примечание: range(of:) берется из фреймворка Foundation, поэтому вам нужен import Foundation или import другой фреймворк, который использует Foundation такие как UIKit или Cocoa .

Комментарии:

1. Это буквально то, о чем просил OP. Если диапазон не нужен, OP также может выполнить if let index = tempString.range(of: "ID: ")?.upperBound {

2. @LeoDabus, правда, я тоже рассматривал возможность написания таким образом.

3. Вау. Элегантное решение. Намного лучше, чем у меня. Мне пришлось выполнить некоторые экранирования: dataString.range(of: «ID «: «») Спасибо.

4. @NYReno вы можете использовать #"whatever"# вместо экранирования вашей строки #"ID":""#

Ответ №3:

Для вашего случая можно использовать NSRegularExpression:

 let tempString1 = "What? The ID: 54673 Over there"
let tempString2 = "Jump... ID: 4561E how high"

func matches(for regex: String, in text: String) -> [String] {
    do {
        let regex = try NSRegularExpression(pattern: regex)
        let results = regex.matches(in: text,
                                    range: NSRange(text.startIndex..., in: text))
        return results.map {
            String(text[Range($0.range, in: text)!])
        }
    } catch let error {
        print("invalid regex: (error.localizedDescription)")
        return []
    }
}

var res1 = matches(for: "[0-9] [A-Z]*", in: tempString1) // 54673
var res2 = matches(for: "[0-9] [A-Z]*", in: tempString2) // 4561E
  

Комментарии:

1. Это регулярное выражение очень мягкое. Его не волнует, предшествует ли цифрам «ID:» и / или скольким цифрам или буквам это будет соответствовать.

Ответ №4:

Хорошо, я начал это упражнение, потому что мне было лень расшифровывать JSON, потому что мне нужно было построить структуры и получить их правильно. Итак, я подумал, что мог бы быстро создать подстроку. Что ж, декодирование было бы лучше, быстрее и точнее, но я покажу свой код ниже. В принципе, я хотел получить идентификатор из строки JSON:

 {
  "@odata.context": "$metadata#GeoFences(Points())/$entity",
  "ID": "2b2a2abc-5962-4290-92b4-773025ffd50b",
  "Points": {
    "POINT_TYPE": "F",
    "POINT_NUM": 0,
    "LATITUDE": 32.92197686725423,
    "LONGITUDE": -117.04306932263532,
    "parent_ID": "2b2a2abc-5962-4290-92b4-773025ffd50b"
  },
  "GEOFENCE_NAME": "New Fence",
  "GEOFENCE_TYPE": "O",
  "PRIVACY": "X",
  "CENTER_LAT": 32.92043316309709,
  "CENTER_LONG": -117.04286922250975,
  "ZOOM_LAT": 0.006238797350533787,
  "ZOOM_LONG": 0.005345531926053582,
  "PATH_TOLERANCE": 5,
  "ENTRANCE_TOLERANCE": 5
}
  

Мой код выглядит следующим образом. Скоро я заменю ее на decode. переменная id действительно содержит идентификатор.

 var foundI :Bool = false
var iIndex = 0
for (index, char) in dataString.enumerated() {
    if char == "I" {
        foundI = true
        iIndex = index
    } else if char == "D" {
        if foundI == true amp;amp; index == iIndex 1 {
           var suffixStr =  String(dataString.dropFirst(iIndex 5))
           var id = String(suffixStr.prefix(36))
        
            print("New String (id)")
        }
    }
}
  

Комментарии:

1. проверьте мой пост о подходе регулярных выражений, чтобы найти идентификатор

2. Это привело бы к «561E» для вашей второй строки в вашем сообщении

3. Да, вы правы, поскольку я не обрабатывал escape-символы. Спасибо. vacawama: если пусть range = dataString.range(of: «ID»:»») { пусть ident = String(dataString[range.upperBound…].prefix(36)) печать(ident) }