Pourquoi cette boucle est-elle infinie?
-
03-07-2019 - |
Question
Donc, mon site vient de lancer le serveur aujourd'hui et je pense que cette fonction est le coupable. Est-ce que quelqu'un peut me dire quel est le problème? Je n'arrive pas à comprendre:
Public Function CleanText(ByVal str As String) As String
'removes HTML tags and other characters that title tags and descriptions don't like
If Not String.IsNullOrEmpty(str) Then
'mini db of extended tags to get rid of
Dim indexChars() As String = {"<a", "<img", "<input type=""hidden"" name=""tax""", "<input type=""hidden"" name=""handling""", "<span", "<p", "<ul", "<div", "<embed", "<object", "<param"}
For i As Integer = 0 To indexChars.GetUpperBound(0) 'loop through indexchars array
Dim indexOfInput As Integer = 0
Do 'get rid of links
indexOfInput = str.IndexOf(indexChars(i)) 'find instance of indexChar
If indexOfInput <> -1 Then
Dim indexNextLeftBracket As Integer = str.IndexOf("<", indexOfInput) + 1
Dim indexRightBracket As Integer = str.IndexOf(">", indexOfInput) + 1
'check to make sure a right bracket hasn't been left off a tag
If indexNextLeftBracket > indexRightBracket Then 'normal case
str = str.Remove(indexOfInput, indexRightBracket - indexOfInput)
Else
'add the right bracket right before the next left bracket, just remove everything
'in the bad tag
str = str.Insert(indexNextLeftBracket - 1, ">")
indexRightBracket = str.IndexOf(">", indexOfInput) + 1
str = str.Remove(indexOfInput, indexRightBracket - indexOfInput)
End If
End If
Loop Until indexOfInput = -1
Next
End If
Return str
End Function
La solution
Quelque chose comme ça ne serait-il pas plus simple? (OK, je sais que ce n'est pas identique au code envoyé):
public string StripHTMLTags(string text)
{
return Regex.Replace(text, @"<(.|\n)*?>", string.Empty);
}
(La conversion en VB.NET devrait être triviale!)
Remarque: si vous utilisez souvent ce logiciel, vous pouvez améliorer les performances de Regex
.
L’une consiste à utiliser une expression précompilée qui nécessite une réécriture légère.
La seconde consiste à utiliser une forme non-capturante de l'expression régulière; Les expressions rationnelles .NET implémentent la syntaxe (? :), ce qui permet d'effectuer un regroupement sans entraîner de pénalité de performances si le texte capturé est mémorisé comme référence arrière. En utilisant cette syntaxe, l'expression régulière ci-dessus peut être modifiée en:
@"<(?:.|\n)*?>"
Autres conseils
Cette ligne est également incorrecte:
Dim indexNextLeftBracket As Integer = str.IndexOf("<", indexOfInput) + 1
Il est garanti de toujours mettre indexNextLeftBracket à indexOfInput, car à ce stade, le caractère à la position indiquée par indexOfInput est déjà toujours un '<'. Faites ceci à la place:
Dim indexNextLeftBracket As Integer = str.IndexOf("<", indexOfInput+1) + 1
Et ajoutez également une clause à l'instruction if pour vous assurer que votre chaîne est suffisamment longue pour cette expression.
Enfin, comme d’autres l’ont déjà dit, ce code sera une bête à maintenir, si vous pouvez le faire fonctionner. Le mieux est de chercher une autre solution, comme une regex ou même simplement remplacer tous les '& Lt;' avec <
.
En plus d'autres bonnes réponses, vous pouvez en savoir plus sur les invariants de boucle a un peu. Le fait de retirer et de remettre les éléments sur la chaîne que vous vérifiez pour terminer votre boucle devrait déclencher toutes sortes de sonneries d’alarme. :)
Juste une supposition, mais est-ce que c'est comme le coupable? indexOfInput = str.IndexOf (indexChars (i)) 'recherche d'une instance de indexChar
Conformément à la document Microsoft , retournez Valeur - La position d'index de valeur si cette chaîne est trouvée ou -1 si ce n'est pas le cas. Si la valeur est vide, la valeur renvoyée est 0.
Alors, indexOfInput est peut-être défini sur 0?
Que se passe-t-il si votre code tente de nettoyer la chaîne <a
?
Comme je l'ai lu, il trouve indexChar à la position 0, mais ensuite indexNextLeftBracket et indexRightBracket valent tous les deux 0, vous tombez dans la condition else, puis vous insérez un &; > " en position -1, qui sera probablement inséré au début, vous donnant la chaîne ><a
. Le nouvel indexRightBracket devient alors 0, vous supprimez donc de la position 0 pour 0 caractères, ce qui vous laisse avec <input type="hidden" name="tax"
. Ensuite, le code retrouve le <input name="tax" type="hidden"
code dans le code et vous êtes prêt pour les courses avec une boucle infinie consommant de la mémoire.
Même si je me trompe, vous devez vous faire passer des tests unitaires pour vous assurer que ces cas extrêmes fonctionnent correctement. Cela devrait également vous aider à trouver le code de la boucle si je ne suis pas basé.
En règle générale, même si vous corrigez ce bogue, il ne sera jamais très robuste. Analyser le HTML est difficile, et les listes noires HTML vont toujours avoir des trous. Par exemple, si je veux vraiment insérer une balise <=>, je l'écrirai simplement comme <=> et votre code l'ignorera. Votre meilleur choix consiste à impliquer un analyseur HTML réel et à n'autoriser que le (très petit) sous-ensemble de balises que vous souhaitez réellement. Ou encore mieux, utilisez une autre forme de balisage et supprimez toutes les balises HTML (en utilisant à nouveau un analyseur HTML réel contenant une description).
Je devrais utiliser un vrai compilateur, mais mindpiler me dit que la ligne str = str.Remove(indexOfInput, indexRightBracket - indexOfInput)
génère à nouveau une balise non valide de sorte que, lorsque vous répétez une boucle, elle retrouve la même erreur & ";" correctifs " il essaie encore, trouve l'erreur " répare " elle, etc.
FWIW présente un extrait de code qui supprime les balises HTML non désirées d'une chaîne (c'est en C # mais le concept se traduit)
public static string RemoveTags( string html, params string[] allowList )
{
if( html == null ) return null;
Regex regex = new Regex( @"(?<Tag><(?<TagName>[a-z/]+)\S*?[^<]*?>)",
RegexOptions.Compiled |
RegexOptions.IgnoreCase |
RegexOptions.Multiline );
return regex.Replace(
html,
new MatchEvaluator(
new TagMatchEvaluator( allowList ).Replace ) );
}
Classe MatchEvaluator
private class TagMatchEvaluator
{
private readonly ArrayList _allowed = null;
public TagMatchEvaluator( string[] allowList )
{
_allowed = new ArrayList( allowList );
}
public string Replace( Match match )
{
if( _allowed.Contains( match.Groups[ "TagName" ].Value ) )
return match.Value;
return "";
}
}
Cela ne semble pas fonctionner dans un cas simpliste <a<a<a
, ni même <a>Test</a>
. Avez-vous testé cela du tout?
Personnellement, je déteste analyser des chaînes de ce genre - je ne vais donc même pas essayer de déterminer où se trouve votre erreur. Il faudrait un débogueur et plus de maux de tête que ce que je suis prêt à mettre.