Vous utilisez str_replace pour qu'il n'agisse que sur la première correspondance?
325
Je veux une version str_replace()qui ne remplace que la première occurrence de $searchdans le $subject. Existe-t-il une solution simple à ce problème ou ai-je besoin d'une solution hacky?
L'inconvénient de cette méthode est la pénalité de performance des expressions régulières.
zombat
27
Un autre inconvénient est que vous devez utiliser preg_quote () sur "l'aiguille" et échapper les méta-caractères $ et \ dans le remplacement.
Josh Davis
32
Cela échoue en tant que solution générique en raison de problèmes d'échappement méchants.
Jeremy Kauffman
2
Trop souvent, les expressions régulières sont ignorées en raison de la «performance», si la performance était la principale préoccupation, nous n'écririons pas PHP! Quelque chose d'autre que «/» pourrait être utilisé pour encapsuler le motif, peut-être «~», ce qui aiderait à éviter le problème de fuite dans une certaine mesure. Cela dépend de ce que sont les données et d'où elles proviennent.
ThomasRedstone
1
Mis à part les performances, ceux qui se plaignent d'échapper à des problèmes ont-ils quelque chose de spécifique à l'esprit, en plus des bogues potentiels preg_quote? Par exemple, @ThomasRedstone craint que le délimiteur /ne soit dangereux s'il apparaît $from, mais heureusement ce n'est pas le cas: il est correctement échappé à cause du preg_quotesecond paramètre de (on peut facilement le tester). Je serais intéressé d'entendre parler de problèmes spécifiques (qui seraient de graves bogues de sécurité PCRE dans mon livre).
MvanGeest
611
Il n'y en a pas de version, mais la solution n'est pas du tout hacky.
Peut être beaucoup plus rapide et utilise moins de mémoire que les expressions régulières. Aucune idée de la raison pour laquelle quelqu'un voterait contre ...
Josh Davis
12
J'aime cette approche, mais le code a une erreur, le dernier paramètre de l'appel substr_replace devrait être strlen ($ needle) au lieu de strlen ($ replace) .. s'il vous plaît méfiez-vous de cela !!
Nelson
C'est "hacky" en ce sens qu'il faut beaucoup plus de temps pour comprendre ce qui se passe. De plus, s'il s'agissait d'un code clair, il n'aurait pas été mentionné que le code contient une erreur. S'il est possible de faire une erreur dans un si petit extrait, c'est déjà beaucoup trop hacky.
Camilo Martin
9
Je ne suis pas d'accord avec @CamiloMartin en ce qui concerne le nombre de lignes par rapport à la possibilité d'erreurs. Bien qu'il substr_replaces'agisse d'une fonction quelque peu difficile à utiliser en raison de tous les paramètres, le vrai problème est que la manipulation de chaînes par des nombres est parfois délicate - vous devez faire attention à transmettre la bonne variable / décalage aux fonctions. J'irais même jusqu'à dire que le code ci-dessus est l'approche la plus simple et la plus logique pour moi.
Alex
1
Une approche brillante. Fonctionne parfaitement lors du remplacement de valeurs de variables qui ont réservé des caractères regex (donc preg_replace est bear). C'est simple et élégant.
Praesagus
96
Modifier: les deux réponses ont été mises à jour et sont maintenant correctes. Je vais laisser la réponse car les timings de fonction sont toujours utiles.
Les réponses de «zombat» et «trop de php» ne sont malheureusement pas correctes. Ceci est une révision de la réponse publiée par zombat (car je n'ai pas assez de réputation pour poster un commentaire):
Notez le strlen ($ needle), au lieu de strlen ($ replace). L'exemple de Zombat ne fonctionnera correctement que si l'aiguille et le remplacement sont de la même longueur.
Voici la même fonctionnalité dans une fonction avec la même signature que str_replace de PHP:
Notez le 2 à la fin au lieu de 1. Ou au format de fonction:
function str_replace_first($search, $replace, $subject){return implode($replace, explode($search, $subject,2));}
J'ai chronométré les deux fonctions et la première est deux fois plus rapide lorsqu'aucune correspondance n'est trouvée. Ils ont la même vitesse lorsqu'un match est trouvé.
Pourquoi ne pas généraliser ceci comme: str_replace_flexible ($ mixte, $ r mixte, décalage $ int, limite $ int) où la fonction remplace les occurrences $ limit à partir de la correspondance $ décalage (nième).
Adam Friedman
Dommage que cela ne s'applique qu'aux remplacements sensibles à la casse.
Merci pour cela, j'utilise généralement preg_replace car c'est le plus flexible si un ajustement futur est nécessaire dans la plupart des cas, 27% plus lent ne sera pas significatif
zzapper
@oLinkWebDevelopment Je serais intéressé de voir votre script de référence. Je pense que cela pourrait s'avérer utile.
Dave Morton
La raison pour laquelle substr_replace()remporte le résultat est simple; parce que c'est une fonction interne. Deux fonctions internes et définies par l'utilisateur faisant la même chose diffèrent dans les performances, car la fonction interne s'exécute dans les couches inférieures. Alors pourquoi pas preg_match()? Les expressions régulières sont presque plus lentes que chaque fonction de manipulation de chaîne interne, en raison de leur nation de recherche dans une chaîne plusieurs fois.
MAChitgarha
1
J'espère que la référence sur votre "gagnant" ( substr_replace($string, $replace, 0, strlen($search));) n'a pas simplement écrit cette statique 0. Une partie de la convolution des solutions non regex est qu'elles doivent "trouver" le point de départ avant de savoir où remplacer.
mickmackusa
55
Malheureusement, je ne connais aucune fonction PHP qui puisse le faire.
Vous pouvez rouler le vôtre assez facilement comme ceci:
function replace_first($find, $replace, $subject){// stolen from the comments at PHP.net/str_replace// Splits $subject into an array of 2 items by $find,// and then joins the array with $replacereturn implode($replace, explode($find, $subject,2));}
$search ='foo';
$replace ='bar';
$string ='foo wizard makes foo brew for evil foo and jack';
$limit =2;
$replaced = str_replace_limit($search, $replace, $string, $limit);
echo $replaced;// bar wizard makes bar brew for evil foo and jack
Bien que je préfère faire ===falseplutôt que d' is_bool(être plus explicite - je donne ce coup de pouce juste parce qu'il a évité la folie RegExp ! ... et en même temps ça marche et une solution propre ...
jave.web
Préférer une preg_solution facilement personnalisable n'est pas de la folie mais une préférence personnelle. return preg_replace('/'.preg_quote($search, '/').'/', $replace, $content, 1);est assez simple à lire pour les personnes qui ne craignent pas les regex. Besoin d'une recherche insensible à la casse? Ajoutez iaprès le délimiteur de motif de fin. Besoin d'un support unicode / multi-octets? Ajoutez uaprès le délimiteur de motif de fin. Besoin de prise en charge des limites de mots? Ajoutez des \bdeux côtés de votre chaîne de recherche. Si vous ne voulez pas d'expression régulière, n'utilisez pas d'expression régulière. Des chevaux pour les cours, mais certainement pas de la folie.
mickmackusa
3
Le moyen le plus simple serait d'utiliser une expression régulière.
L'autre façon est de trouver la position de la chaîne avec strpos () puis un substr_replace ()
Les réponses de code uniquement sont de faible valeur sur StackOverflow car elles font un mauvais travail d'éduquer / d'autonomiser des milliers de futurs chercheurs.
mickmackusa
3
=> LE CODE A ÉTÉ RÉVISÉ, alors considérez certains commentaires trop anciens
Et merci à tous de m'aider à améliorer cela
Tout BUG, veuillez me communiquer; Je vais arranger ça juste après
Alors, allons-y pour:
Remplacement du premier «o» par «ea» par exemple:
$s='I love you';
$s=str_replace_first('o','ea',$s);
echo $s;//output: I leave you
La fonction:
function str_replace_first($a,$b,$s){
$w=strpos($s,$a);if($w===false)return $s;return substr($s,0,$w).$b.substr($s,$w+strlen($a));}
Échoue si $ this a répété des caractères comme aaa vs aaaaaaaaa
Cristo
Je pense que ça devrait l'être substr($where,$b+strlen($this)), non substr($where,$b+1). Et je suppose que substr_replacec'est plus rapide.
Titus
Le code a été révisé, maintenant il fonctionne même pour les cordes longues
PYK
Cette solution ne fonctionne pas comme codée. Preuve: 3v4l.org/cMeZj Et lorsque vous corrigez le problème de dénomination des variables, cela ne fonctionne pas lorsque la valeur de recherche n'est pas trouvée - cela endommage la chaîne d'entrée. Preuve: 3v4l.org/XHtfc
mickmackusa
Est-il juste que quelqu'un demande à FIXER le code? @mickmackusa Pouvez-vous vérifier à nouveau s'il vous plaît?
PYK
2
$string ='this is my world, not my world';
$find ='world';
$replace ='farm';
$result = preg_replace("/$find/",$replace,$string,1);
echo $result;
C'est la même chose que la première réponse. En outre, vous devriez faire preg_quotedes $findavant de l' utiliser comme une expression.
Emil Vikström
c'est ce que j'ai utilisé, donc je l'ai voté. La première réponse a provoqué un conflit avec Drupal, elle doit avoir écrasé une fonction d'aide drupal. J'ai donc pris le code qui était à l'intérieur de la fonction et l'ai utilisé en ligne avec le reste du code ...
Dan Mantyla
Cette réponse uniquement codée fournit des conseils redondants sur la page (sans oublier qu'elle fait défaut preg_quote(). Cette réponse en double tardive peut être supprimée de la page en toute sécurité car ses conseils sont fournis par la réponse acceptée la plus ancienne et la plus élevée.
mickmackusa
2
Pour développer la réponse de @ renocor , j'ai écrit une fonction qui est 100% rétrocompatible avec str_replace(). Autrement dit, vous pouvez remplacer toutes les occurrences de str_replace()avec str_replace_limit()sans rien gâcher, même ceux qui utilisent des tableaux pour la $search, $replaceet / ou$subject .
La fonction pourrait être complètement autonome, si vous vouliez remplacer l'appel de fonction par ($string===strval(intval(strval($string)))), mais je le déconseille car valid_integer()c'est une fonction plutôt utile lorsque vous traitez avec des entiers fournis sous forme de chaînes.
Remarque: Dans la mesure du possible, str_replace_limit()utilisera à la str_replace()place, de sorte que tous les appels à str_replace()puissent être remplacés str_replace_limit()sans se soucier de la performance.
<?php
/**
* Checks if $string is a valid integer. Integers provided as strings (e.g. '2' vs 2)
* are also supported.
* @param mixed $string
* @return bool Returns boolean TRUE if string is a valid integer, or FALSE if it is not
*/function valid_integer($string){// 1. Cast as string (in case integer is provided)// 1. Convert the string to an integer and back to a string// 2. Check if identical (note: 'identical', NOT just 'equal')// Note: TRUE, FALSE, and NULL $string values all return FALSE
$string = strval($string);return($string===strval(intval($string)));}/**
* Replace $limit occurences of the search string with the replacement string
* @param mixed $search The value being searched for, otherwise known as the needle. An
* array may be used to designate multiple needles.
* @param mixed $replace The replacement value that replaces found search values. An
* array may be used to designate multiple replacements.
* @param mixed $subject The string or array being searched and replaced on, otherwise
* known as the haystack. If subject is an array, then the search and replace is
* performed with every entry of subject, and the return value is an array as well.
* @param string $count If passed, this will be set to the number of replacements
* performed.
* @param int $limit The maximum possible replacements for each pattern in each subject
* string. Defaults to -1 (no limit).
* @return string This function returns a string with the replaced values.
*/function str_replace_limit(
$search,
$replace,
$subject,&$count,
$limit =-1){// Set some defaults
$count =0;// Invalid $limit provided. Throw a warning.if(!valid_integer($limit)){
$backtrace = debug_backtrace();
trigger_error('Invalid $limit `'.$limit.'` provided to '.__function__.'() in '.'`'.$backtrace[0]['file'].'` on line '.$backtrace[0]['line'].'. Expecting an '.'integer', E_USER_WARNING);return $subject;}// Invalid $limit provided. Throw a warning.if($limit<-1){
$backtrace = debug_backtrace();
trigger_error('Invalid $limit `'.$limit.'` provided to '.__function__.'() in '.'`'.$backtrace[0]['file'].'` on line '.$backtrace[0]['line'].'. Expecting -1 or '.'a positive integer', E_USER_WARNING);return $subject;}// No replacements necessary. Throw a notice as this was most likely not the intended// use. And, if it was (e.g. part of a loop, setting $limit dynamically), it can be// worked around by simply checking to see if $limit===0, and if it does, skip the// function call (and set $count to 0, if applicable).if($limit===0){
$backtrace = debug_backtrace();
trigger_error('Invalid $limit `'.$limit.'` provided to '.__function__.'() in '.'`'.$backtrace[0]['file'].'` on line '.$backtrace[0]['line'].'. Expecting -1 or '.'a positive integer', E_USER_NOTICE);return $subject;}// Use str_replace() whenever possible (for performance reasons)if($limit===-1){return str_replace($search, $replace, $subject, $count);}if(is_array($subject)){// Loop through $subject values and call this function for each one.foreach($subject as $key => $this_subject){// Skip values that are arrays (to match str_replace()).if(!is_array($this_subject)){// Call this function again for
$this_function = __FUNCTION__;
$subject[$key]= $this_function(
$search,
$replace,
$this_subject,
$this_count,
$limit
);// Adjust $count
$count += $this_count;// Adjust $limit, if not -1if($limit!=-1){
$limit -= $this_count;}// Reached $limit, return $subjectif($limit===0){return $subject;}}}return $subject;} elseif(is_array($search)){// Only treat $replace as an array if $search is also an array (to match str_replace())// Clear keys of $search (to match str_replace()).
$search = array_values($search);// Clear keys of $replace, if applicable (to match str_replace()).if(is_array($replace)){
$replace = array_values($replace);}// Loop through $search array.foreach($search as $key => $this_search){// Don't support multi-dimensional arrays (to match str_replace()).
$this_search = strval($this_search);// If $replace is an array, use the value of $replace[$key] as the replacement. If// $replace[$key] doesn't exist, just an empty string (to match str_replace()).if(is_array($replace)){if(array_key_exists($key, $replace)){
$this_replace = strval($replace[$key]);}else{
$this_replace ='';}}else{
$this_replace = strval($replace);}// Call this function again for
$this_function = __FUNCTION__;
$subject = $this_function(
$this_search,
$this_replace,
$subject,
$this_count,
$limit
);// Adjust $count
$count += $this_count;// Adjust $limit, if not -1if($limit!=-1){
$limit -= $this_count;}// Reached $limit, return $subjectif($limit===0){return $subject;}}return $subject;}else{
$search = strval($search);
$replace = strval($replace);// Get position of first $search
$pos = strpos($subject, $search);// Return $subject if $search cannot be foundif($pos===false){return $subject;}// Get length of $search, to make proper replacement later on
$search_len = strlen($search);// Loop until $search can no longer be found, or $limit is reachedfor($i=0;(($i<$limit)||($limit===-1));$i++){// Replace
$subject = substr_replace($subject, $replace, $pos, $search_len);// Increase $count
$count++;// Get location of next $search
$pos = strpos($subject, $search);// Break out of loop if $needleif($pos===false){break;}}// Return new $subjectreturn $subject;}}
un peu gonflé si vous me demandez. Ce que je déteste le plus à cette solution, c'est la gestion des erreurs. Il casse le script si vous passez des valeurs incorrectes. Vous pensez qu'il a l'air professionnel, mais ce n'est pas le cas, au lieu d'une erreur, produisez plutôt un avis ou un avertissement. Il vaut mieux ignorer les conneries, renvoyer false à la place ou null et ne jamais utiliser de trace dans une fonction comme celle-ci. La meilleure solution est que le programmeur puisse décider quoi faire lorsque la sortie est incorrecte / inattendue.
Codebeat
@Erwinus Ceci est utilisé E_USER_WARNINGpartout, ce qui est un avertissement et non une erreur . La trace est extrêmement utile pour savoir quel code transmet les données non valides à la fonction en premier lieu (ce qui est absolument nécessaire pour traquer les bogues en production). Quant au retour $subjectau lieu de false/ nullou le lancement d'une erreur, c'était simplement un choix personnel pour mon cas d'utilisation. Pour faire correspondre str_replace()la fonctionnalité de, l'utilisation d'erreurs fatales capturables serait le meilleur pari (tout comme str_replace()lors de la fermeture des deux premiers arguments).
0b10011
Ah, je n'ai pas remarqué les E_USER_WARNING que vous utilisez, désolé pour cela. Le problème avec le retour du sujet est que vous ne pouvez jamais voir qu'il y avait quelque chose de mal, en dehors de la fonction. Cela dit, la fonction peut être la moitié de la taille si vous la faites plus intelligemment (c'est possible). Deuxièmement, les commentaires sont bons quand ils expliquent quelque chose de complexe mais pas très utile pour des choses simples comme augmenter une valeur. Dans l'ensemble, je pense que c'est inutile énorme. En outre, l'utilisation d'avertissements dans un environnement de production peut poser un problème de sécurité lorsque vous utilisez ce code sur un serveur qui ne supprime pas les messages d'exécution par défaut (journaux).
Codebeat
@Erwinus J'ai été prolixe en ce qui concerne les commentaires, car certaines personnes ne comprennent pas la langue aussi bien que d'autres, et les commentaires peuvent toujours être supprimés par ceux qui la comprennent. Si vous connaissez un meilleur moyen d'obtenir le même résultat final pour tous les cas marginaux, modifiez certainement la réponse. Et si votre environnement de production ne supprime pas les messages d'erreur, vous avez un problème plus important que cette fonction;)
0b10011
TL; DR Cet extrait est tellement gonflé que je ne peux pas imaginer le choisir plutôt qu'une fonction d'expression régulière (je déteste le défilement). Si vous souhaitez compter les remplacements effectués, il existe un paramètre pour cela dans preg_replace(). De plus, preg_replace()/ regex offre une gestion des limites des mots (si cela est souhaitable) - quelque chose que les fonctions non regex ne fourniront pas avec élégance.
mickmackusa
2
Selon mon résultat de test, je voudrais voter celui regular_express fourni par karim79. (Je n'ai pas assez de réputation pour le voter maintenant!)
La solution de zombat utilise trop d'appels de fonctions, je simplifie même les codes. J'utilise PHP 5.4 pour exécuter les deux solutions 100 000 fois, et voici le résultat:
$str ='Hello abc, have a nice day abc! abc!';
$pos = strpos($str,'abc');
$str = substr_replace($str,'123', $pos,3);
==> 1,85 sec
$str ='Hello abc, have a nice day abc! abc!';
$str = preg_replace('/abc/','123', $str,1);
==> 1,35 sec
Comme vous pouvez le voir. La performance de preg_replace n'est pas aussi mauvaise que beaucoup de gens le pensent. Je suggère donc la solution chic si votre express régulier n'est pas compliqué.
Votre premier extrait est une comparaison injuste car il n'utilise pas une implémentation correcte. Vous ne vérifiez pas $pospour false, donc quand l'aiguille n'existe pas dans la botte de foin, il endommagera la sortie.
mickmackusa
Merci @mickmackusa, vous avez raison. Mais ce n'est pas le but. J'ai dit que ce code est simplifié juste pour comparer l'efficacité des implémentations.
Hunter Wu
C'est exactement ce que je veux dire. Vous ne devez jamais faire de comparaisons de référence qui n'effectuent pas exactement le même processus. Comparer des pommes à des demi-oranges n'est pas utile. La mise en œuvre complète de l'approche non regex complète rendra la différence de vitesse plus profonde.
mickmackusa
Eh bien, merci encore. Mais ce que je veux, c'est trouver la meilleure mise en œuvre, pas faire plus de différence en profondeur.
Hunter Wu
2
Pour développer la réponse de zombat (que je pense être la meilleure réponse), j'ai créé une version récursive de sa fonction qui prend un $limitparamètre pour spécifier le nombre d'occurrences que vous souhaitez remplacer.
Remarque, il n'y a pas de contrôle de la qualité sur $start_pos, donc si elle est au - delà de la longueur de chaîne, cette fonction génère: Warning: strpos(): Offset not contained in string.... Cette fonction ne parvient pas à effectuer un remplacement lorsqu'elle $start_posest au-delà de la longueur. Preuve d'échec: 3v4l.org/qGuVIR ... Votre fonction peut combiner les return $haystackconditions et éviter de déclarer des variables à usage unique comme ceci: 3v4l.org/Kdmqp Cependant, comme je l'ai dit dans les commentaires ailleurs sur cette page, je préfère utiliser un appel très net, direct et non récursif preg_replace().
mickmackusa
oui pour que vous puissiez ajouter cette elsedéclaration de ligne$start_pos > strlen($haystack) ? $start_pos = strlen($haystack) : '';
Manojkiran.A
2
Pour une chaîne
$string ='OOO.OOO.OOO.S';
$search ='OOO';
$replace ='B';//replace ONLY FIRST occurance of "OOO" with "B"
$string = substr_replace($string,$replace,0,strlen($search));//$string => B.OOO.OOO.S//replace ONLY LAST occurance of "OOOO" with "B"
$string = substr_replace($string,$replace,strrpos($string,$search),strlen($search))//$string => OOO.OOO.B.S//replace ONLY LAST occurance of "OOOO" with "B"
$string = strrev(implode(strrev($replace),explode(strrev($search),strrev($string),2)))//$string => OOO.OOO.B.S
Pour un seul personnage
$string[strpos($string,$search)]= $replace;//EXAMPLE
$string ='O.O.O.O.S';
$search ='O';
$replace ='B';//replace ONLY FIRST occurance of "O" with "B"
$string[strpos($string,$search)]= $replace;//$string => B.O.O.O.S//replace ONLY LAST occurance of "O" with "B"
$string[strrpos($string,$search)]= $replace;// $string => B.O.O.B.S
Le premier extrait substr_replace () échoue lorsque la chaîne de recherche n'est pas au décalage 0 de la chaîne d'entrée. Preuve d'échec: 3v4l.org/oIbRv Et les deux substr_replace()techniques endommagent la chaîne d'entrée lorsque la valeur de recherche n'est pas présente. Preuve d'échec: 3v4l.org/HmEml (Et cette dernière technique avec tous les revappels est sérieusement alambiquée / dure pour les yeux.)
mickmackusa
2
En complément de ce que les gens ont dit, rappelez-vous que la chaîne entière est un tableau:
$string ="Lorem ipsum lá lá lá";
$string[0]="B";
echo $string;
Sauf s'il contient des caractères multi-octets ... et que votre technique échoue. Quel dommage que vous ayez proposé un exemple de chaîne d'entrée contenant á. Démonstration d'échec
mickmackusa
Vous pouvez vérifier si votre stringchaîne est multi-octets en utilisantmb_strlen($subject) != strlen($subject)
RousseauAlexandre
Ce message ne tente pas de répondre à la question posée.
En utilisant substr_replace, nous pouvons remplacer l'occurrence du premier caractère uniquement dans la chaîne. comme & est répété plusieurs fois mais seulement en première position nous devons remplacer & avec?
Si c'est la sortie, quel est le point? Ne devrait-il pas seulement remplacer le premier "z" minuscule par un "Z" majuscule? Au lieu de les remplacer tous? Je pensais que c'était ce dont nous parlions ici ...
Swivel
Mon mauvais, il ne remplacera que la première occurrence. Édité.
happyhardik
Ce même conseil avait déjà été proposé par Bas près de 3 ans plus tôt (et sans trop appeler strpos()). Voté parce qu'il n'ajoute aucune nouvelle valeur à la page.
mickmackusa
0
Si votre chaîne ne contient aucun caractère multi-octets et si vous souhaitez remplacer un seul caractère, vous pouvez simplement utiliser strpos
Voici une fonction qui gère les erreurs
/**
* Replace the first occurence of given string
*
* @param string $search a char to search in `$subject`
* @param string $replace a char to replace in `$subject`
* @param string $subject
* @return string
*
* @throws InvalidArgumentException if `$search` or `$replace` are invalid or if `$subject` is a multibytes string
*/function str_replace_first(string $search ,string $replace ,string $subject):string{// check paramsif(strlen($replace)!=1|| strlen($search)!=1){thrownewInvalidArgumentException('$search & $replace must be char');}elseif(mb_strlen($subject)!= strlen($subject)){thrownewInvalidArgumentException('$subject is an multibytes string');}// search
$pos = strpos($subject, $search);if($pos ===false){// not foundreturn $subject;}// replace
$subject[$replace]= $subject;return $subject;}
Voici une classe simple que j'ai créée pour encapsuler nos fonctions str_replace () légèrement modifiées .
Notre fonction php :: str_rreplace () vous permet également d'effectuer une opération inverse, limitée str_replace () qui peut être très pratique lorsque vous essayez de remplacer uniquement les instances X finales d'une chaîne.
<?php
class php {/**
* str_replace() from the end of a string that can also be limited e.g. replace only the last instance of '</div>' with ''
*
* @param string $find
* @param string $replace
* @param string $subject
* @param int $replacement_limit | -1 to replace all references
*
* @return string
*/publicstaticfunction str_replace($find, $replace, $subject, $replacement_limit =-1){
$find_pattern = str_replace('/','\/', $find);return preg_replace('/'. $find_pattern .'/', $replace, $subject, $replacement_limit);}/**
* str_replace() from the end of a string that can also be limited e.g. replace only the last instance of '</div>' with ''
*
* @param string $find
* @param string $replace
* @param string $subject
* @param int $replacement_limit | -1 to replace all references
*
* @return string
*/publicstaticfunction str_rreplace($find, $replace, $subject, $replacement_limit =-1){return strrev(self::str_replace(strrev($find), strrev($replace), strrev($subject), $replacement_limit));}}
Votre message n'ajoute pas de valeur à cette page déjà saturée. Votre solution d'expression régulière échoue sur de nombreux cas de franges, car vous avez utilisé l'outil incorrect pour échapper les caractères dans la chaîne d'aiguille. Preuve d'échec: 3v4l.org/dTdYK La réponse largement votée et acceptée de 2009 montre déjà la bonne exécution de cette technique. Votre deuxième méthode ne répond pas à la question posée et a déjà été fournie par oLinkWebDevelopment.
Cette technique a été fournie par toomuchphp en 2009 ! J'ai voté contre parce que votre message n'ajoute aucune nouvelle valeur aux chercheurs. Veuillez vous assurer, avant de publier une réponse, que votre solution est unique sur la page et ajoute de la valeur à la page.
mickmackusa
-3
c'est ma première réponse ici, j'espère le faire correctement. Pourquoi ne pas utiliser le quatrième argument de la fonction str_replace pour ce problème?
count: S'il est réussi, il sera défini sur le nombre de remplacements effectués.
edit: Cette réponse est fausse, car le 4ème paramètre de str_replace est une variable qui obtient le nombre de remplacements effectués. Ceci n'est pas cohérent avec preg_replace , qui a un 4ème paramètre $limitet un 5ème paramètre &$count.
Le quatrième argument sera défini par str_replace () au nombre de remplacements effectués. C'est pourquoi vous obtenez une erreur lorsque vous lui passez un entier et non une variable.
arminrosu
-6
Il est facile de trouver une solution pour remplacer uniquement le premier ou le premier couple d'instances (en donnant la valeur de comptage). Il n'y a pas beaucoup de solutions pour remplacer le dernier ou le dernier couple d'instances.
Peut-être que quelque chose comme str_replace ($ find, $ replace, $ subject, -3) devrait remplacer les trois dernières instances.
Pourquoi répondre à une question avec une suggestion alors qu'une réponse a été acceptée deux ans auparavant?!
mbinette
De plus, -3 ne fonctionnera pas comme paramètre, car le 4ème paramètre est une sortie et non un paramètre d'entrée. Il serait préférable de tester ce que vous proposez, au lieu de publier du code qui se bloque.
Ghostwriter78
Ouais, c'est faux, cependant, pourquoi est-ce que j'obtiens un crash d'écran vide, quand je l'essaye? J'ai fait le error_reporting habituel (E_ALL); ini_set ("display_errors", 1); Erreur d'écran encore vierge.
s($subject)->replaceFirst($search)
ets($subject)->replaceFirstIgnoreCase($search)
utile, comme dans cette bibliothèque autonome .Réponses:
Peut être fait avec preg_replace :
La magie est dans le quatrième paramètre optionnel [Limite]. De la documentation:
Cependant, consultez la réponse de zombat pour une méthode plus efficace (environ 3 à 4 fois plus rapide).
la source
preg_quote
? Par exemple, @ThomasRedstone craint que le délimiteur/
ne soit dangereux s'il apparaît$from
, mais heureusement ce n'est pas le cas: il est correctement échappé à cause dupreg_quote
second paramètre de (on peut facilement le tester). Je serais intéressé d'entendre parler de problèmes spécifiques (qui seraient de graves bogues de sécurité PCRE dans mon livre).Il n'y en a pas de version, mais la solution n'est pas du tout hacky.
Assez facile et économise la pénalité de performance des expressions régulières.
Bonus: si vous souhaitez remplacer la dernière occurrence, utilisez-la simplement
strrpos
à la place destrpos
.la source
substr_replace
s'agisse d'une fonction quelque peu difficile à utiliser en raison de tous les paramètres, le vrai problème est que la manipulation de chaînes par des nombres est parfois délicate - vous devez faire attention à transmettre la bonne variable / décalage aux fonctions. J'irais même jusqu'à dire que le code ci-dessus est l'approche la plus simple et la plus logique pour moi.Modifier: les deux réponses ont été mises à jour et sont maintenant correctes. Je vais laisser la réponse car les timings de fonction sont toujours utiles.
Les réponses de «zombat» et «trop de php» ne sont malheureusement pas correctes. Ceci est une révision de la réponse publiée par zombat (car je n'ai pas assez de réputation pour poster un commentaire):
Notez le strlen ($ needle), au lieu de strlen ($ replace). L'exemple de Zombat ne fonctionnera correctement que si l'aiguille et le remplacement sont de la même longueur.
Voici la même fonctionnalité dans une fonction avec la même signature que str_replace de PHP:
Ceci est la réponse révisée de «trop de php»:
Notez le 2 à la fin au lieu de 1. Ou au format de fonction:
J'ai chronométré les deux fonctions et la première est deux fois plus rapide lorsqu'aucune correspondance n'est trouvée. Ils ont la même vitesse lorsqu'un match est trouvé.
la source
stripos()
à la rescousse :-)Je me suis demandé laquelle était la plus rapide, alors je les ai toutes testées.
Vous trouverez ci-dessous:
Toutes les fonctions ont été testées avec les mêmes paramètres:
Fonctions qui ne remplacent que la première occurrence d'une chaîne dans une chaîne:
substr_replace($string, $replace, 0, strlen($search));
replace_first($search, $replace, $string);
preg_replace($search, $replace, $string, 1);
str_replace_once($search, $replace, $string);
str_replace_limit($search, $replace, $string, $count, 1);
str_replace_limit($search, $replace, $string, 1);
str_replace_limit($string, $search, $replace, 1, 0);
Fonctions qui ne remplacent que la dernière occurrence d'une chaîne dans une chaîne:
substr_replace($string, $replace, strrpos($string, $search), strlen($search));
strrev(implode(strrev($replace), explode(strrev($search), strrev($string), 2)));
la source
substr_replace()
remporte le résultat est simple; parce que c'est une fonction interne. Deux fonctions internes et définies par l'utilisateur faisant la même chose diffèrent dans les performances, car la fonction interne s'exécute dans les couches inférieures. Alors pourquoi paspreg_match()
? Les expressions régulières sont presque plus lentes que chaque fonction de manipulation de chaîne interne, en raison de leur nation de recherche dans une chaîne plusieurs fois.substr_replace($string, $replace, 0, strlen($search));
) n'a pas simplement écrit cette statique0
. Une partie de la convolution des solutions non regex est qu'elles doivent "trouver" le point de départ avant de savoir où remplacer.Malheureusement, je ne connais aucune fonction PHP qui puisse le faire.
Vous pouvez rouler le vôtre assez facilement comme ceci:
la source
join
au lieu deimplode
.return implode($replace, explode($find, $subject, $limit+1));
pour les numéros de remplacement personnalisésJ'ai créé cette petite fonction qui remplace chaîne sur chaîne (sensible à la casse) avec limite, sans avoir besoin de Regexp. Ça fonctionne bien.
Exemple d'utilisation:
la source
===false
plutôt que d'is_bool(
être plus explicite - je donne ce coup de pouce juste parce qu'il a évité la folie RegExp ! ... et en même temps ça marche et une solution propre ...preg_
solution facilement personnalisable n'est pas de la folie mais une préférence personnelle.return preg_replace('/'.preg_quote($search, '/').'/', $replace, $content, 1);
est assez simple à lire pour les personnes qui ne craignent pas les regex. Besoin d'une recherche insensible à la casse? Ajoutezi
après le délimiteur de motif de fin. Besoin d'un support unicode / multi-octets? Ajoutezu
après le délimiteur de motif de fin. Besoin de prise en charge des limites de mots? Ajoutez des\b
deux côtés de votre chaîne de recherche. Si vous ne voulez pas d'expression régulière, n'utilisez pas d'expression régulière. Des chevaux pour les cours, mais certainement pas de la folie.Le moyen le plus simple serait d'utiliser une expression régulière.
L'autre façon est de trouver la position de la chaîne avec strpos () puis un substr_replace ()
Mais j'irais vraiment pour le RegExp.
la source
la source
=> LE CODE A ÉTÉ RÉVISÉ, alors considérez certains commentaires trop anciens
Alors, allons-y pour:
Remplacement du premier «o» par «ea» par exemple:
La fonction:
la source
substr($where,$b+strlen($this))
, nonsubstr($where,$b+1)
. Et je suppose quesubstr_replace
c'est plus rapide.la source
preg_quote
des$find
avant de l' utiliser comme une expression.preg_quote()
. Cette réponse en double tardive peut être supprimée de la page en toute sécurité car ses conseils sont fournis par la réponse acceptée la plus ancienne et la plus élevée.Pour développer la réponse de @ renocor , j'ai écrit une fonction qui est 100% rétrocompatible avec
str_replace()
. Autrement dit, vous pouvez remplacer toutes les occurrences destr_replace()
avecstr_replace_limit()
sans rien gâcher, même ceux qui utilisent des tableaux pour la$search
,$replace
et / ou$subject
.La fonction pourrait être complètement autonome, si vous vouliez remplacer l'appel de fonction par
($string===strval(intval(strval($string))))
, mais je le déconseille carvalid_integer()
c'est une fonction plutôt utile lorsque vous traitez avec des entiers fournis sous forme de chaînes.Remarque: Dans la mesure du possible,
str_replace_limit()
utilisera à lastr_replace()
place, de sorte que tous les appels àstr_replace()
puissent être remplacésstr_replace_limit()
sans se soucier de la performance.Usage
Fonction
la source
E_USER_WARNING
partout, ce qui est un avertissement et non une erreur . La trace est extrêmement utile pour savoir quel code transmet les données non valides à la fonction en premier lieu (ce qui est absolument nécessaire pour traquer les bogues en production). Quant au retour$subject
au lieu defalse
/null
ou le lancement d'une erreur, c'était simplement un choix personnel pour mon cas d'utilisation. Pour faire correspondrestr_replace()
la fonctionnalité de, l'utilisation d'erreurs fatales capturables serait le meilleur pari (tout commestr_replace()
lors de la fermeture des deux premiers arguments).preg_replace()
. De plus,preg_replace()
/ regex offre une gestion des limites des mots (si cela est souhaitable) - quelque chose que les fonctions non regex ne fourniront pas avec élégance.Selon mon résultat de test, je voudrais voter celui regular_express fourni par karim79. (Je n'ai pas assez de réputation pour le voter maintenant!)
La solution de zombat utilise trop d'appels de fonctions, je simplifie même les codes. J'utilise PHP 5.4 pour exécuter les deux solutions 100 000 fois, et voici le résultat:
==> 1,85 sec
==> 1,35 sec
Comme vous pouvez le voir. La performance de preg_replace n'est pas aussi mauvaise que beaucoup de gens le pensent. Je suggère donc la solution chic si votre express régulier n'est pas compliqué.
la source
$pos
pourfalse
, donc quand l'aiguille n'existe pas dans la botte de foin, il endommagera la sortie.Pour développer la réponse de zombat (que je pense être la meilleure réponse), j'ai créé une version récursive de sa fonction qui prend un
$limit
paramètre pour spécifier le nombre d'occurrences que vous souhaitez remplacer.la source
$start_pos
, donc si elle est au - delà de la longueur de chaîne, cette fonction génère:Warning: strpos(): Offset not contained in string...
. Cette fonction ne parvient pas à effectuer un remplacement lorsqu'elle$start_pos
est au-delà de la longueur. Preuve d'échec: 3v4l.org/qGuVIR ... Votre fonction peut combiner lesreturn $haystack
conditions et éviter de déclarer des variables à usage unique comme ceci: 3v4l.org/Kdmqp Cependant, comme je l'ai dit dans les commentaires ailleurs sur cette page, je préfère utiliser un appel très net, direct et non récursifpreg_replace()
.else
déclaration de ligne$start_pos > strlen($haystack) ? $start_pos = strlen($haystack) : '';
Pour une chaîne
Pour un seul personnage
la source
substr_replace()
techniques endommagent la chaîne d'entrée lorsque la valeur de recherche n'est pas présente. Preuve d'échec: 3v4l.org/HmEml (Et cette dernière technique avec tous lesrev
appels est sérieusement alambiquée / dure pour les yeux.)En complément de ce que les gens ont dit, rappelez-vous que la chaîne entière est un tableau:
"Borem ipsum lá lá lá"
la source
á
. Démonstration d'échecstring
chaîne est multi-octets en utilisantmb_strlen($subject) != strlen($subject)
En utilisant substr_replace, nous pouvons remplacer l'occurrence du premier caractère uniquement dans la chaîne. comme & est répété plusieurs fois mais seulement en première position nous devons remplacer & avec?
la source
Cette fonction est fortement inspirée de la réponse de @renocor. Il rend la fonction multi-octets sûre.
la source
Vous pouvez utiliser ceci:
Trouvé cet exemple sur php.net
Usage:
Production:
Cela peut réduire un peu les performances, mais c'est la solution la plus simple.
la source
strpos()
). Voté parce qu'il n'ajoute aucune nouvelle valeur à la page.Si votre chaîne ne contient aucun caractère multi-octets et si vous souhaitez remplacer un seul caractère, vous pouvez simplement utiliser
strpos
Voici une fonction qui gère les erreurs
la source
Pour la solution de boucle
la source
Voici une classe simple que j'ai créée pour encapsuler nos fonctions str_replace () légèrement modifiées .
Notre fonction php :: str_rreplace () vous permet également d'effectuer une opération inverse, limitée str_replace () qui peut être très pratique lorsque vous essayez de remplacer uniquement les instances X finales d'une chaîne.
Ces exemples utilisent tous deux preg_replace () .
la source
Il y a un espace supplémentaire, mais cela n'avait pas d'importance comme c'était le cas pour le script d'arrière-plan dans mon cas.
la source
c'est ma première réponse ici, j'espère le faire correctement. Pourquoi ne pas utiliser le quatrième argument de la fonction str_replace pour ce problème?
edit: Cette réponse est fausse, car le 4ème paramètre de str_replace est une variable qui obtient le nombre de remplacements effectués. Ceci n'est pas cohérent avec preg_replace , qui a un 4ème paramètre
$limit
et un 5ème paramètre&$count
.la source
Il est facile de trouver une solution pour remplacer uniquement le premier ou le premier couple d'instances (en donnant la valeur de comptage). Il n'y a pas beaucoup de solutions pour remplacer le dernier ou le dernier couple d'instances.
Peut-être que quelque chose comme str_replace ($ find, $ replace, $ subject, -3) devrait remplacer les trois dernières instances.
Quoi qu'il en soit, juste une suggestion.
la source