C#在字符串两边按顺序修剪所有需要的字符和字符集

问题描述 投票:0回答:2

我的输入由一串不确定的长度和内容组成。我需要在开头和结尾修剪所有空格、字符

#
\t
和片段
//
/*
*/
。所有这些符号和字符集都相继出现。如果他们之间还有其他事情,你需要停下来。

例子:

"///g, hhh/ , test" ===> "/g, hhh/ , test"

"//*g, hhh/ , test " ===> "*g, hhh/ , test"

"#/test" ===> "/test"

"hello  //" ===> "hello"

重要说明: 我需要知道我在开头修剪了多少个字符,在最后修剪了多少个字符。

另外,我不想使用正则表达式,因为这段代码对性能非常敏感。据我所知,正则表达式非常慢。但是,如果我的任务不能用循环完成,或者类似的事情非常困难——我准备使用正则表达式。

到目前为止,我已经尝试过这样的代码。由于某些符号代表两个相互跟随的字符,因此任务变得复杂。

private const char specialSymbol_1 = '/';
private const char specialSymbol_2 = '*';
private const char specialSymbol_3 = '#';

private void ObserveTrim(ref string target, ref int start, ref int end) {
    int s = 0; int e = 0;
    
    for (int i = 0; i < target.Length; ++i) {
        char c = target[i];
        
        bool flag = false;
        if (target.Length > 1) {
            if (i == 0) {
                flag = c == specialSymbol_1 && target[i + 1] == specialSymbol_1 ||
                    c == specialSymbol_1 && target[i + 1] == specialSymbol_2 ||
                    c == specialSymbol_2 && target[i + 1] == specialSymbol_1;
            }
            else if (i == target.Length - 1) {
                flag = c == specialSymbol_1 && target[i - 1] == specialSymbol_1 ||
                    c == specialSymbol_1 && target[i - 1] == specialSymbol_2 ||
                    c == specialSymbol_2 && target[i - 1] == specialSymbol_1;
            }
            else {
                flag = c == specialSymbol_1 && target[i + 1] == specialSymbol_1 ||
                    c == specialSymbol_1 && target[i - 1] == specialSymbol_1 ||
                    c == specialSymbol_1 && target[i + 1] == specialSymbol_2 ||
                    c == specialSymbol_1 && target[i - 1] == specialSymbol_2;
            }
        }
        
        if (flag) continue;
        
        if (!char.IsWhiteSpace(c) && c != specialSymbol_3) {
            s = i;
            break;
        }
    }
    
    for (int i = target.Length - 1; i >= 0; --i) {
        char c = target[i];
        
        bool flag = false;
        if (target.Length > 1) {
            if (i == 0) {
                flag = c == specialSymbol_1 && target[i + 1] == specialSymbol_1 ||
                    c == specialSymbol_1 && target[i + 1] == specialSymbol_2 ||
                    c == specialSymbol_2 && target[i + 1] == specialSymbol_1;
            }
            else if (i == target.Length - 1) {
                flag = c == specialSymbol_1 && target[i - 1] == specialSymbol_1 ||
                    c == specialSymbol_1 && target[i - 1] == specialSymbol_2 ||
                    c == specialSymbol_2 && target[i - 1] == specialSymbol_1;
            }
            else {
                flag = c == specialSymbol_1 && target[i + 1] == specialSymbol_1 ||
                    c == specialSymbol_1 && target[i - 1] == specialSymbol_1 ||
                    c == specialSymbol_1 && target[i + 1] == specialSymbol_2 ||
                    c == specialSymbol_1 && target[i - 1] == specialSymbol_2;
            }
        }
        
        if (flag) continue;
        
        if (!char.IsWhiteSpace(c) && c != specialSymbol_3) {
            e = target.Length - 1 - i;
            break;
        }
    }
    
    start += s;
    end -= e;
    target = target.Substring(s, target.Length - s - e);
}

但是这段代码没有按预期工作。 例子:

"///g, hhh/ , test" ===> "g, hhh/ , test"
"/*g, hhh/ , test" ===> "*g, hhh/ , test"

这些只是他错误工作的几个例子,实际上有几十个。

它根本不考虑某些字符或它们的顺序。我不擅长这样的算法,欢迎任何帮助。

c# string trim
2个回答
1
投票

虽然所有字符串都指向

trim
,但我建议用
StartsWith
EndsWith
检查所有字符串。在
ReadOnlySpan<char>
的帮助下,我们可以在不创建许多不需要的子字符串的情况下做到这一点。

既然你想得到

