2016-05-04 1 views
1

Donc, j'ai un texte im recherche creux, je cherche un mot spécifique dans le texte. Pour montrer cela, im définissant le mot im cherchant comme: "1 Johannes 1:12". J'utilise la méthode String.Contains, mais cette méthode retourne deux réponses parce que, je recherche aussi à travers une autre phrase: "1 Johannes 1:1". Donc ce qu'il fait, au lieu de prendre "1 Johannes 1:12" d'abord, puis "1 Johannes 1:1", c'est faire le contraire. Maintenant c'est mauvais pour moi. Parce que je veux la bonne phrase.Chaîne contenant contient. C#

J'ai essayé différentes options, y compris la sous-chaîne. Mais j'ai besoin d'aide sur ce sujet.

Merci pour toutes les réponses à l'avance. À votre santé!

List<string> sentences = new List<string>(); 
sentences.Add("1 Johannes 1:12"); 
sentences.Add("1 Johannes 1:1"); 
string fulltext = "randomtext 1 Johannes 1:12 randomtext"; 

foreach (string item in sentences) 
{ 
    if (fulltext.Contains(item)) 
    { 
     //expect the result to be 1 Johannes 1:12, but the result is 1 Johannes 1:1 
     //do operation 
    } 
} 
+5

Pouvez-vous poster un exemple de code? Quelle chaîne recherchez-vous et quelles sont les «deux réponses» que vous obtenez? Ce n'est pas vraiment clair quel est votre problème ici. – millimoose

+0

Pouvez-vous poster l'exemple complet de phrase que vous commencez avec? Gardez à l'esprit que "1 Johannes 1: 1" est contenu dans "1 Johannes 1:12", ce qui peut expliquer votre faux positif. Je voudrais vous aider à répondre à la question aussi complètement que possible. –

+2

En outre, si vous pouvez fournir exactement ce que vous essayez d'extraire de cette phrase complète, cela vous aidera à obtenir une réponse réelle. –

Répondre

2

d'analyse syntaxique de référence biblique et la reconnaissance est délicat, en particulier parce qu'il existe plusieurs styles d'abréviation, des nombres qui ressemblent. Le problème que vous avez est que String.Contains() est un gros marteau, et vous avez besoin de quelque chose de plus comme un jeu de clés à douille. En d'autres termes, une réponse complète et correcte va nécessiter plus de code que ce qui peut s'adapter confortablement dans ce format. J'ai écrit du code pour passer en revue les dévotions et les transcriptions et en retirer toutes les références. Le code est dans un dépôt privé, mais je vais essayer d'afficher les parties pertinentes.

Une référence biblique est écrite dans ce format: {Book} {Chapter}:{Verse}, avec une certaine variation pour les plages de vers. Donc, la première partie est de reconnaître le livre. À cette fin, j'ai créé une classe pour représenter un livre et ses abréviations connues (je soutenais deux styles d'abréviations documentés). La classe ressemble Book ceci:

public class Book 
{ 
    // The set of books we recognize 
    private static readonly List<Book> books; 
    private static readonly Dictionary<string, Book> commonMisspellings; 

