我需要将字符串中的非字母数字字形转换为它们的 unicode 值,同时保留字母数字字符。有没有一种方法可以在 C# 中执行此操作?
例如,我需要转换此字符串:
"hello world!"
对此:
"hello_x0020_world_x0021_"
要使 XML 节点名称的字符串安全,您应该使用 XmlConver.EncodeName。
请注意,如果您需要对所有非字母数字字符进行编码,则需要自己编写,因为该方法未对"_"进行编码。
您可以使用LINQSelect
扩展方法从以下代码开始:
string str = "hello world!";
string a = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
a += a.ToLower();
char[] alphabet = a.ToCharArray();
str = string.Join("",
str.Select(ch => alphabet.Contains(ch) ?
ch.ToString() : String.Format("_x{0:x4}_", ch)).ToArray()
);
现在显然它有一些问题:
- 它在字符列表中进行线性搜索
- 错过数字...
- 如果我们添加数字需要确定第一个字符是否可以成为数字(假设是)
- 代码创建大量立即丢弃的字符串(每个字符一个)
- 字母数字仅限于 ASCII(假设正常,如果不是
Char.IsLetterOrDigit
提供帮助) - 对纯字母数字字符串做了很多工作
前两个很简单 - 我们可以使用由完整字符列表初始化的HashSet
(O(1)Contains
)(如果任何 alpah 数字字符可以使用现有方法 -Char.IsLetterOrDigit
更具可读性):
public static HashSet<char> asciiAlphaNum = new HashSet<char>
("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789");
为了避免真正毫无意义地为即时 GC 生成字符串ch.ToString()
,我们需要弄清楚如何从char
和string
的混合中构造字符串。String.Join
不起作用,因为它希望字符串开始,常规new string(...)
没有混合char
和string
的选项。所以我们只剩下StringBuilder
愉快地将两者带到Append
.如果大多数字符串没有其他字符,请考虑从初始大小开始str.Length
。
因此,对于每个角色,我们只需要builder.Append(ch)
或builder.AppendFormat(("_x{0:x4}_", (int)ch)
.要执行迭代,只使用常规foreach
更容易,但如果真的想要 LINQ -Enumerable.Aggregate
是要走的路。
string ReplaceNonAlphaNum(string str)
{
var builder = new StringBuilder();
foreach (var ch in str)
{
if (asciiAlphaNum.Contains(ch))
builder.Append(ch);
else
builder.AppendFormat("_x{0:x4}_", (int)ch);
}
return builder.ToString();
}
string ReplaceNonAlphaNumLinq(string str)
{
return str.Aggregate(new StringBuilder(), (builder, ch) =>
asciiAlphaNum.Contains(ch) ?
builder.Append(ch) : builder.AppendFormat("_x{0:x4}_", (int)ch)
).ToString();
}
最后一点 - 如果没有什么要转换的,我们真的不需要做任何事情 - 所以一些检查,如检查 c# 中字符串中的字母数字字符将有助于避免额外的字符串。
因此最终版本(LINQ,因为它更短,更花哨):
private static asciiAlphaNumRx = new Regex(@"^[a-zA-Z0-9]*$");
public static HashSet<char> asciiAlphaNum = new HashSet<char>
("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789");
string ReplaceNonAlphaNumLinq(string str)
{
return asciiAlphaNumRx.IsMatch(str) ? str :
str.Aggregate(new StringBuilder(), (builder, ch) =>
asciiAlphaNum.Contains(ch) ?
builder.Append(ch) : builder.AppendFormat("_x{0:x4}_", (int)ch)
).ToString();
}
或者,整个事情可以用正则表达式完成 - 请参阅正则表达式替换:使用自定义函数转换模式作为起点。