2010-08-06 3 views
2

Je compare des mises à jour à deux chaînes. j'ai fait un:Comment ne pas inclure des sauts de ligne lors de la comparaison de deux chaînes

string1 != string2 

et ils s'avèrent différents. Je les ai mis dans le « Ajouter Watch » et je vois la seule différence est qu'on a des sauts de ligne et l'autre ne marche pas » .:

string1 = "This is a test. \nThis is a test"; 
string2 = "This is a test. This is a test"; 

je veux essentiellement à faire comparer mais ne comprennent les sauts de ligne. Donc, si le saut de ligne est la seule différence, considérez-les comme égaux.

+1

Voir aussi http://stackoverflow.com/questions/1862314/string-comparison-performance-with-trim –

+0

Sur le plan technique , les 2 chaînes * sont * différentes et s'afficheront différemment à l'écran ou si vous les imprimez. Cela dit, toutes les réponses fournies feront ce que vous voulez, bien sûr. – ThunderGr

Répondre

8

Une façon rapide et sale, lorsque les performances ne sont pas vraiment un problème:

string1.Replace("\n", "") != string2.Replace("\n", "") 
+0

Cette réponse ne prend pas en compte le caractère '\ r'. –

+1

En ce qui concerne la référence aux performances, le problème potentiel ici est que chaque comparaison implique l'allocation de copies de chaque chaîne sur le tas. Si la performance est importante, jetez un coup d'œil à [ma réponse] (http://stackoverflow.com/a/41408399/24874). –

0
string1.replace('\n','') != string2.replace('\n','') 
0

Vous pouvez simplement supprimer les sauts de ligne avant de comparer les chaînes?

E.g. (Pseudocode) ...

string1.replace('\n','') != string2.replace('\n','') 
+0

@Paul Creasey: Tu m'as battu! – barrylloyd

2

Je vous suggère regex de réduire tous les space, tab, \r, \n à un seul espace:

Regex.Replace(string1, @"\s+", " ") != Regex.Replace(string2, @"\s+", " ") 
+0

Mais cela enlèvera l'espace et les tabulations - Je pense que l'OP voulait seulement ignorer les sauts de ligne – barrylloyd

+0

Oui, comme je l'ai dit, mais je ne vois pas pourquoi plusieurs espaces devraient importer quand les nouvelles lignes ne sont pas pertinentes – Diadistis

+0

@Diadistis: It Il se trouve juste que j'ai un cas d'utilisation qui nécessite exactement cela - tests unitaires pour un fournisseur de retrait intelligent pour une extension Visual Studio (l'indentation compte mais les fins de ligne ne le font pas) :-) – Cameron

3

en supposant que:

  1. Le type de comparaison directe des valeurs de char-value-for-char de! = Et == est ce qui est voulu ici, sauf pour les nouvelles lignes.
  2. Les chaînes sont, ou peuvent, être assez grandes ou comparées assez souvent pour remplacer simplement "\n" par une chaîne vide trop inefficace.

Puis:

public bool LinelessEquals(string x, string y) 
{ 
    //deal with quickly handlable cases quickly. 
    if(ReferenceEquals(x, y))//same instance 
     return true;   // - generally happens often in real code, 
          //and is a fast check, so always worth doing first. 
    //We already know they aren't both null as 
    //ReferenceEquals(null, null) returns true. 
    if(x == null || y == null) 
     return false; 
    IEnumerator<char> eX = x.Where(c => c != '\n').GetEnumerator(); 
    IEnumerator<char> eY = y.Where(c => c != '\n').GetEnumerator(); 
    while(eX.MoveNext()) 
    { 
     if(!eY.MoveNext()) //y is shorter 
      return false; 
     if(ex.Current != ey.Current) 
      return false; 
    } 
    return !ey.MoveNext(); //check if y was longer. 
} 

Il se définit comme l'égalité plutôt que l'inégalité, donc vous pouvez facilement l'adapter à être une mise en œuvre de IEqualityComparer<string>.Equals. Votre question pour une linebreak-moins string1 != string2 devient: !LinelessEquals(string1, string2)

+0

Voyons, +1 pour prendre le temps de fournissez une bonne réponse, +1 pour la première partie où vous vérifiez l'égalité de référence et null et -1 pour l'overkill linq dans une méthode de comparaison de chaînes. +1 globalement :) – Diadistis

+0

J'ai mis le lambda dedans principalement parce qu'il a rendu le code plus compact, et c'est une expression assez directe qui s'exécuterait assez rapidement. La manière démodée serait d'ajouter une fonction d'assistance qui restituerait les caractères qui n'étaient pas des sauts de ligne. Le mode vraiment démodé (.NET 1.1) aurait une classe d'aide ou serait beaucoup plus compliqué dans son itération. Il est difficile d'avoir une surdétermination dans une méthode de comparaison de chaînes, en général la comparaison de chaînes peut être très compliquée en effet (considérons le cas courant de ß correspondant SS dans des comparaisons sans cas). –

1

Une approche plus propre serait d'utiliser:

string1.Replace(Environment.NewLine, String.Empty) != string2.Replace(Environment.NewLine, String.Empty); 
0

Ceci est une version généralisée et testée de réponse Jon Hannas.

/// <summary> 
/// Compares two character enumerables one character at a time, ignoring those specified. 
/// </summary> 
/// <param name="x"></param> 
/// <param name="y"></param> 
/// <param name="ignoreThese"> If not specified, the default is to ignore linefeed and newline: {'\r', '\n'} </param> 
/// <returns></returns> 
public static bool EqualsIgnoreSome(this IEnumerable<char> x, IEnumerable<char> y, params char[] ignoreThese) 
{ 
    // First deal with quickly handlable cases quickly: 
    // Same instance - generally happens often in real code, and is a fast check, so always worth doing first. 
    if (ReferenceEquals(x, y)) 
     return true;   // 
    // We already know they aren't both null as ReferenceEquals(null, null) returns true. 
    if (x == null || y == null) 
     return false; 
    // Default ignore is newlines: 
    if (ignoreThese == null || ignoreThese.Length == 0) 
     ignoreThese = new char[] { '\r', '\n' }; 
    // Filters by specifying enumerator. 
    IEnumerator<char> eX = x.Where(c => !ignoreThese.Contains(c)).GetEnumerator(); 
    IEnumerator<char> eY = y.Where(c => !ignoreThese.Contains(c)).GetEnumerator(); 
    // Compares. 
    while (eX.MoveNext()) 
    { 
     if (!eY.MoveNext()) //y is shorter 
      return false; 
     if (eX.Current != eY.Current) 
      return false; 
    } 
    return !eY.MoveNext(); //check if y was longer. 
} 
1

Voici un comparateur d'égalité pour les chaînes qui ne tient pas compte de certains caractères, tels que \r et \n.

Cette implémentation n'alloue aucune mémoire de segment pendant l'exécution, ce qui améliore ses performances. Il évite également les appels virtuels à travers IEnumerable et IEnumerator.

public sealed class SelectiveStringComparer : IEqualityComparer<string> 
{ 
    private readonly string _ignoreChars; 

    public SelectiveStringComparer(string ignoreChars = "\r\n") 
    { 
     _ignoreChars = ignoreChars; 
    } 

    public bool Equals(string x, string y) 
    { 
     if (ReferenceEquals(x, y)) 
      return true; 
     if (x == null || y == null) 
      return false; 
     var ix = 0; 
     var iy = 0; 
     while (true) 
     { 
      while (ix < x.Length && _ignoreChars.IndexOf(x[ix]) != -1) 
       ix++; 
      while (iy < y.Length && _ignoreChars.IndexOf(y[iy]) != -1) 
       iy++; 
      if (ix >= x.Length) 
       return iy >= y.Length; 
      if (iy >= y.Length) 
       return false; 
      if (x[ix] != y[iy]) 
       return false; 
      ix++; 
      iy++; 
     } 
    } 

    public int GetHashCode(string obj) 
    { 
     throw new NotSupportedException(); 
    } 
} 
0

Voici une version en VB.net basé sur Drew Noakes répondre

Dim g_sIgnore As String = vbSpace & vbNewLine & vbTab 'String.Format("\n\r\t ") 

Public Function StringCompareIgnoringWhitespace(s1 As String, s2 As String) As Boolean 
    Dim i1 As Integer = 0 
    Dim i2 As Integer = 0 
    Dim s1l As Integer = s1.Length 
    Dim s2l As Integer = s2.Length 

    Do 
     While i1 < s1l AndAlso g_sIgnore.IndexOf(s1(i1)) <> -1 
      i1 += 1 
     End While 
     While i2 < s2l AndAlso g_sIgnore.IndexOf(s2(i2)) <> -1 
      i2 += 1 
     End While 
     If i1 = s1l And i2 = s2l Then 
      Return True 
     Else 
      If i1 < s1l AndAlso i2 < s2l AndAlso s1(i1) = s2(i2) Then 
       i1 += 1 
       i2 += 1 
      Else 
       Return False 
      End If 
     End If 
    Loop 
    Return False 
End Function 

J'ai aussi testé avec

Try 
    Debug.Assert(Not StringCompareIgnoringWhitespace("a", "z")) 
    Debug.Assert(Not StringCompareIgnoringWhitespace("aa", "zz")) 
    Debug.Assert(StringCompareIgnoringWhitespace("", "")) 
    Debug.Assert(StringCompareIgnoringWhitespace(" ", "")) 
    Debug.Assert(StringCompareIgnoringWhitespace("", " ")) 
    Debug.Assert(StringCompareIgnoringWhitespace(" a", "a ")) 
    Debug.Assert(StringCompareIgnoringWhitespace(" aa", "aa ")) 
    Debug.Assert(StringCompareIgnoringWhitespace(" aa ", " aa ")) 
    Debug.Assert(StringCompareIgnoringWhitespace(" aa a", " aa a")) 
    Debug.Assert(Not StringCompareIgnoringWhitespace("a", "")) 
    Debug.Assert(Not StringCompareIgnoringWhitespace("", "a")) 
    Debug.Assert(Not StringCompareIgnoringWhitespace("ccc", "")) 
    Debug.Assert(Not StringCompareIgnoringWhitespace("", "ccc")) 
Catch ex As Exception 
    Console.WriteLine(ex.ToString) 
End Try 
Questions connexes