使用string.EndsWith方法是别忘了用StringComparison.Ordinal!

本文探讨了在C#项目中使用StringBuilder和String.EndsWith方法时的性能问题,通过添加StringComparison.Ordinal参数显著提升执行效率,并解释了不同比较方式之间的差异。

今天项目(C#项目)中遇到了一个性能问题,在对一个10万字左右文本解析时速度特别慢,用Profiler调查了一下原来大多时间都用在了String.EndsWith这个方法上。 大家现在看下面一段程序。

 

 

            var sb = new StringBuilder(100000);
            for (var i = 0; i < 10000; i++)
            {
                sb.Append("ABCDEFGHIJ");
            }
            var text = sb.ToString();
            var sw = new Stopwatch();
            sw.Start();
            for (var i = 0; i < 10000; i++)
            {
                var b = text.EndsWith("AAAA");
            }
            sw.Stop();
            Console.WriteLine(sw.ElapsedMilliseconds);

 

 

大家觉得这段程序大概要执行多长时间?

在我的机器上:36782 毫秒

 

大家再看这段代码

 

 

            var sb = new StringBuilder(100000);
            for (var i = 0; i < 10000; i++)
            {
                sb.Append("ABCDEFGHIJ");
            }
            var text = sb.ToString();
            var sw = new Stopwatch();
            sw.Start();
            for (var i = 0; i < 10000; i++)
            {
                var b = text.EndsWith("AAAA", StringComparison.Ordinal);
            }
            sw.Stop();
            Console.WriteLine(sw.ElapsedMilliseconds);

 

 

仅仅加了一个StringComparison.Ordinal, 执行时间是0,也就是不到1毫秒。

 

之前一直做java的项目,对于java里的String的endsWith出了比较字符串以外没有任何悬念。所以习惯性的在C#里也这样用。 但是在C#里,StringComparison.Ordinal才是常规用法。因为:

1. 用了它EndsWith会非常快

2. 一般情况下就是你想要的比较方式

 

使用StringComparison.Ordinal的比较方式就是我们常用的把字符当纯数字的比较方式。

 

如果不加这个参数, String的EndsWith的比较方式是culture-sensitive的,相当于EndsWith(str, StringComparison.CurrentCulture)。 这个是什么概念呢。 简单来讲就是某些语言上,虽然字符不同但是在语言上是相同的。

例如在德语环境下:

static void Main(string[] args)
{
  Console.WriteLine("Grüße".EndsWith("sse", StringComparison.Ordinal));
  Console.WriteLine("Grüße".EndsWith("sse", StringComparison.CurrentCulture));
  Console.WriteLine("Grüße".EndsWith("sse", StringComparison.InvariantCulture));
  Console.WriteLine("Grüße".EndsWith("sse"));
}

 

得到的结果是:

False
True
True
True

 

 

using Autodesk.AutoCAD.Runtime; using System; using System.Collections.Generic; using System.IO; using System.Linq; using System.Reflection; using SystemException = System.Exception; [assembly: ExtensionApplication(typeof(MainPluginHost.PluginLoader))] namespace MainPluginHost { public class PluginLoader : IExtensionApplication { // 包含所有需要嵌入的资源 private readonly string[] _embeddedResources = { // DLL插件 "BW.dll", "bzxl.dll", "fgbz.dll", "gjl.dll", "gtbbz.dll", "ktj.dll", "MyCadPlugin.dll", "pkbz.dll", "pmhz.dll", "pmxlg.dll", "th.dll", "tk.dll", "tyck.dll", "tytk.dll", "tzcl.dll", "wjpm.dll", "wjtj.dll", "wjxgbz.dll", "xgbz.dll", "zs.dll", "zw.dll", // 新增资源文件 "System thesaurus.txt", "qrcode.png" }; // 存储提取的资源路径 public static Dictionary<string, string> ExtractedResources { get; } = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase); public void Initialize() { LoadEmbeddedResources(); } public void Terminate() { // 清理临时文件 foreach (var path in ExtractedResources.Values) { try { File.Delete(path); } catch { /* 忽略清理错误 */ } } ExtractedResources.Clear(); } private void LoadEmbeddedResources() { var availableResources = Assembly.GetExecutingAssembly().GetManifestResourceNames(); int successCount = 0; var failedList = new System.Text.StringBuilder(); foreach (string resourceName in _embeddedResources) { try { string fullResourcePath = FindMatchingResource(availableResources, resourceName); if (fullResourcePath == null) { } // 根据文件类型处理 string extension = Path.GetExtension(resourceName).ToLower(); switch (extension) { case ".dll": // 加载程序集 Assembly.Load(ExtractResource(fullResourcePath)); break; case ".txt": case ".png": default: // 提取到文件系统 string tempPath = ExtractToTempFile(fullResourcePath, resourceName); ExtractedResources[resourceName] = tempPath; break; } // 输出加载信息 var doc = Autodesk.AutoCAD.ApplicationServices.Application.DocumentManager.MdiActiveDocument; if (doc != null) { } successCount++; } catch (SystemException ex) { failedList.AppendLine($"× {resourceName}: {ex.Message}"); } } } // 提取资源到临时文件 private string ExtractToTempFile(string resourceName, string originalName) { // 创建临时目录 string tempDir = Path.Combine(Path.GetTempPath(), "MyCadPlugin"); Directory.CreateDirectory(tempDir); // 保持原始文件名 string fileName = Path.GetFileName(originalName); string tempPath = Path.Combine(tempDir, fileName); // 避免文件锁定冲突 if (File.Exists(tempPath)) { File.Delete(tempPath); } // 写入文件 File.WriteAllBytes(tempPath, ExtractResource(resourceName)); return tempPath; } private string FindMatchingResource(string[] allResources, string resourceName) { // 1. 精确匹配(区分大小写) string exactMatch = allResources.FirstOrDefault(r => r.EndsWith(resourceName, StringComparison.Ordinal)); if (exactMatch != null) return exactMatch; // 2. 忽略大小写匹配 string ignoreCaseMatch = allResources.FirstOrDefault(r => r.EndsWith(resourceName, StringComparison.OrdinalIgnoreCase)); if (ignoreCaseMatch != null) return ignoreCaseMatch; // 3. 使用文件名匹配(忽略路径差异) string fileNameOnly = Path.GetFileName(resourceName); string fileNameMatch = allResources.FirstOrDefault(r => r.EndsWith(fileNameOnly, StringComparison.OrdinalIgnoreCase)); return fileNameMatch; } private byte[] ExtractResource(string resourceName) { using (Stream stream = Assembly.GetExecutingAssembly().GetManifestResourceStream(resourceName)) { if (stream == null) { } byte[] buffer = new byte[stream.Length]; stream.Read(buffer, 0, buffer.Length); return buffer; } } } // 资源访问器示例 public static class ResourceAccessor { // 获取文本资源内容 public static string GetThesaurusText() { if (PluginLoader.ExtractedResources.TryGetValue("System thesaurus.txt", out string path)) { return File.ReadAllText(path); } throw new FileNotFoundException("词典文件未加载"); } // 获取图像资源路径(可直接用于AutoCAD界面) public static string GetQrCodeImagePath() { if (PluginLoader.ExtractedResources.TryGetValue("qrcode.png", out string path)) { return path; } throw new FileNotFoundException("二维码图像未加载"); } } } 删除所有日志信息
10-12
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值