【问题标题】:How to split text into words?如何将文本拆分为单词?
【发布时间】:2021-07-26 14:24:15
【问题描述】:

如何将文本拆分成单词?

示例文本:

'哦,你没办法,'猫说:'我们都疯了。我生气了。你疯了。'

该行中的单词是:

  1. 不能
  2. 帮助
  3. 那个
  4. 我们是
  5. 全部
  6. 疯了
  7. 这里
  8. 我是
  9. 疯了
  10. 你是
  11. 疯了

【问题讨论】:

  • 我的建议:首先定义一个明确的词法语法,然后为该语法编写一个词法分析器,生成一系列标记。然后拒绝没有被引入“单词”产生的标记。这不是正则表达式的工作。
  • 我真的很喜欢 Eric 的回复。我知道我参加聚会有点晚了,但这是最好的方式。
  • 我收集了上面所有的 delimiter 并且发现了类似这样的结果。Split({ " '" , " " , ",'" , ": '" , "." , ".'" }, StringSplitOptions.RemoveEmptyEntries);

标签: c# .net


【解决方案1】:

在空白处分割文本,然后修剪标点符号。

var text = "'Oh, you can't help that,' said the Cat: 'we're all mad here. I'm mad. You're mad.'";
var punctuation = text.Where(Char.IsPunctuation).Distinct().ToArray();
var words = text.Split().Select(x => x.Trim(punctuation));

完全符合例子。

