2015-04-20 1 views
4

Je suis en train de faire un match simple regex utilisant NSRegularExpression, mais je vais avoir quelques problèmes correspondant à la chaîne lorsque la source contient des caractères multi-octets:correspondant Swift Regex échoue lorsque la source contient des caractères unicode

let string = "D 9" 

// The following matches (any characters)(SPACE)(numbers)(any characters) 
let pattern = "([\\s\\S]*) ([0-9]*)(.*)" 

let slen : Int = string.lengthOfBytesUsingEncoding(NSUTF8StringEncoding) 

var error: NSError? = nil 

var regex = NSRegularExpression(pattern: pattern, options: NSRegularExpressionOptions.DotMatchesLineSeparators, error: &error) 

var result = regex?.stringByReplacingMatchesInString(string, options: nil, range: NSRange(location:0, 
length:slen), withTemplate: "First \"$1\" Second: \"$2\"") 

Le code ci-dessus renvoie « D » et « 9 » comme prévu

Si je change maintenant la première ligne d'inclure un symbole monétaire « Livre » au Royaume-Uni comme suit:

let string = "£ 9" 

Alors e Le match ne fonctionne pas, même si la partie ([\\s\\S]*) de l'expression doit toujours correspondre à caractères principaux.

Je comprends que le symbole £ prendra deux octets, mais la correspondance générique ignorera ceux qui ne le devraient pas? Quelqu'un peut-il expliquer ce qui se passe ici s'il vous plaît?

+0

Je ne suis pas familier avec Swift et son moteur de regex, mais en général je serais terriblement surpris de constater que '\ s \ S' ne correspond pas à' .' quand Unicode est impliqué. Pourquoi n'utilisez-vous pas '. *' Dans le premier groupe? Cela dit, je ne suis pas entièrement convaincu que c'est là où le problème est; Je pense qu'il est plus probable que '[0-9]' ne corresponde pas aux chiffres unicode que '\ S' ne corresponde pas aux caractères unicode non spatiaux arbitraires. –

+0

Swift * supporte * la classe de caractères '\ d', alors pourquoi utilisez-vous' [0-9] '? Si vous essayez de faire correspondre avec '(. *) (\ D *) (. *)', Obtenez-vous une correspondance? –

+0

Merci Kyle. J'utilisais \ s \ S à cause d'une mauvaise lecture d'un article sur le mauvais usage du '.' personnage. Je l'ai changé pour "(. *) (\ D *) (. *)" Mais il échoue toujours à correspondre. Je commence à soupçonner que c'est un bug dans l'implémentation de Swift - tout autre caractère correspond à OK - par exemple. "D $ + @ 9" mais quand je mets un symbole '£' n'importe où dans la chaîne à faire correspondre, ça échoue! –

Répondre

8

Cela peut prêter à confusion. Le premier paramètre de stringByReplacingMatchesInString() est mappé de NSString en Objective-C à String dans Swift, mais le paramètre range: est toujours et NSRange. Par conséquent, vous devez spécifier la plage dans les unités utilisées par NSString (ce qui est le nombre de points de code UTF-16):

var result = regex?.stringByReplacingMatchesInString(string, 
     options: nil, 
     range: NSRange(location:0, length:(string as NSString).length), 
     withTemplate: "First \"$1\" Second: \"$2\"") 

vous pouvez également utiliser count(string.utf16) au lieu de (string as NSString).length.

Exemple complet:

let string = "£ 9" 

let pattern = "([\\s\\S]*) ([0-9]*)(.*)" 
var error: NSError? = nil 
let regex = NSRegularExpression(pattern: pattern, 
     options: NSRegularExpressionOptions.DotMatchesLineSeparators, 
     error: &error)! 

let result = regex.stringByReplacingMatchesInString(string, 
    options: nil, 
    range: NSRange(location:0, length:(string as NSString).length), 
    withTemplate: "First \"$1\" Second: \"$2\"") 
println(result) 
// First "£" Second: "9" 
+0

Merci Martin - cela explique pourquoi la longueur de la chaîne contenant le symbole monétaire a été signalé comme 4 plutôt que 3. Il rapporte la longueur correctement maintenant, mais l'expression ne correspond toujours pas, j'ai peur. –

+1

Mes excuses Martin - doit être trop tôt le matin - votre solution a travaillé !! Merci beaucoup!! :-) –