/* * BotSharp.NLP Library * Copyright (C) 2018 Haiping Chen * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Text.RegularExpressions; namespace BotSharp.NLP.Tokenize { /// /// Penn Treebank Tokenizer /// The Treebank tokenizer uses regular expressions to tokenize text as in Penn Treebank. /// This implementation is a port of the tokenizer sed script written by Robert McIntyre /// and available at ftp://ftp.cis.upenn.edu/pub/treebank/public_html/tokenizer.sed, /// or reference ftp://ftp.cis.upenn.edu/pub/treebank/public_html/tokenization.html. /// public class TreebankTokenizer : ITokenizer { private List> STARTING_QUOTES = new List>(); private List> PUNCTUATION = new List>(); private List> PARENS_BRACKETS = new List>(); private List> CONVERT_PARENTHESES = new List>(); private List> ENDING_QUOTES = new List>(); private List> CONVENTIONS = new List>(); public TreebankTokenizer() { Init(); } public List Tokenize(string sentence, TokenizationOptions options) { string text = sentence; // starting quoting replace STARTING_QUOTES.ForEach(x => { text = Regex.Replace(text, x.Item1, x.Item2); }); // replace PUNCTUATION PUNCTUATION.ForEach(x => { text = Regex.Replace(text, x.Item1, x.Item2); }); // Handles parentheses. PARENS_BRACKETS.ForEach(x => { text = Regex.Replace(text, x.Item1, x.Item2); }); // convert parentheses if (options.ConvertParentheses) { CONVERT_PARENTHESES.ForEach(x => { text = Regex.Replace(text, x.Item1, x.Item2); }); } // Handles repeated dash. text = Regex.Replace(text, "(-{2,})", " $1 ").Trim(); // replace ending quotes ENDING_QUOTES.ForEach(x => { text = Regex.Replace(text, x.Item1, x.Item2); }); // replace ending quotes CONVENTIONS.ForEach(x => { text = Regex.Replace(text, x.Item1, x.Item2); }); // remove duplicated spaces text = Regex.Replace(text, "\\s+", " ") + " "; // split int pos = 0; var tokens = Regex.Matches(text, "\\s") .Cast() .Select(x => { var token = new Token { Start = pos, Text = text.Substring(pos, x.Index - pos) }; pos = x.Index + 1; return token; }).ToList(); // correct token position CorrectTokenPosition(sentence, tokens); return tokens; } private void CorrectTokenPosition(string sentence, List tokens) { int startPos = 0; for(int i = 0; i < tokens.Count; i++) { var token = tokens[i]; token.Start = sentence.IndexOf(token.Text, startPos); startPos = token.End; } } private void Init() { STARTING_QUOTES.Add(new Tuple(@"([«“‘„]|[`]+)", " $1 ")); STARTING_QUOTES.Add(new Tuple("^\"", "``")); STARTING_QUOTES.Add(new Tuple(@"(``)", " $1 ")); STARTING_QUOTES.Add(new Tuple("([ ([{<])(\" | '{2})", "$1 `` ")); PUNCTUATION.Add(new Tuple(@"([^\.])(\.)([\]\)}>" + "\"" + @"\\'»”’ ]*)\s*$", "$1 $2 $3 ")); PUNCTUATION.Add(new Tuple(@"([:,])([^\d])", " $1 $2")); PUNCTUATION.Add(new Tuple(@"([:,])$", " $1 ")); PUNCTUATION.Add(new Tuple(@"(\.\.\.)", " $1 ")); PUNCTUATION.Add(new Tuple(@"([;@#$%&])", " $1 ")); PUNCTUATION.Add(new Tuple(@"([^\.])(\.)([\]\)}>" + "\"" + @"']*)\s*$", "$1 $2 $3 ")); PUNCTUATION.Add(new Tuple(@"([?!])", " $1 ")); PUNCTUATION.Add(new Tuple(@"([^'])' ", "$1 ' ")); PARENS_BRACKETS.Add(new Tuple(@"([\]\[\(\)\{\}\<\>])", " $1 ")); CONVERT_PARENTHESES.Add(new Tuple(@"\(", "-LRB-")); CONVERT_PARENTHESES.Add(new Tuple(@"\)", "-RRB-")); CONVERT_PARENTHESES.Add(new Tuple(@"\[", "-LSB-")); CONVERT_PARENTHESES.Add(new Tuple(@"\]", "-RRB-")); CONVERT_PARENTHESES.Add(new Tuple(@"\{", "-LCB-")); CONVERT_PARENTHESES.Add(new Tuple(@"\}", "-RCB-")); ENDING_QUOTES.Add(new Tuple(@"([»”’])", " $1 ")); ENDING_QUOTES.Add(new Tuple("\"", " '' ")); ENDING_QUOTES.Add(new Tuple(@"(\S)(\'\')", "$1 $2 ")); ENDING_QUOTES.Add(new Tuple(@"('[sS]|'[mM]|'[dD]|') ", " $1 ")); ENDING_QUOTES.Add(new Tuple(@"('ll|'LL|'re|'RE|'ve|'VE|n't|N'T) ", " $1 ")); CONVENTIONS.Add(new Tuple(@"(?i)\b(can)(?#X)(not)\b", "$1 $2 ")); CONVENTIONS.Add(new Tuple(@"(?i)\b(d)(?#X)('ye)\b", "$1 $2 ")); CONVENTIONS.Add(new Tuple(@"(?i)\b(gim)(?#X)(me)\b", "$1 $2 ")); CONVENTIONS.Add(new Tuple(@"(?i)\b(gon)(?#X)(na)\b", "$1 $2 ")); CONVENTIONS.Add(new Tuple(@"(?i)\b(got)(?#X)(ta)\b", "$1 $2 ")); CONVENTIONS.Add(new Tuple(@"(?i)\b(lem)(?#X)(me)\b", "$1 $2 ")); CONVENTIONS.Add(new Tuple(@"(?i)\b(mor)(?#X)('n)\b", "$1 $2 ")); CONVENTIONS.Add(new Tuple(@"(?i)\b(wan)(?#X)(na)\s", "$1 $2 ")); CONVENTIONS.Add(new Tuple(@"(?i) ('t)(?#X)(is)\b", "$1 $2 ")); CONVENTIONS.Add(new Tuple(@"(?i) ('t)(?#X)(was)\b", "$1 $2 ")); } } }