【讨论】:

    【解决方案2】:

    首先,删除所有特殊字符:

    var fixedInput = Regex.Replace(input, "[^a-zA-Z0-9% ._]", string.Empty);
    // This regex doesn't support apostrophe so the extension method is better
    

    然后拆分:

    var split = fixedInput.Split(' ');
    

    对于删除特殊字符的更简单的 C# 解决方案(您可以轻松更改),添加此扩展方法(我添加了对撇号的支持):

    public static string RemoveSpecialCharacters(this string str) {
       var sb = new StringBuilder();
       foreach (char c in str) {
          if ((c >= '0' && c <= '9') || (c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z') || c == '\'' || c == ' ') {
             sb.Append(c);
          }
       }
       return sb.ToString();
    }
    

    然后像这样使用它:

    var words = input.RemoveSpecialCharacters().Split(' ');
    

    你会惊讶地发现这种扩展方法非常有效(肯定比 Regex 更有效),所以我建议你使用它;)

    更新

    我同意这是一种仅限英语的方法,但要使其与 Unicode 兼容,您只需替换:

    (c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z')
    

    与:

    char.IsLetter(c)
    

    其中支持Unicode,.Net还为您提供char.IsSymbolchar.IsLetterOrDigit的各种情况

    【讨论】:

    • 我不认为数字是单词的一部分——但我想这取决于 OP
    • 我猜这取决于他,他可以随意更改正则表达式。
    • 我看到的唯一问题是您的解决方案会修剪宫缩的撇号。前任。将“不是”改为“不是”
    • 是的,我也看到了,当你写评论时,我改进了我的解决方案。
    • 好像他只是在寻找一个快速的字数统计,s.Split(' ').Length
    【解决方案3】:

    只是为了在@Adam Fridental 的答案上添加一个非常好的变体,你可以试试这个正则表达式:

    var text = "'Oh, you can't help that,' said the Cat: 'we're all mad here. I'm mad. You're mad.'";
    
    var matches = Regex.Matches(text, @"\w+[^\s]*\w+|\w");
    
    foreach (Match match in matches) {
        var word = match.Value;
    }
    

    我相信这是能得到所有单词的最短正则表达式

    \w+[^\s]*\w+|\w
    

    【讨论】:

    • 不错。但正如我在回答中所说,在用正则表达式解决这个问题时有一件事是有问题的 - 我检查了它所花费的时间,我在回答中写的扩展方法比正则表达式解析快 X7。
    • 感谢您对他们进行分析,我今天学到了一些新东西 :) 你有我的支持。我会一直争论(这就是我的本性)正则表达式来降低代码复杂性,但是你的方法也很短,大多数人并不觉得正则表达式像我一样友好。哦,好吧。
    • 我同意 Refex 很棒。当你有一秒钟的等待时:)
    • 一般来说,这是更好的解决方案,因为它可以处理任何 Unicode 单词字符。如果需要,您也可以对其进行修改以处理撇号和数字。尽管撇号可能很难正确处理。如果你能保证英文文本,更快的方法很好,但否则它会失败。
    【解决方案4】:

    如果您不想使用 Regex 对象,您可以执行类似...

    string mystring="Oh, you can't help that,' said the Cat: 'we're all mad here. I'm mad. You're mad.";
    List<string> words=mystring.Replace(",","").Replace(":","").Replace(".","").Split(" ").ToList();
    

    您仍然需要处理“that,”末尾的尾随撇号

    【讨论】:

      【解决方案5】:

      这是一种解决方案,我不使用任何辅助类或方法。

              public static List<string> ExtractChars(string inputString) {
                  var result = new List<string>();
                  int startIndex = -1;
                  for (int i = 0; i < inputString.Length; i++) {
                      var character = inputString[i];
                      if ((character >= 'a' && character <= 'z') ||
                          (character >= 'A' && character <= 'Z')) {
                          if (startIndex == -1) {
                              startIndex = i;
                          }
                          if (i == inputString.Length - 1) {
                              result.Add(GetString(inputString, startIndex, i));
                          }
                          continue;
                      }
                      if (startIndex != -1) {
                          result.Add(GetString(inputString, startIndex, i - 1));
                          startIndex = -1;
                      }
                  }
                  return result;
              }
      
              public static string GetString(string inputString, int startIndex, int endIndex) {
                  string result = "";
                  for (int i = startIndex; i <= endIndex; i++) {
                      result += inputString[i];
                  }
                  return result;
              }
      

      【讨论】:

        【解决方案6】:

        您可以尝试使用正则表达式删除未被字母包围的撇号(即单引号),然后使用 Char 静态方法删除所有其他字符。通过首先调用正则表达式,您可以保留收缩撇号(例如can't),但删除像'Oh 中的单引号。

        string myText = "'Oh, you can't help that,' said the Cat: 'we're all mad here. I'm mad. You're mad.'";
        
        Regex reg = new Regex("\b[\"']\b");
        myText = reg.Replace(myText, "");
        
        string[] listOfWords = RemoveCharacters(myText);
        
        public string[] RemoveCharacters(string input)
        {
            StringBuilder sb = new StringBuilder();
            foreach (char c in input)
            {
                if (Char.IsLetter(c) || Char.IsWhiteSpace(c) || c == '\'')
                   sb.Append(c);
            }
        
            return sb.ToString().Split(' ');
        }
        

        【讨论】:

          【解决方案7】:

          如果您想使用“for 循环”来检查每个字符并保存输入字符串中的所有标点符号,我已经创建了这个类。 GetSplitSentence() 方法返回一个 SentenceSplitResult 列表。在此列表中,保存了所有单词和所有标点符号和数字。保存的每个标点符号或数字都是列表中的一个项目。 sentenceSplitResult.isAWord 用于检查是否是一个单词。 [对不起我的英语]

          public class SentenceSplitResult
          {
              public string word;
              public bool isAWord;
          }
          
          public class StringsHelper
          {
          
              private readonly List<SentenceSplitResult> outputList = new List<SentenceSplitResult>();
          
              private readonly string input;
          
              public StringsHelper(string input)
              {
                  this.input = input;
              }
          
              public List<SentenceSplitResult> GetSplitSentence()
              {
                  StringBuilder sb = new StringBuilder();
          
                  try
                  {
                      if (String.IsNullOrEmpty(input)) {
                          Logger.Log(new ArgumentNullException(), "GetSplitSentence - input is null or empy");
                          return outputList;                    
                      }
          
                      bool isAletter = IsAValidLetter(input[0]);
          
                      // Each char i checked if is a part of a word.
                      // If is YES > I can store the char for later
                      // IF is NO > I Save the word (if exist) and then save the punctuation
                      foreach (var _char in input)
                      {
                          isAletter = IsAValidLetter(_char);
          
                          if (isAletter == true)
                          {
                              sb.Append(_char);
                          }
                          else
                          {
                              SaveWord(sb.ToString());
                              sb.Clear();
                              SaveANotWord(_char);                        
                          }
                      }
          
                      SaveWord(sb.ToString());
          
                  }
                  catch (Exception ex)
                  {
                      Logger.Log(ex);
                  }
          
                  return outputList;
          
              }
          
              private static bool IsAValidLetter(char _char)
              {
                  if ((Char.IsPunctuation(_char) == true) || (_char == ' ') || (Char.IsNumber(_char) == true))
                  {
                      return false;
                  }
                  return true;
              }
          
              private void SaveWord(string word)
              {
                  if (String.IsNullOrEmpty(word) == false)
                  {
                      outputList.Add(new SentenceSplitResult()
                      {
                          isAWord = true,
                          word = word
                      });                
                  }
              }
          
              private void SaveANotWord(char _char)
              {
                  outputList.Add(new SentenceSplitResult()
                  {
                      isAWord = false,
                      word = _char.ToString()
                  });
              }
          

          【讨论】:

            猜你喜欢
            • 2010-10-13
            • 1970-01-01
            • 2010-10-21
            • 2021-10-16
            • 2020-12-15
            • 2016-09-16
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多