    static Book() 
    { 
     // Initialize the set 
     books = new List<Book>{ 
      // Old Testament 
      new Book("Genesis", "Gen.", "Ge", 50), // Gen 
      new Book("Exodus", "Ex.", "Ex", 40), // Exod 
      new Book("Leviticus", "Lev.", "Le", 27), // Lev 
      new Book("Numbers", "Num.", "Nu", 36), // Num 
      new Book("Deuteronomy", "Deut.", "De", 34), // Deut 
      new Book("Joshua", "Josh.", "Jos", 24), // Josh 
      new Book("Judges", "Judg.", "Jud", 21), // Judg 
      new Book("Ruth", "Ruth", "Ru", 4), // Ruth 
      new Book("1 Samuel", "1 Sam.", "1 S", 31), // 1Sam 
      new Book("2 Samuel", "2 Sam.", "2 S", 24), // 2Sam 
      new Book("1 Kings", "1 Kings", "1 K", 22), // 1Kgs 
      new Book("2 Kings", "2 Kings", "2 K", 25), // 2Kgs 
      new Book("1 Chronicles", "1 Chron.", "1 Chr", 29), // 1Chr 
      new Book("2 Chronicles", "2 Chron.", "2 Chr", 36), // 2Chr 
      new Book("Ezra", "Ezra", "Ezr", 10), // Ezra 
      new Book("Nehemiah", "Neh.", "Ne", 13), // Neh 
      new Book("Esther", "Est.", "Est", 10), // Esth 
      new Book("Job", "Job", "Jb", 42), // Job 
      new Book("Psalms", "Ps.", "Ps", 150), // Ps 
      new Book("Proverbs", "Prov.", "Pr", 31), // Prov 
      new Book("Ecclesiastes", "Eccl.", "Ec", 12), // Eccl 
      new Book("Song of Solomon", "Song", "Song", 8), // Song 
      new Book("Isaiah", "Isa.", "Is", 66), // Isa 
      new Book("Jeremiah", "Jer.", "Je", 52), // Jer 
      new Book("Lamentations", "Lam.", "Lam", 5), // Lam 
      new Book("Ezekiel", "Ezek.", "Ez", 48), // Ezek 
      new Book("Daniel", "Dan.", "Da", 12), // Dan 
      new Book("Hosea", "Hos.", "Ho", 14), // Hos 
      new Book("Joel", "Joel", "Joel", 3), // Joel 
      new Book("Amos", "Amos", "Am", 9), // Amos 
      new Book("Obadaiah", "Obad.", "Obad", 1), // Obad 
      new Book("Jonah", "Jonah", "Jona", 4), // Jonah 
      new Book("Micah", "Mic.", "Mi", 7), // Mic 
      new Book("Nahum", "Nah.", "Na", 3), // Nah 
      new Book("Habakkuk", "Hab.", "Hab", 3), // Hab 
      new Book("Zephaniah", "Zeph.", "Zep", 3), // Zeph 
      new Book("Haggai", "Hag.", "Hag", 2), // Hag 
      new Book("Zechariah", "Zech.", "Zec", 14), // Zech 
      new Book("Malachai", "Mal.", "Mal", 4), // Mal 

      // New Testament 
      new Book("Matthew", "Matt.", "Mt", 28), // Matt 
      new Book("Mark", "Mark", "Mk", 16), // Mark 
      new Book("Luke", "Luke", "Lu", 24), // Luke 
      new Book("John", "John", "Jn", 21), // John 
      new Book("Acts", "Acts", "Ac", 28), // Acts 
      new Book("Romans", "Rom.", "Ro", 16), // Rom 
      new Book("1 Corinthians", "1 Cor.", "1 Co", 16), // 1Cor 
      new Book("2 Corinthians", "2 Cor.", "2 Co", 13), // 2Cor 
      new Book("Galatians", "Gal.", "Ga", 6), // Gal 
      new Book("Ephesians", "Eph.", "Ep", 6), // Eph 
      new Book("Philippians", "Phil.", "Ph", 4), // Phil 
      new Book("Colossians", "Col.", "Col", 4), // Col 
      new Book("1 Thessalonians", "1 Thes.", "1 Th", 5), // 1Thess 
      new Book("2 Thessalonians", "2 Thes.", "2 Th", 3), // 2Thess 
      new Book("1 Timothy", "1 Tim.", "1 Ti", 6), // 1Tim 
      new Book("2 Timothy", "2 Tim.", "2 Ti", 4), // 2Tim 
      new Book("Titus", "Titus", "Tit", 3), // Titus 
      new Book("Philemon", "Philem.", "Phm", 1), // Phlm 
      new Book("Hebrews", "Heb.", "He", 13), // Heb 
      new Book("James", "James", "Ja", 5), // Jas 
      new Book("1 Peter", "1 Peter", "1 Pe", 5), // 1Pet 
      new Book("2 Peter", "2 Peter", "2 Pe", 3), // 2Pet 
      new Book("1 John", "1 John", "1 Jn", 5), // 1John 
      new Book("2 John", "2 John", "2 Jn", 1), // 2John 
      new Book("3 John", "3 John", "3 Jn", 1), // 3John 
      new Book("Jude", "Jude", "Jude", 1), // Jude 
      new Book("Revelation", "Rev.", "Re", 22) // Rev 
     }; 

     Debug.Assert(books.Count == 66); 

     // These are based on what I found in the set of over 6,000 
     // transcripts that people typed. 
     commonMisspellings = new Dictionary<string, Book>(); 
     commonMisspellings.Add("song of songs", books.FirstOrDefault(b => b.ThompsonAbreviation == "Song")); 
     commonMisspellings.Add("psalm", books.FirstOrDefault(b => b.ThompsonAbreviation == "Ps")); 
     commonMisspellings.Add("like", books.FirstOrDefault(b => b.ThompsonAbreviation == "Lu")); 
     commonMisspellings.Add("jerimiah", books.FirstOrDefault(b => b.ThompsonAbreviation == "Je")); 
     commonMisspellings.Add("galations", books.FirstOrDefault(b => b.ThompsonAbreviation == "Ga")); 
    } 

