Je rencontre un problème que je ne sais pas comment résoudre et j'espère que quelqu'un ici pourra m'aider. Actuellement, j'ai une variable de chaîne et plus tard, je remplace les lettres de la chaîne par des traits de soulignement comme suit:
str = "H__l_ ___yg_____" str = "_____ play______" str = "__ll_ ____g____d"
Sachez que je voudrais aléatoirement générer 25% des caractères dans str (Dans ce cas 16 * 0,25 = 4) donc il affiche plus tard quelque chose comme ces exemples:
var str = "Hello playground" let replace = str.replacingOccurrences(of: "\\S", with: "_", options: .regularExpression) print(str)
Quelqu'un at-il des idées sur la façon de faire cela?
9 Réponses :
De la même façon que dans Remplacer des caractères spécifiques dans la chaîne , vous pouvez mappez chaque caractère et combinez le résultat en une chaîne. Mais maintenant, vous devez garder une trace des nombres (restants) de caractères non-espace et des nombres (restants) de caractères qui doivent être affichés. Pour chaque caractère (sans espace), il est décidé au hasard de l'afficher (de le conserver) ou de le remplacer par un trait de soulignement.
let s = "Hello playground" let factor = 0.25 var n = s.filter({ $0 != " " }).count // # of non-space characters var m = lrint(factor * Double(n)) // # of characters to display let t = String(s.map { c -> Character in if c == " " { // Preserve space return " " } else if Int.random(in: 0..<n) < m { // Keep m -= 1 n -= 1 return c } else { // Replace n -= 1 return "_" } }) print(t) // _e_l_ ______o_n_
Une solution possible:
$>Before: Hello playground $>After: ____o ___y____n_ $>Before: Hello playground $>After: _el__ _______u__ $>Before: Hello playground $>After: _e___ ____g___n_ $>Before: Hello playground $>After: H___o __a_______ $>Before: Hello playground $>After: H___o _______u__ $>Before: Hello playground $>After: __l__ _____ro___ $>Before: Hello playground $>After: H____ p________d $>Before: Hello playground $>After: H_l__ _l________ $>Before: Hello playground $>After: _____ p____r__n_ $>Before: Hello playground $>After: H___o _____r____ $>Before: Hello playground $>After: __l__ ___y____n_
L'idée derrière cela:
Utilisez le même modèle d'expression régulière que celui que vous avez utilisé.
Ramassez n éléments dedans (dans votre cas 1/4)
Remplacez tous les caractères qui ne figurent pas dans cette courte liste.
Maintenant que vous avez l'idée, il est encore plus rapide de remplacer la boucle for par
for aMatch in randomElementsToReplace { str.replaceSubrange(Range(aMatch.range, in: str)!, with: "_") }
Merci au commentaire de @Martin R pour l'avoir signalé. p >
Résultat (fait 10 fois):
var str = "Hello playground" print("Before: \(str)") do { let regex = try NSRegularExpression(pattern: "\\S", options: []) let matches = regex.matches(in: str, options: [], range: NSRange(location: 0, length: str.utf16.count)) //Retrieve 1/4 elements of the string let randomElementsToReplace = matches.shuffled().dropLast(matches.count * 1/4) matches.forEach({ (aMatch) in if randomElementsToReplace.first(where: { $0.range == aMatch.range } ) != nil { str.replaceSubrange(Range(aMatch.range, in: str)!, with: "_") } else { //Do nothing because that's the one we are keeping as such } }) print("After: \(str)") } catch { print("Error while creating regex: \(error)") }
Vous verrez qu'il y a une petite différence par rapport à votre résultat attendu, c'est parce que correspond.count == 15, donc 1/4 d'entre eux devrait être quoi? A vous là de faire le bon calcul en fonction de vos besoins (arrondir ?, etc.) puisque vous ne l'avez pas précisé.
Notez que si vous ne voulez pas arrondir, vous pourrait aussi faire l'inverse, utiliser l'aléatoire pour celui à ne pas remplacer, et alors le tour pourrait jouer en votre faveur.
Plus simple (?): for aMatch in randomElementsToReplace {str.replaceSubrange (Range (aMatch.range, in: str) !, with: "_")}
En effet, je me suis concentré sur l'explication de l'idée sous-jacente que je codais comme tel et non pas comme simplifié.
Est-ce que for in
est vraiment plus rapide que .forEach
?
C'est juste que j'ai fait un d'abord (où :)
des correspondances
à chaque itération, mais comme nous avons le NSTextChekingResult
que nous voulons déjà, nous besoin de les parcourir.
Une autre approche possible consiste à générer des index aléatoires pour la chaîne donnée, puis à remplacer les caractères de ces index:
extension String { func randomUnderscores(factor: Double) -> String { let indexes: [Int] = Array(0..<count) let endIndexes = Int(Double(count) * factor) let randomIndexes = Array(indexes.shuffled()[0..<endIndexes]) var randomized = self for index in randomIndexes { let start = randomized.index(startIndex, offsetBy: index) let end = randomized.index(startIndex, offsetBy: index+1) randomized.replaceSubrange(start..<end, with: "_") } return randomized } } print(str.randomUnderscores(factor: 0.25))
Si vous mettez ceci dans une extension sur String, cela ressemblerait à:
var str = "Hello, playground" let indexes: [Int] = Array(0..<str.count) let randomIndexes = Array(indexes.shuffled()[0..<(str.count / 4)]) for index in randomIndexes { let start = str.index(str.startIndex, offsetBy: index) let end = str.index(str.startIndex, offsetBy: index+1) str.replaceSubrange(start..<end, with: "_") } print(str)
Notez que votre code peut permettre le remplacement de la virgule, de l'espace, pas seulement des lettres (légères différences par rapport aux besoins de l'auteur).
Cette méthode crée un tableau de booléens qui détermine quels caractères seront conservés et lesquels seront remplacés en utilisant la fonction intégrée shuffled
.
let string = "Hello playground and stackoverflow" let nonSpaces = string.filter{ $0 != " " }.count let bools = (Array<Bool>(repeating: true, count: nonSpaces / 4) + Array<Bool>(repeating: false, count: nonSpaces - nonSpaces / 4)).shuffled() var nextBool = bools.makeIterator() let output = string.map { char in return char == " " ? " " : (nextBool.next()! ? char : "_") } print(String(output)) // Hel__ __________ a__ __a____e____w // ___l_ _l__g_____ _n_ __a_____r__o_
Modifier Ce qui précède ne traite pas correctement les espaces, mais je vais quand même le laisser ici à titre d'exemple général.
Voici une version qui traite les espaces.
let string = "Hello playground" let charsToKeep = string.count / 4 let bools = (Array<Bool>(repeating: true, count: charsToKeep) + Array<Bool>(repeating: false, count: string.count - charsToKeep)).shuffled() let output = zip(string, bools).map { char, bool in return bool ? char : "_" } print(String(output))
Je viens de proposer la solution suivante:
let str = "Hello playground" print(generateMyString(string: str)) // ___lo _l_______d
Sortie:
func generateMyString(string: String) -> String { let percentage = 0.25 let numberOfCharsToReplace = Int(floor(Double(string.count) * percentage)) let generatedString = stride(from: 0, to: string.count, by: 1).map { index -> String in return string[string.index(string.startIndex, offsetBy: index)] == " " ? " " : "_" }.joined() var newString = generatedString for i in generateNumbers(repetitions: numberOfCharsToReplace, maxValue: string.count - 1) { var newStringArray = Array(newString) newStringArray[i] = Array(string)[i] newString = String(newStringArray) } return newString } func generateNumbers(repetitions: Int, maxValue: Int) -> [Int] { guard maxValue >= repetitions else { fatalError("maxValue must be >= repetitions for the numbers to be unique") } var numbers = [Int]() for _ in 0..<repetitions { var n: Int repeat { n = Int.random(in: 1...maxValue) } while numbers.contains(n) numbers.append(n) } return numbers }
Je pensais à la même solution, mais votre solution ne couvre pas les espaces. Et comme le nombre aléatoire de Swift 4.2 peut être simplement n = Int.random (in: 1 ... maxValue)
@RobertDresler Bon point pour le n = Int.random (in: 1 ... maxValue)
. Je ne sais pas ce que vous entendez par "votre solution ne couvre pas les espaces" ... Merci.
mes compétences en anglais ...: D Je veux dire, votre solution remplace également les espaces par des traits de soulignement. Et je pense que OP ne veut pas de ça.
@RobertDresler Merci encore :) Edité.
L'idée est la même que celle des méthodes ci-dessus, avec juste un peu moins de code.
var str = "Hello playground" print(randomString(str)) print(randomString(str)) // counting whitespace as a random factor func randomString(_ str: String) -> String{ let strlen = str.count let effectiveCount = Int(Double(strlen) * 0.25) let shuffled = (0..<strlen).shuffled() return String(str.enumerated().map{ shuffled[$0.0] < effectiveCount || ($0.1) == " " ? ($0.1) : "_" })} //___l_ _l__gr____ //H____ p___g____d func underscorize(_ str: String) -> String{ let effectiveStrlen = str.filter{$0 != " "}.count let effectiveCount = Int(floor(Double(effectiveStrlen) * 0.25)) let shuffled = (0..<effectiveStrlen).shuffled() return String((str.reduce(into: ([],0)) { $0.0.append(shuffled[$0.1] <= effectiveCount || $1 == " " ? $1 : "_" ) $0.1 += ($1 == " ") ? 0 : 1}).0) } print(underscorize(str)) print(underscorize(str)) //__l__ pl__g_____ //___lo _l_______d
Une solution qui garde les espaces et la ponctuation intacts.
Nous les trouverons avec une méthode d'extension indiciesOfPuntationBlanks () -> [Int]
. le remplacement des caractères choisis au hasard se fera par blankOut (pourcentage: Double) -> String
_el_o, _____! __llo, _____! He__o, _____! _e___, W_r__! _el_o, _____! _el__, ___l_! _e___, __rl_! _e__o, _o___! H____, Wo___! H____, __rl_! -------------------- xxxlx,xWxrxx! xxxxx,xxorxd! Hxxxx,xWxrxx! xxxxx, xoxlx! Hxllx,xxxxxx! xelxx,xxoxxx! Hxxxx,xWxxxd! Hxxxo,xxxxxd! Hxxxx,xxorxx! Hxxxx, Wxxxx! -------------------- ***l***Wo**d* *e**o**W**l** ***lo**Wo**** *el*****or*** H****,****ld* ***l*, **r*** *el*o* ****** *e*lo*******! H*l****W***d* H****, **r***
Utilisation:
let str = "Hello, World!" for _ in 0 ..< 10 { print(str.blankOut(percentage: 0.75)) } print("--------------------") for _ in 0 ..< 10 { print(str.blankOut(percentage: 0.75, with:"x", ignore: [.punctuationCharacters])) } print("--------------------") for _ in 0 ..< 10 { print(str.blankOut(percentage: 0.75, with:"*", ignore: [])) }
désolé, je ne l'ai pas décliné. vous pouvez le modifier un peu. afin que je puisse le voter.
@ E.Coms: bien sûr, pas de problème
Vous pouvez utiliser un algorithme en 3 étapes qui effectue les opérations suivantes:
Le code pourrait ressembler à ceci:
____o p_ay______ ____o p__y____n_ _el_o p_________
Exemples de résultats:
func underscorize(_ str: String, factor: Double) -> String { // making sure we have a factor between 0 and 1 let factor = max(0, min(1, factor)) let nonSpaceIndices = str.enumerated().compactMap { $0.1 == " " ? nil : $0.0 } let replaceIndices = nonSpaceIndices.shuffled().dropFirst(Int(Double(str.count) * factor)) return String(str.enumerated().map { replaceIndices.contains($0.0) ? "_" : $0.1 }) } let str = "Hello playground" print(underscorize(str, factor: 0.25))
Vous devez d'abord obtenir les indices de votre chaîne et filtrer ceux qui sont des lettres. Ensuite, vous pouvez mélanger le résultat et choisir le nombre d'éléments (%) moins le nombre d'espaces dans la chaîne d'origine, parcourir le résultat en remplaçant les plages résultantes par le trait de soulignement. Vous pouvez étendre le protocole RangeReplaceable pour pouvoir également l'utiliser avec des sous-chaînes:
// mutating test var str = "Hello playground" str.randomReplace(percentage: 0.75) // "___lo _l___r____\n" print(str) // "___lo _l___r____\n" // non mutating with another character let str2 = "Hello playground" str2.randomReplacing(percentage: 0.75, with: "â¢") // "â¢â¢â¢â¢o pâ¢â¢yâ¢â¢â¢uâ¢â¢" print(str2) // "Hello playground\n"
extension StringProtocol where Self: RangeReplaceableCollection{ mutating func randomReplace(characterSet: CharacterSet = .letters, percentage: Double, with element: Element = "_") { precondition(0...1 ~= percentage) let indices = self.indices.filter { characterSet.contains(self[$0].unicodeScalars.first!) } let lettersCount = indices.count let nonLettersCount = count - lettersCount let n = lettersCount - nonLettersCount - Int(Double(lettersCount) * Double(1-percentage)) indices .shuffled() .prefix(n) .forEach { replaceSubrange($0...$0, with: Self([element])) } } func randomReplacing(characterSet: CharacterSet = .letters, percentage: Double, with element: Element = "_") -> Self { precondition(0...1 ~= percentage) var result = self result.randomReplace(characterSet: characterSet, percentage: percentage, with: element) return result } }
Vous pouvez utiliser un
NSRegularExpression
, obtenir toutes les correspondances, et à l'intérieur, en choisir 3/4 et les remplacer par "_".Doit-il être exactement 25%?