我的输入由一串不确定的长度和内容组成。我需要在开头和结尾修剪所有空格、字符
#
\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"
这些只是他错误工作的几个例子,实际上有几十个。
它根本不考虑某些字符或它们的顺序。我不擅长这样的算法,欢迎任何帮助。
由于我的问题引起了如此大的反对票(我很惊讶),我将把任务扩展到单一方法之外。也许这会把事情搞清楚一点。
我的任务是交换任意两段用逗号分隔的文本。在这样做时,我必须考虑到最里面的括号
{}
[]
()
<>
,并且只在其中移动文本。
在这样做时,我应该只考虑文本本身,而不应该考虑空格,
#
//
/*
*/
等字符。目前,在换行时只考虑字符是行不通的,这个词是连同这些字符一起换行的。这是完整的代码。
using EnvDTE; using EnvDTE80;
public class C : VisualCommanderExt.ICommand {
public void Run(EnvDTE80.DTE2 DTE, Microsoft.VisualStudio.Shell.Package package) {
EnvDTE.TextSelection ts = DTE.ActiveDocument.Selection as EnvDTE.TextSelection;
EnvDTE.TextDocument doc = DTE.ActiveDocument.Object("TextDocument") as EnvDTE.TextDocument;
EnvDTE.EditPoint p = doc.CreateEditPoint();
p.MoveToLineAndOffset(ts.TopLine, 1);
string text = p.GetText(p.LineLength);
if (!string.IsNullOrWhiteSpace(text)) {
StatementConstructor c = new StatementConstructor(text, ts);
c.Process();
c.Destroy();
}
}
class StatementConstructor {
private const char openSymbol_1 = '['; private const char openSymbol_2 = '(';
private const char openSymbol_3 = '<'; private const char openSymbol_4 = '{';
private const char closeSymbol_1 = ']'; private const char closeSymbol_2 = ')';
private const char closeSymbol_3 = '>'; private const char closeSymbol_4 = '}';
private const char separator = ',';
private const char specialSymbol_1 = '/'; private const char specialSymbol_2 = '*';
private const char specialSymbol_3 = '#';
private string text;
private EnvDTE.TextSelection ts;
public StatementConstructor(string text, EnvDTE.TextSelection ts) {
this.text = text; this.ts = ts;
}
public void Process() {
int startIndex = ts.AnchorPoint.LineCharOffset - 1;
if (CheckTextOnPoint(startIndex)) {
int start; int end;
string content = FindContent(startIndex, out start, out end);
int rightStart; int rightEnd; string rightText; int leftStart; int leftEnd; string leftText;
if (CheckContent(content, startIndex - start + 1, out rightStart, out rightEnd, out rightText, out leftStart, out leftEnd, out leftText)) {
string fullS = text.Substring(0, start)
+ SwapStrings(content, leftText, leftStart, leftEnd, rightText, rightStart, rightEnd)
+ text.Substring(end + 1);
//var charPos = ts.AnchorPoint.AbsoluteCharOffset - leftText.Length - 2;
//ts.GotoLine(ts.AnchorPoint.Line, true);
//ts.Insert(fullS, 4);
//ts.MoveToAbsoluteOffset(charPos, false);
System.Windows.MessageBox.Show(fullS);
}
}
}
private string SwapStrings(string text, string firstText, int firstStart, int firstEnd, string secondText, int secondStart, int secondEnd) {
string newS = text.Substring(0, firstStart)
+ secondText
+ text.Substring(firstEnd + 1, secondStart - firstEnd - 1)
+ firstText + text.Substring(secondEnd + 1);
return newS;
}
private bool CheckContent(string text, int startIndex, out int rightStart, out int rightEnd, out string rightText, out int leftStart, out int leftEnd, out string leftText) {
rightStart = -1; rightEnd = -1; rightText = null;
leftStart = -1; leftEnd = -1; leftText = null;
if (startIndex > text.Length) startIndex = text.Length;
if (CheckTextOnPointS(text, startIndex)) {
bool isSepOnPoint = text[startIndex - 1] == separator;
int leftSep = startIndex == 0 ? -1 : text.LastIndexOf(separator, isSepOnPoint ? startIndex - 2 : startIndex - 1);
if (leftSep != -1) {
int leftWordIndex = leftSep - 1;
int leftWordSep = leftWordIndex <= 0 ? -1 : text.LastIndexOf(separator, leftWordIndex - 1);
int leftWordStart = leftWordSep + 1;
int leftWordEnd = leftSep - 1;
string leftWord = text.Substring(leftWordStart, leftWordEnd - leftWordStart + 1);
ObserveTrim(ref leftWord, ref leftWordStart, ref leftWordEnd);
int rightSep = isSepOnPoint ? startIndex - 1 : text.IndexOf(separator, startIndex);
System.Windows.MessageBox.Show(leftWordStart.ToString() + " " + leftWordEnd.ToString());
int rightWordStart = leftSep + 1;
int rightWordEnd = rightSep != -1 ? rightSep - 1 : text.Length - 1;
string rightWord = text.Substring(rightWordStart, rightWordEnd - rightWordStart + 1);
ObserveTrim(ref rightWord, ref rightWordStart, ref rightWordEnd);
leftStart = leftWordStart;
leftEnd = leftWordEnd;
leftText = leftWord;
rightStart = rightWordStart;
rightEnd = rightWordEnd;
rightText = rightWord;
return true;
}
}
return false;
}
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 (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 (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);
}
private string FindContent(int startIndex, out int startC, out int endC) {
int start; int end;
int type = 0;
if (CheckBounds(startIndex, openSymbol_1, closeSymbol_1, out start, out end)) type = 1;
int newStart; int newEnd;
if (CheckBounds(startIndex, openSymbol_2, closeSymbol_2, out newStart, out newEnd)) {
if (newStart >= start && newEnd <= end) { start = newStart; end = newEnd; type = 2; }
}
if (CheckBounds(startIndex, openSymbol_3, closeSymbol_3, out newStart, out newEnd)) {
if (newStart >= start && newEnd <= end) { start = newStart; end = newEnd; type = 3; }
}
if (CheckBounds(startIndex, openSymbol_4, closeSymbol_4, out newStart, out newEnd)) {
if (newStart >= start && newEnd <= end) { start = newStart; end = newEnd; type = 4; }
}
startC = type < 1 ? start : start + 1; endC = type < 1 ? end : end - 1;
if (type < 1) return text;
else return text.Substring(start + 1, end - start - 1);
}
private bool CheckBounds(int startIndex, char openBound, char closeBound, out int start, out int end) {
start = 0;
end = text.Length - 1;
if (startIndex >= text.Length) return false;
bool hasOpen = false; bool hasClose = false;
for (int i = startIndex; i < text.Length; ++i) {
if (text[i] == closeBound) {
end = i;
hasClose = true;
break;
}
}
for (int i = startIndex - 1; i >= 0; --i) {
if (text[i] == openBound) {
start = i;
hasOpen = true;
break;
}
}
return hasOpen && hasClose;
}
private bool CheckTextOnPointS(string text, int startIndex) {
return startIndex >= 0 &&
(startIndex == text.Length ? IsTextS(text[startIndex - 1]) :
(startIndex == 0 ? IsTextS(text[startIndex]) : (IsTextS(text[startIndex]) || IsTextS(text[startIndex - 1]))));
}
private bool IsTextS(char t) {
return t != separator;
}
private bool CheckTextOnPoint(int startIndex) {
return startIndex >= 0 && (startIndex == text.Length ? IsText(text[startIndex - 1]) : (startIndex == 0 ? IsText(text[startIndex]) : (IsText(text[startIndex]) || IsText(text[startIndex - 1]))));
}
private bool IsText(char t) {
if (t == openSymbol_1 || t == openSymbol_2 || t == openSymbol_3 || t == openSymbol_4 ||
t == closeSymbol_1 || t == closeSymbol_2 || t == closeSymbol_3 || t == closeSymbol_4) {
return false;
} else return true;
}
public void Destroy() {
ts = null;
}
}
}
虽然所有字符串都指向
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)