    private static int numCreated = 0; 
    private int order; 

    private Book(string fullName, string abbrev, string thompsan, int chapters) 
    { 
     order = numCreated; 
     Name = fullName; 
     StandardAbreviation = abbrev; 
     ThompsonAbreviation = thompsan; 
     ChapterCount = chapters; 
     numCreated++; 
    } 

    /// <summary> 
    /// The unabbreviated name of the book. 
    /// </summary> 
    public string Name { get; private set; } 

    /// <summary> 
    /// Standard abbreviations as defined in "The Christian Writer's 
    /// Manual of Style", 2004 edition (ISBN: 9780310487715). 
    /// </summary> 
    public string StandardAbreviation { get; private set; } 

    /// <summary> 
    /// Thompson Chain references, pulled from the 5th edition. 
    /// </summary> 
    public string ThompsonAbreviation { get; private set; } 

    /// <summary> 
    /// The number of chapters in the book. 
    /// </summary> 
    public int ChapterCount { get; private set; } 

    public static bool TryParse(string inString, out Book book) 
    { 
     string potentialBook = StandardizeBookOrdinals(inString); 

     // Find the first book where the input string now matches one of the recognized formats. 
     book = books.FirstOrDefault(
      b => b.ThompsonAbreviation.Equals(potentialBook, StringComparison.InvariantCultureIgnoreCase) 
       || b.StandardAbreviation.Equals(potentialBook, StringComparison.InvariantCultureIgnoreCase) 
       || b.Name.Equals(potentialBook, StringComparison.InvariantCultureIgnoreCase)); 

     if (book != null) 
     { 
      return true; 
     } 

     // If we didn't find it, check to see if we just missed it because the abbreviation 
     // didn't have a period 
     book = books.FirstOrDefault((b) => 
     { 
      string stdAbrev = b.StandardAbreviation; 
      if(stdAbrev.EndsWith(".")) 
      { 
       stdAbrev = stdAbrev.Substring(0, stdAbrev.Length - 1); 
      } 

      return potentialBook == stdAbrev; 
     }); 

     if (book != null) 
     { 
      return true; 
     } 

     // Special Case: check for common misspellings 
     string lowercase = potentialBook.ToLowerInvariant(); 
     commonMisspellings.TryGetValue(lowercase, out book); 

     return book != null; 
    } 

    private static string StandardizeBookOrdinals(string str) 
    { 
     // Break up on all remaining white space 
     string[] parts = (str ?? "").Trim().Split(' ', '\r', '\n', '\t'); 

     // If the first part is a roman numeral, or spelled ordinal, convert it to arabic 
     var number = parts[0].ToLowerInvariant(); 
     switch (number) 
     { 
      case "first": 
      case "i": 
       parts[0] = "1"; 
       break; 

      case "second": 
      case "ii": 
       parts[0] = "2"; 
       break; 

      case "third": 
      case "iii": 
       parts[0] = "3"; 
       break; 
     } 

     // Recompile the parts into one string that only has a single space separating elements 
     return string.Join(" ", parts); 
    } 

    public static IEnumerable<Book> List() 
    { 
     return books.ToArray(); 
    } 
} 

Alors que vous permet de reconnaître un livre si vous nourrissez ce texte dans TryParse(). Nous traitons même les fautes d'orthographe courantes, les chiffres romains (I, II, III) par rapport aux chiffres arabes (1, 2, 3) et plus d'un style d'abréviation. N'hésitez pas à vous adapter si nécessaire, mais une fois que nous pourrons reconnaître un livre, le reste sera le même.La raison de la liste du nombre de chapitres dans un livre deviendra plus évident quand on regarde la classe suivante pour faire face à une Reference

public class Reference 
{ 
    private static readonly Regex RemoveHtml = new Regex("<[^>]*>", RegexOptions.Compiled); 

