【问题标题】:Is there a function in string that extracts a specific substring from all occurences of a delimiter?字符串中是否有一个函数可以从所有出现的分隔符中提取特定的子字符串?
【发布时间】:2019-07-24 22:52:08
【问题描述】:

例如,我有一个字符串,其中包含多个字符串,其中包含必须提取的标记,同时还删除重复项

{..}P1, {..}P2, {..}P3, {..}P3
{..}P4, {..}P1, {..}P6, {..}P5

是否有内置的字符串函数(或可以制作的函数)可用于创建此令牌?:

P1, P2, P3, P4, P5, P6

P 标记总是紧跟在一个结束大括号之后,中间没有空格。怎样才能达到效果?

【问题讨论】:

  • 使用Regex 模式提取并使用LINQ 进行处理。

标签: c# .net string


【解决方案1】:

如前所述,您可以使用正则表达式来解决您的问题。但是,如果您想在不使用正则表达式的情况下执行此操作,则可以在字符串上使用 split('}') 来创建子字符串集合,然后获取所有以 P 开头的子字符串,后跟一个数字,然后对结果进行排序并加入您的集合成一个新的字符串。 ?

【讨论】:

    【解决方案2】:

    不,但如果您在此解决方案中创建一个扩展方法,则可以使用 state machine 且不使用正则表达式。

    在不了解所有限制条件的情况下,这可能不是 100% 正确,但它确实产生了您正在寻找的正确输出。如有必要,您可以为此添加状态和约束。

    class Program
    {
        static void Main(string[] args)
        {
            var input = @"        {..}P1, {.sdfsdfsdf.}P2, {.sdfsdfsdf.}P3, {..}P3
        {..}P4, {..}P1, {..}P8886, {..}P5";
    
            var items = Extractinator(input).Distinct().OrderBy(x => x).ToList();
            Console.WriteLine(string.Join(", ", items));
            Console.ReadLine();
        }
    
        static IEnumerable<string> Extractinator(string source)
        {
            var state = States.Extraneous;
            string item = "";
            foreach (var c in source)
            {
                switch (state)
                {
                    case States.Extraneous:
                        switch (c)
                        {
                            case '{':
                                state = States.InsideBrackets;
                                break;
                        }
                        continue;
                    case States.InsideBrackets:
                        switch (c)
                        {
                            case '}':
                                state = States.BuildingItem;
                                break;
                        }
                        continue;
                    case States.BuildingItem:
                        switch (c)
                        {
                            case char _ when char.IsDigit(c) || char.IsLetter(c):
                                item += c;
                                break;
                            default:
                                yield return item;
                                item = "";
                                state = States.Extraneous;
                                break;
                        }
                        continue;
                }
            }
    
            if (item != "")
                yield return item;
        }
    
        enum States
        {
            Extraneous,
            InsideBrackets,
            BuildingItem,
        }
    }
    

    我修改了输入只是为了让它更真实一点。我还使用了switch 而不是if/else,因为这是一种“经典”方式,允许您在必要时添加新规则。例如,如果逗号, 确实是移动到无关文本的信号,那么在case States.BuildingItem 中,您可能希望将case char _ when char.IsDigit(c) || char.IsLetter(c): 替换为case char _ when c != ',':

    像这样的状态机的关键是缓冲部分结果,在完成时产生/处理结果,并知道何时切换状态。

    【讨论】:

      【解决方案3】:

      假设你的意思是你的字符串(我用src)里面有多行,这应该可以工作:

      var pRE = new Regex(@"(?<=})[^,\r\n]+", RegexOptions.Compiled);
      var ans = String.Join(", ", pRE.Matches(src).Cast<Match>().Select(m => m.Value).Distinct());
      

      但是我没有对 Px 进行排序。

      这是一个非基于 RE 的版本,它使用了我有 UpTo 的扩展方法,它接受一个停止字符数组并返回字符串直到第一次出现,或者如果没有出现则返回整个字符串。

      var ans = src.Split('}').Skip(1).Select(s => s.UpTo(',', '\r', '\n')).Distinct();
      

      这是扩展名:

      public static class Extensions {
          public static string UpTo(this string s, params char[] stopChars) {
              var stopPos = s.IndexOfAny(stopChars);
              return (stopPos >= 0) ? s.Substring(0, stopPos) : s;
          }    
          public static T MinOrDefault<T>(this IEnumerable<T> src, T defval = default) => src.DefaultIfEmpty(defval).Min();
      }
      

      【讨论】:

      • 我喜欢这个解决方案。你能在没有正则表达式的情况下实现这个吗? @NetMage
      • @Shylif,你能详细说明不使用正则表达式的原因吗?这感觉像是一项任务。您真的希望社区为您解决问题吗??
      • 这不是一项任务,我只是希望看到另一个解决方案来比较速度和内存效率。
      • @Shylif 我使用 LINQ 和 Split 添加了一个非 RE 解决方案。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2015-08-01
      • 1970-01-01
      • 2021-08-25
      • 2012-09-14
      • 2018-02-05
      • 1970-01-01
      • 2021-03-30
      相关资源
      最近更新 更多