Dividir text en matriz mientras se mantiene la puntuación en Swift

Quiero dividir el text en una matriz, manteniendo la puntuación separada por el rest de las palabras, por lo que una cadena como:

Hello, I am Albert Einstein. 

debería convertirse en una matriz como esta:

 ["Hello", ",", "I", "am", "Albert", "Einstein", "."] 

He intentado con sting.components(separatedBy: CharacterSet.init(charactersIn: " ,;;:")) pero este método elimina todas las puntuaciones y devuelve una matriz como esta:

 ["Hello", "I", "am", "Albert", "Einstein"] 

Entonces, ¿cómo puedo get una matriz como mi primer ejemplo?

No es una solución hermosa, pero puedes probar con:

 var str = "Hello, I am Albert Einstein." var list = [String]() var currentSubString = ""; //enumerate to get all characters including ".", ",", ";", " " str.enumerateSubstrings(in: str.startIndex..<str.endIndex, options: String.EnumerationOptions.byComposedCharacterSequences) { (substring, substringRange, enclosingRange, value) in if let _subString = substring { if (!currentSubString.isEmpty && (_subString.compare(" ") == .ordenetworkingSame || _subString.compare(",") == .ordenetworkingSame || _subString.compare(".") == .ordenetworkingSame || _subString.compare(";") == .ordenetworkingSame ) ) { //create word if see any of those character and currentSubString is not empty list.append(currentSubString) currentSubString = _subString.trimmingCharacters(in: CharacterSet.whitespaces ) } else { //add to current sub string if current character is not space. if (_subString.compare(" ") != .ordenetworkingSame) { currentSubString += _subString } } } } //last word if (!currentSubString.isEmpty) { list.append(currentSubString) } 

En Swift3:

 var str = "Hello, I am Albert Einstein." var list = [String]() var currentSubString = ""; //enumerate to get all characters including ".", ",", ";", " " str.enumerateSubstrings(in: str.startIndex..<str.endIndex, options: String.EnumerationOptions.byComposedCharacterSequences) { (substring, substringRange, enclosingRange, value) in if let _subString = substring { if (!currentSubString.isEmpty && (_subString.compare(" ") == .ordenetworkingSame || _subString.compare(",") == .ordenetworkingSame || _subString.compare(".") == .ordenetworkingSame || _subString.compare(";") == .ordenetworkingSame ) ) { //create word if see any of those character and currentSubString is not empty list.append(currentSubString) currentSubString = _subString.trimmingCharacters(in: CharacterSet.whitespaces ) } else { //add to current sub string if current character is not space. if (_subString.compare(" ") != .ordenetworkingSame) { currentSubString += _subString } } } } //last word if (!currentSubString.isEmpty) { list.append(currentSubString) } 

La idea es hacer un ciclo para todos los personajes y crear palabras al mismo time. Una palabra es un grupo de carácter consecutivo que no es . o ; . Por lo tanto, durante la creación de word in loop, terminamos la palabra actual si vemos uno de esos caracteres, y la palabra actual en construcción no está vacía. Para desglosar los pasos con tu input:

  1. get H (no espacio ni otro carácter de terminal) -> currentSubString = "H"
  2. get e (no espacio ni otro carácter de terminal) -> currentSubString = "He"
  3. get l (no espacio ni otro carácter terminal) -> currentSubString = "Hel"
  4. get l (no espacio ni ningún otro carácter de terminal) -> currentSubString = "Hell"
  5. get o (no espacio ni otro carácter de terminal) -> currentSubString = "Hello"
  6. get (es un personaje terminal)
    • -> como currentSubString no está vacío, agregue a la list y reinicie la construcción para la siguiente palabra, luego list = ["Hello"]
    • -> currentSubString = "." (la razón por la que usé el recorte es solo para eliminar si consigo este personaje pero para otro carácter terminal, debemos mantener la siguiente palabra.
  7. get (es el carácter del espacio)
    • -> como currentSubString no está vacío, agregue a la list y reinicie la construcción -> list = ["Hello", "."]
    • -> currentSubString = "" (recortado). … y así.

Para explicar desde mi comentario … Piensa en expresiones regulares como una manera de encontrar patrones en Cuerdas. En su caso, el patrón es palabras (grupos de letras) con otros símbolos posibles (signos de puntuación) en el medio.

Tome la expresión regular en mi comentario (que he ampliado un poco aquí), por ejemplo: ([,\.\:\"])*([A-Za-z0-9\']*)([,\.\:\"])*

Ahí tenemos 3 groups . La primera busca símbolos (como una comilla principal). El segundo es search letras, numbers y un apóstrofo (porque a las personas les gusta concatenar palabras, como "yo soy"). y el tercer grupo busca cualquier signo de puntuación final.

Editar para notar: los grupos en lo anterior se indican con paréntesis (y), mientras que los corchetes [y] indican caracteres aceptables para una búsqueda. Entonces, por ejemplo, [AZ] dice que todas las letras mayúsculas de AZ son aceptables. [A-Za-z] permite get tanto la parte superior como la inferior, mientras que [A-Za-z0-9] incluye todas las letras y numbers de 0-9. Por supuesto, existen versiones taquigráficas para escribir esto, pero las descubrirás en el futuro.

Entonces, ahora tenemos una manera de separar todas las palabras y los signos de puntuación, ahora necesitas usarlo, haciendo algo similar a:

 func find(value: NSString) throws -> [NSString] { let regex = try NSRegularExpression(pattern: "([,\\.\\:\\\"])*([A-Za-z0-9\\']*)([,\\.\\:\\\"])*") // Notice you have to escape the values in code let results = regex.matches(in: value, range: NSRange(location: 0, length: nsString.length)) return results.map({ value.substring(with: $0.range) }).filter({ $0 != nil }) } 

Eso debería darle a cada grupo no-nulo que se encuentre dentro del valor de cadena que proporciona al método.

Por supuesto, ese último método de filter puede no ser necesario, pero no estoy lo suficientemente familiarizado con cómo Swift maneja expresiones regulares para saberlo con certeza.

Pero eso definitivamente debería apuntarte en la dirección correcta …

Alegrías