    public Book Book { get; set; } 
    public int Chapter { get; set; } 
    public int[] Verses { get; set; } 

    public static bool TryParse(string text, out Reference reference) 
    { 
     string errorString; 
     reference = InternalParse(text, out errorString); 

     if(errorString!=null) 
     { 
      reference = null; 
      return false; 
     } 

     return true; 
    } 

    private static Reference InternalParse(string text, out string errorString) 
    { 
     errorString = null; 
     int colon = text.LastIndexOf(':'); 
     int chapter = -1; 
     string chapterSection = "1"; 
     string verseSection = ""; 

     if (colon > 0) 
     { 
      verseSection = text.Substring(colon + 1); 
      chapter = colon - 3; 

      chapterSection = text.Substring(chapter, colon - chapter); 
      while (!string.IsNullOrEmpty(chapterSection) && !Char.IsDigit(chapterSection[0])) 
      { 
       chapter++; 
       chapterSection = text.Substring(chapter, colon - chapter); 
      } 
     } 
     else 
     { 
      chapter = 2; // skip initial numbers for books 
      while(chapter < text.Length && !Char.IsDigit(text[chapter])) 
      { 
       chapter++; 
      } 

      if(chapter == text.Length) 
      { 
       errorString = "There are no chapter or verses, can't be a reference."; 
       return null; 
      } 

      verseSection = text.Substring(chapter); 
     } 

     Book book; 
     if (!Book.TryParse(text.Substring(0, chapter), out book)) 
     { 
      errorString = "There is no book, can't be a reference."; 
      return null; 
     } 

     if(!int.TryParse(chapterSection, out chapter)) 
     { 
      errorString = "Bad chapter format"; 
      return null; 
     } 

     Reference reference = new Reference 
     { 
      Book = book, 
      Chapter = chapter 
     }; 

     if(colon < 0 && reference.Book.ChapterCount > 1) 
     { 
      if(!int.TryParse(verseSection, out chapter)) 
      { 
       errorString = "Bad chapter format."; 
       return null; 
      } 

      reference.Chapter = chapter; 
      reference.Verses = new int[0]; 
      return reference; 
     } 

     if (reference.Chapter > reference.Book.ChapterCount) 
     { 
      errorString = "Chapter found was too high"; 
      return null; 
     } 

     reference.Verses = ParseRanges(verseSection, out errorString); 

     return reference; 
    } 

    private static int[] ParseRanges(string section, out string errorString) 
    { 
     errorString = null; 
     List<int> numbers = new List<int>(); 
     string[] items = section.Split(','); 

     foreach (string verse in items) 
     { 
      string[] ranges = verse.Split('-'); 

      if (ranges.Length > 2 || ranges.Length == 0) 
      { 
       errorString = "Invalid range specification"; 
       return new int[0]; 
      } 

      int start; 
      if(!int.TryParse(ranges[0], out start)) 
      { 
       errorString = "Invalid range specification"; 
       return new int[0]; 
      } 

      int end = start; 
      if(ranges.Length >1 && !int.TryParse(ranges[1], out end)) 
      { 
       errorString = "Invalid range specification"; 
       return new int[0]; 
      } 

      if (end < start) 
      { 
       errorString = "invalid range specification"; 
       return new int[0]; 
      } 

      for (int i = start; i <= end; i++) 
      { 
       numbers.Add(i); 
      } 
     } 

     return numbers.ToArray(); 
    } 
} 

Avec tout ce qui a mis en place, nous pouvons maintenant analyser tout texte des références bibliques. Cette méthode est aussi dans ma classe Reference:

public static ICollection<Reference> Scan(string text) 
    { 
     List<Reference> references = new List<Reference>(); 

     if (text == null) 
     { 
      return references; 
     } 

     string[] words = RemoveHtml.Replace(text, "").Split(' ', '(', ')', ';', '\r', '\n', '\t'); 

     for (int i = 0; i < words.Length; i++) 
     { 
      string one = words[i]; 

      // If we are starting with a blank entry, just skip this cycle 
      if(string.IsNullOrWhiteSpace(one)) 
      { 
       continue; 
      } 

      string two = i + 1 < words.Length ? string.Join(" ", one, words[i + 1]) : one; 
      string three = i + 2 < words.Length ? string.Join(" ", two, words[i + 2]) : two; 

      Book book; 
      bool match = Book.TryParse(one, out book); 
      match = match || Book.TryParse(two, out book); 
      match = match || Book.TryParse(three, out book); 

      if(match) 
      { 
       string four = i + 3 < words.Length ? string.Join(" ", three, words[i + 3]) : three; 
       string five = i + 4 < words.Length ? string.Join(" ", four, words[i + 4]) : four; 

       // Keep the most inclusive version of the reference 
       Reference found = null; 
       foreach(string test in new [] {two,three,four,five}) 
       { 
        Reference check; 
        if(TryParse(test, out check)) 
        { 
         found = check; 
        } 
       } 

       if(found != null && !references.Contains(found)) 
       { 
        references.Add(found); 
       } 
      } 
     } 

     return references; 
    } 

