[英]How Can I Parse String and Get Random Sentences in C#?
我試圖弄清楚如何將這種格式的字符串解析成樹狀的任意深度的數據結構。 然后隨機造句。
"{{Hello,Hi,Hey} {world,earth},{Goodbye,farewell} {planet,rock,globe{.,!}}}"
在哪里
, means or
{ means expand
} means collapse up to parent
例如,我想像這樣得到 output:
1) hello world planet.
2) hi earth globe!
3) goodby planet.
and etc.
我認為這可能是一項復雜的工作,因為我使用了本教程,我強烈建議您閱讀整個頁面以了解其工作原理。
首先,您必須將此“樹”作為數組傳遞。 您可以解析字符串,手動設置數組或其他任何東西。 這很重要,因為該樹 model 沒有一個好的 model,所以最好使用已經可用的 model。 此外,重要的是,如果您想設置正確的語法,您需要為這些單詞添加“權重”並告訴代碼如何正確設置以及以什么順序進行設置。
這是代碼片段:
using System;
using System.Text;
namespace App
{
class Program
{
static void Main(string[] args)
{
string tree = "{{Hello,Hi,Hey} {world,earth},{Goodbye,farewell} {planet,rock,globe{.,!}}}";
string[] words = { "Hello", "Hi", "Hey", "world", "earth", "Goodbye", "farewell", "planet", "rock", "globe" };
RandomText text = new RandomText(words);
text.AddContentParagraphs(12, 1, 3, 3, 3);
string content = text.Content;
Console.WriteLine(content);
}
}
public class RandomText
{
static Random _random = new Random();
StringBuilder _builder;
string[] _words;
public RandomText(string[] words)
{
_builder = new StringBuilder();
_words = words;
}
public void AddContentParagraphs(int numberParagraphs, int minSentences,
int maxSentences, int minWords, int maxWords)
{
for (int i = 0; i < numberParagraphs; i++)
{
AddParagraph(_random.Next(minSentences, maxSentences + 1),
minWords, maxWords);
_builder.Append("\n\n");
}
}
void AddParagraph(int numberSentences, int minWords, int maxWords)
{
for (int i = 0; i < numberSentences; i++)
{
int count = _random.Next(minWords, maxWords + 1);
AddSentence(count);
}
}
void AddSentence(int numberWords)
{
StringBuilder b = new StringBuilder();
// Add n words together.
for (int i = 0; i < numberWords; i++) // Number of words
{
b.Append(_words[_random.Next(_words.Length)]).Append(" ");
}
string sentence = b.ToString().Trim() + ". ";
// Uppercase sentence
sentence = char.ToUpper(sentence[0]) + sentence.Substring(1);
// Add this sentence to the class
_builder.Append(sentence);
}
public string Content
{
get
{
return _builder.ToString();
}
}
}
}
必須解析輸入字符串。 由於它可以包含嵌套的大括號,我們需要一個遞歸解析器。 但首先,我們需要一個數據 model 來表示樹結構。
我們可以在這棵樹中擁有三種不同類型的項目:文本、表示序列的列表和表示選擇的列表。 讓我們從這個抽象基礎 class 派生三個類:
abstract public class TreeItem
{
public abstract string GetRandomSentence();
}
TextItem
class 只是將其文本作為“隨機句子”返回:
public class TextItem : TreeItem
{
public TextItem(string text)
{
Text = text;
}
public string Text { get; }
public override string GetRandomSentence()
{
return Text;
}
}
該序列連接其項目的文本:
public class SequenceItem : TreeItem
{
public SequenceItem(List<TreeItem> items)
{
Items = items;
}
public List<TreeItem> Items { get; }
public override string GetRandomSentence()
{
var sb = new StringBuilder();
foreach (var item in Items) {
sb.Append(item.GetRandomSentence());
}
return sb.ToString();
}
}
選擇項是唯一使用隨機性從列表中選擇一個隨機項的項:
public class ChoiceItem : TreeItem
{
private static readonly Random _random = new();
public ChoiceItem(List<TreeItem> items)
{
Items = items;
}
public List<TreeItem> Items { get; }
public override string GetRandomSentence()
{
int index = _random.Next(Items.Count);
return Items[index].GetRandomSentence();
}
}
請注意,序列和選擇項都在其項上遞歸調用GetRandomSentence()
以遞歸地下降樹。
這是最容易的部分。 現在讓我們創建一個解析器。
public class Parser
{
enum Token { Text, LeftBrace, RightBrace, Comma, EndOfString }
int _index;
string _definition;
Token _token;
string _text; // If token is Token.Text;
public TreeItem Parse(string definition)
{
_index = 0;
_definition = definition;
GetToken();
return Choice();
}
private void GetToken()
{
if (_index >= _definition.Length) {
_token = Token.EndOfString;
return;
}
switch (_definition[_index]) {
case '{':
_index++;
_token = Token.LeftBrace;
break;
case '}':
_index++;
_token = Token.RightBrace;
break;
case ',':
_index++;
_token = Token.Comma;
break;
default:
int startIndex = _index;
do {
_index++;
} while (_index < _definition.Length & !"{},".Contains(_definition[_index]));
_text = _definition[startIndex.._index];
_token = Token.Text;
break;
}
}
private TreeItem Choice()
{
var items = new List<TreeItem>();
while (_token != Token.EndOfString && _token != Token.RightBrace) {
items.Add(Sequence());
if (_token == Token.Comma) {
GetToken();
}
}
if (items.Count == 0) {
return new TextItem("");
}
if (items.Count == 1) {
return items[0];
}
return new ChoiceItem(items);
}
private TreeItem Sequence()
{
var items = new List<TreeItem>();
while (true) {
if (_token == Token.Text) {
items.Add(new TextItem(_text));
GetToken();
} else if (_token == Token.LeftBrace) {
GetToken();
items.Add(Choice());
if (_token == Token.RightBrace) {
GetToken();
}
} else {
break;
}
}
if (items.Count == 0) {
return new TextItem("");
}
if (items.Count == 1) {
return items[0];
}
return new SequenceItem(items);
}
}
它由一個詞法分析器組成,即一種將輸入文本拆分為標記的低級機制。 我們有四種標記:文本、“{”、“}”和“,”。 我們將這些代幣表示為
enum Token { Text, LeftBrace, RightBrace, Comma, EndOfString }
我們還添加了一個EndOfString
標記來告訴解析器已到達輸入字符串的結尾。 當標記為Text
時,我們將此文本存儲在字段_text
中。 詞法分析器由GetToken()
方法實現,該方法沒有返回值,而是設置_token
字段,以使當前令牌在兩個解析方法Choice()
和Sequence()
中可用。
一個困難是,當我們遇到一個項目時,我們不知道它是單個項目還是序列或選擇的一部分。 我們假設整個句子定義是一個由序列組成的選擇,這使得序列優先於選擇(就像數學中的“*”優先於“+”)。
Choice
和Sequence
都將項目收集在一個臨時列表中。 如果此列表僅包含一項,則將返回此項目,而不是選擇列表或序列列表。
你可以像這樣測試這個解析器:
const string example = "{{Hello,Hi,Hey} {world,earth},{Goodbye,farewell} {planet,rock,globe{.,!}}}";
var parser = new Parser();
var tree = parser.Parse(example);
for (int i = 0; i < 20; i++) {
Console.WriteLine(tree.GetRandomSentence());
}
output 可能如下所示:
再見搖滾
你好地球
再見地球。
嘿世界
再見搖滾
你好地球
嘿地球
告別星球
再見地球。
嘿世界
再見星球
你好世界
你好世界
再見星球
嘿地球
告別地球儀!
再見地球。
再見地球。
再見星球
告別搖滾
如果問題是如何解析文本。 我想也許你可以使用堆棧來解析它。
"{{Hello,Hi,Hey} {world,earth},{Goodbye,farewell} {planet,rock,globe{.,!}}}"
基本上,當您讀取 char 不是 '}' 時,您將 char 推入堆棧。 當你得到一個'}'時,你會從堆棧中彈出很多次,直到你到達一個'{'。 但它有更多細節,因為你有一個規則“,”用於OR
。
解析就像通過堆棧進行計算。 這就是你處理方程括號的方式。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.