3
参数-
result
(修剪字符串),从
left
right
中删除了多少个符号,让我们将它们组合成一个元组:

代码:

  public static (string result, int left, int right) MyTrim(
    string value, params string[] trim) {

    if (string.IsNullOrEmpty(value) || trim is null || trim.Length == 0)
      return (value, 0, 0);

    int trimmedLeft = 0;
    int trimmedRight = 0;  
      
    var span = value.AsSpan();

    for (bool keep = true; keep; ) {
      keep = false;

      foreach (var item in trim)
        if (!string.IsNullOrEmpty(item) && span.StartsWith(item)) {
          trimmedLeft += item.Length;   
          span = span.Slice(item.Length);
          keep = true;

          break;
        }
    }

    for (bool keep = true; keep; ) {
      keep = false;

      foreach (var item in trim)
        if (!string.IsNullOrEmpty(item) && span.EndsWith(item)) {
          trimmedRight += item.Length;   
          span = span.Slice(0, span.Length - item.Length);
          keep = true;

          break;
        }
    }

    return (span.ToString(), trimmedLeft, trimmedRight);
  } 

用法:

string value = "///g, hhh/ , test";

(string result, int left, int right) = MyTrim(
  value, " ", "#", "\t", "//", "/*", "*/");

演示:

using System.Linq;

...

string[] tests = new string[] {
  "///g, hhh/ , test",
  "//*g, hhh/ , test ", 
  "#/test",
  "hello  //",
};
      
var report = string.Join(Environment.NewLine, tests
  .Select(test => (test, result : MyTrim(test, " ", "#", "\t", "//", "/*", "*/")))                       
  .Select(pair => $"{pair.test,30} ===> {pair.result.result} (left: {pair.result.left}; right: {pair.result.right}) "));  
                
Console.WriteLine(report);

输出:

             ///g, hhh/ , test ===> /g, hhh/ , test (left: 2; right: 0) 
            //*g, hhh/ , test  ===> *g, hhh/ , test (left: 2; right: 1) 
                        #/test ===> /test (left: 1; right: 0) 
                     hello  // ===> hello (left: 0; right: 4) 

小提琴


0
投票

尽管 Dmitry 的回答非常详尽,但我无法以其原始形式应用它,因为它受到编译器的限制。

我正在为一个程序编写脚本,我什至不知道它支持什么语言版本。在这方面,我重写了代码并只使用了我设法编译的那些结构。我为那些发现自己处于我的情况的人发布此降级。遗憾的是我们不得不牺牲便利性和性能。 但是,我会将他的回答标记为解决方案,因为现代大多数编译器都会处理这段代码。

我还部分编辑了问题,使问题及其解决方案对于遇到类似问题的人来说更具描述性。

谢谢大家的帮助。

private const char specialSymbol_1 = '/';
private const char specialSymbol_2 = '*';
private const char specialSymbol_3 = '#';

private void CheckContent() {
    int leftWordStart = ...;
    int leftWordEnd = ...;
    string leftWord = ...;
    MyTrim(ref leftWord, ref leftWordStart, ref leftWordEnd,
        new string[6] { " ", "\t", new string(specialSymbol_1, 2), new string(new char[2] {specialSymbol_1, specialSymbol_2}), 
            new string(new char[2] {specialSymbol_2, specialSymbol_1}), specialSymbol_3.ToString()});
    //...
}

private void MyTrim(ref string target, ref int start, ref int end, string[] trim) {
    if (string.IsNullOrWhiteSpace(target) || trim == null || trim.Length == 0) return;

    int trimmedLeft = 0;
    int trimmedRight = 0;

    bool keep = true;
    while (keep) {
        keep = false;

        for (int i = 0; i < trim.Length; ++i) {
            string item = trim[i];
            if (!string.IsNullOrEmpty(item) && target.StartsWith(item)) {
                trimmedLeft += item.Length;   
                target = target.Substring(item.Length);
                keep = true;

                break;
            }
        }
    }
    
    keep = true;
    while (keep) {
        keep = false;

        for (int i = 0; i < trim.Length; ++i) {
            string item = trim[i];
            if (!string.IsNullOrEmpty(item) && target.EndsWith(item)) {
                trimmedRight += item.Length;
                target = target.Substring(0, target.Length - item.Length);
                keep = true;

                break;
            }
        }
    }
    
    start += trimmedLeft;
    end -= trimmedRight;
}

我的问题收到的否决票数量给我留下了深刻的印象,考虑到没有任何重复并且问题本身并不完全标准。

© www.soinside.com 2019 - 2024. All rights reserved.