Cela va être la façon la plus robuste pour gérer ce que vous voulez et gérer les cas de coin, vous ne l'avez pas pris en compte. Le code gère le tri, l'égalité, et prend un ensemble de références et les réduit au plus petit ensemble (dans les transcriptions, nous travaillons généralement à travers un passage d'écriture, ce qui nous permet de créer la référence pour toute la gamme après avoir scanné toute la transcription).

+0

NOTE: traduire ceci dans votre langue (allemand, je pense?) Est un exercice pour vous. –

+1

Son norvégien. C'est agréable de voir d'autres personnes qui travaillent sur la bible dans C# haha ​​:) Great work mate! – blueio22

+0

Je les traduirai volontiers en norvégien, pour mon but. si vous voulez que je puisse vous envoyer la traduction? C'est un peu drôle, en anglais vous avez genèse, exode etc. nous avons 1 Mosebok et 2 Mosebok (genèse, exode) – blueio22

0

Laissez la chaîne de recherche en cours peut définir comme:

string searchString="1 Johannes 1:1"; 

changement simple vous donnera le résultat escompté, à savoir ajouter un espace de début et de fin de la chaîne de recherche.

string searchString=" 1 Johannes 1:1 "; 
+0

probablement préférable d'utiliser le '.EndWith()' car se terminant par '1: 1' n'est pas la même chose que '1: 12' – MethodMan

+0

Bonne idée, mais leur peut être une chance pour' "01 Johannes 1: 1" ' –

+1

vrai .. mais semble que nous avons besoin d'une meilleure clarification de l'OP avec un exemple exact de ce qu'ils essaient d'atteindre – MethodMan

0

vous devez supprimer tous les espaces blancs de vous chaîne et la chaîne que vous recherchez

searchString.Replace(" ", string.Empty); 

fullText.Replace(" ", string.Empty); 



fullText.Contains(searchString) 

ou si vous voulez une correspondance exacte, vous pouvez utiliser RegEx

bool contains = Regex.IsMatch(fullText, @"(^|\s)" + searchString + "(\s|$)"); 
0

si vous voulez la liste à venir en fonction de votre code que vous devez Sort() la sentenses Liste

List<string> sentences = new List<string>(); 
sentences.Add("1 Johannes 1:12"); 
sentences.Add("1 Johannes 1:1"); 
string fulltext = "randomtext 1 Johannes 1:12 randomtext"; 
sentences.Sort(); 
foreach(string item in sentences) 
{ 
    if(fulltext.Contains(item)) 
    { 
     //expect the result to be 1 Johannes 1:12, but the result is 1 Johannes 1:1 
     //do operation 
     Console.WriteLine(item);//try it in a Console App you will get the results in the order that you are expecting 
    } 
} 
Console.Read(); 
0

Ok bien ce texte intégral contient vos deux valeurs. de sorte que vous obtenez toujours la dernière valeur de votre liste si vous voulez obtenir la première valeur de ce filtre, vous pouvez utiliser quelque chose comme ceci:

string item1 = "1 Johannes 1:12"; 
string item2 = "1 Johannes 1:1"; 
string fullText= "randomtext 1 Johannes 1:12 randomtext"; 
string comparedValue =fullText.Replace(" ", string.Empty) 
string result ; 
List<string> sentences = new List<string>(); 
sentences.add(item1.Replace(" ", string.Empty)); 
sentences.add(item2.Replace(" ", string.Empty)); 
foreach(string item in sentences){ 
     if(comparedValue .Contains(item){ 
     result = item; 
    break; 

     } 

} 

Maintenant, vous pouvez utiliser résultat