C# String 扩展方法【原创】
StringEx
1 using System; 2 using System.Collections.Generic; 3 using System.Linq; 4 using System.Reflection; 5 using System.Text; 6 using System.Text.RegularExpressions; 7 using NPOI.OpenXmlFormats.Spreadsheet; 8 9 namespace Nutix.Extensions 10 { 11 public static class StringEx 12 { 13 static StringEx() 14 { 15 StringEx.ResetParsers(false); 16 } 17 18 #region 1.Intercept :截取字符串 19 20 /// <summary> 21 /// 获取字符串中指定字符串之后的字符串 22 /// </summary> 23 /// <param name="str">要截取的原字符串</param> 24 /// <param name="afterWhat">截取的依据</param> 25 /// <returns> 26 /// 返回截取到的字符串。 27 /// 如果无任何匹配,则返回 null; 28 /// </returns> 29 public static string GetAfter(this string str, string afterWhat) 30 { 31 int index = str.IndexOf(afterWhat); 32 if (index == -1) return null; 33 34 index += str.Length; 35 return str.Substring(index); 36 } 37 38 /// <summary> 39 /// 获取字符串中指定字符串的最后一个匹配之后的字符串 40 /// </summary> 41 /// <param name="str">要截取的原字符串</param> 42 /// <param name="afterWhat">截取的依据</param> 43 /// <returns> 44 /// 返回截取到的字符串。 45 /// 如果无任何匹配,则返回 null; 46 /// </returns> 47 public static string GetLastAfter(this string str, string afterWhat) 48 { 49 int index = str.LastIndexOf(afterWhat); 50 if (index == -1) return null; 51 52 index += str.Length; 53 return str.Substring(index); 54 } 55 56 /// <summary> 57 /// 获取字符串中指定字符串之前的字符串 58 /// </summary> 59 /// <param name="str">要截取的原字符串</param> 60 /// <param name="beforeWhat">截取的依据</param> 61 /// <returns> 62 /// 返回截取到的字符串。 63 /// 如果无任何匹配,则返回 null; 64 /// </returns> 65 public static string GetBefore(this string str, string beforeWhat) 66 { 67 int index = str.IndexOf(beforeWhat); 68 return str.Substring(0, index); 69 } 70 71 /// <summary> 72 /// 获取字符串中指定字符串最后一个匹配之前的字符串 73 /// </summary> 74 /// <param name="str">要截取的原字符串</param> 75 /// <param name="beforeWhat">截取的依据</param> 76 /// <returns> 77 /// 返回截取到的字符串。 78 /// 如果无任何匹配,则返回 null; 79 /// </returns> 80 public static string GetLastBefore(this string str, string beforeWhat) 81 { 82 int index = str.LastIndexOf(beforeWhat); 83 return str.Substring(0, index); 84 } 85 86 /// <summary> 87 /// 获取字符串中指定的两个字符串之间的字符串内容 88 /// </summary> 89 /// <param name="str">要截取的原字符串</param> 90 /// <param name="from"> 91 /// 截取时作为依据的起始字符串 92 /// 如果 from == "",从零位置开始截取 93 /// </param> 94 /// <param name="to"> 95 /// 截取时作为依据的终止字符串 96 /// 如果 to == "", 一直截取到最后一个字符 97 /// </param> 98 /// <returns> 99 /// 返回截取到的字符串 100 /// </returns> 101 public static string GetBetween(this string str, string from, string to) 102 { 103 if (from == null || to == null) 104 { 105 throw new ArgumentException("参数 from 与 to,都不能为 null"); 106 } 107 int iStart, iEnd; 108 if (from == string.Empty) 109 iStart = 0; 110 else 111 iStart = str.IndexOf(from) + from.Length; 112 if (to == string.Empty) 113 iEnd = str.Length; 114 else 115 iEnd = str.IndexOf(to); 116 return str.Substring(iStart, iEnd - iStart); 117 } 118 119 #endregion 120 121 #region 2.Regex :正则操作 122 123 /// <summary> 124 /// 判断字符串是否与给定模式相匹配 125 /// </summary> 126 /// <param name="str">原字符串</param> 127 /// <param name="pattern">要匹配的模式</param> 128 /// <returns> 129 /// 返回是否匹配 130 /// </returns> 131 public static bool IsMatch(this string str, string pattern) 132 { 133 if (str == null) return false; 134 135 return System.Text.RegularExpressions.Regex.IsMatch(str, pattern); 136 } 137 138 /// <summary> 139 /// 查找字符串中与指定模式的所有匹配 140 /// </summary> 141 /// <param name="str">要匹配的字符串</param> 142 /// <param name="pattern">进行匹配的正则表达式</param> 143 /// <returns> 144 /// 返回所有匹配,包括全局匹配和子匹配,匹配到的文本 145 /// </returns> 146 public static string[] FindAll(this string str, string pattern) 147 { 148 if (str == null) return null; 149 150 Match m = System.Text.RegularExpressions.Regex.Match(str, pattern); 151 return m.Groups.OfType<Group>().Select(g => g.Value).ToArray(); 152 } 153 154 #endregion 155 156 #region 3.Fill :填充 157 158 #region 3.1.Center :居中填充 159 160 /// <summary> 161 /// 使用空格对文本进行居中填充 162 /// </summary> 163 /// <param name="str">被居中填充的文本</param> 164 /// <param name="totalWidth">填充后的总字符数</param> 165 /// <returns> 166 /// 返回填充后的文本 167 /// </returns> 168 public static string Center(this string str, int totalWidth) 169 { 170 return Center(str, totalWidth, ' '); 171 } 172 173 /// <summary> 174 /// 使用指定字符对文本进行居中填充 175 /// </summary> 176 /// <param name="str">被居中填充的文本</param> 177 /// <param name="totalWidth">填充后的总字符数</param> 178 /// <param name="fillWith">填充时使用的字符</param> 179 /// <returns> 180 /// 返回填充后的文本 181 /// </returns> 182 public static string Center(this string str, int totalWidth, char fillWith) 183 { 184 int strlen = str.Length; 185 if (strlen >= totalWidth) 186 { 187 return str; 188 } 189 else 190 { 191 int rightLen = (totalWidth - strlen) / 2; 192 int leftLen = totalWidth - strlen - rightLen; 193 return fillWith.ToString().Repeat(leftLen) + 194 str + fillWith.ToString().Repeat(rightLen); 195 } 196 } 197 198 #endregion 199 200 #region 3.2.PadLeftEx :定宽左填充 201 202 /// <summary> 203 /// 按系统默认字符编码对文本进行定宽左填充(以半角字符宽度为1单位宽度) 204 /// </summary> 205 /// <param name="str">要填充的文本</param> 206 /// <param name="totalByteCount">要填充到的字节长度</param> 207 /// <returns> 208 /// 返回填充后的文本 209 /// </returns> 210 public static string PadLeftEx(this string str, int totalByteCount) 211 { 212 return PadLeftEx(str, totalByteCount, Encoding.Default.BodyName); 213 } 214 215 /// <summary> 216 /// 按指定字符编码对文本进行定宽左填充(以半角字符宽度为1单位宽度) 217 /// </summary> 218 /// <param name="str">要填充的文本</param> 219 /// <param name="totalByteCount">要填充到的字节长度</param> 220 /// <param name="encodingName">用于在填充过程中进行文本解析的字符编码</param> 221 /// <returns> 222 /// 返回填充后的文本 223 /// </returns> 224 public static string PadLeftEx(this string str, int totalByteCount, string encodingName) 225 { 226 Encoding coding = Encoding.GetEncoding(encodingName); 227 int width = coding.GetByteCount(str); 228 //总字节数减去原字符串多占的字节数,就是应该添加的空格数 229 int padLen = totalByteCount - width; 230 if (padLen <= 0) 231 return str; 232 else 233 return str.PadLeft(padLen); 234 } 235 236 /// <summary> 237 /// 按系统默认字符编码对文本使用指定的填充符进行定宽左填充(以半角字符宽度为1单位宽度) 238 /// </summary> 239 /// <param name="str">要填充的文本</param> 240 /// <param name="totalByteCount">要填充到的字节长度</param> 241 /// <param name="fillWith">填充符</param> 242 /// <returns> 243 /// 返回填充后的文本 244 /// </returns> 245 public static string PadLeftEx(this string str, int totalByteCount, char fillWith) 246 { 247 return PadLeftEx(str, totalByteCount, fillWith, Encoding.Default.BodyName); 248 } 249 250 /// <summary> 251 /// 按指定字符编码对文本使用指定的填充符进行定宽左填充(以半角字符宽度为1单位宽度) 252 /// </summary> 253 /// <param name="str">要填充的文本</param> 254 /// <param name="totalByteCount">要填充到的字节长度</param> 255 /// <param name="fillWith">填充符</param> 256 /// <param name="encodingName">用于在填充过程中进行文本解析的字符编码</param> 257 /// <returns> 258 /// 返回填充后的文本 259 /// </returns> 260 public static string PadLeftEx(this string str, int totalByteCount, 261 char fillWith, string encodingName) 262 { 263 Encoding coding = Encoding.GetEncoding(encodingName); 264 int fillWithWidth = coding.GetByteCount(new char[] { fillWith }); 265 int width = coding.GetByteCount(str); 266 //总字节数减去原字符串多占的字节数,再除以填充字符的占的字节数, 267 //就是应该添加的空格数【因为有时候是双字节的填充符,比如中文】 268 int padLen = (totalByteCount - width) / fillWithWidth; 269 if (padLen <= 0) 270 return str; 271 else 272 return str.PadLeft(padLen, fillWith); 273 } 274 275 #endregion 276 277 #region 3.3.CenterEx :定宽居中填充 278 279 /// <summary> 280 /// 按系统默认字符编码对文本进行定宽居中填充(以半角字符宽度为1单位宽度) 281 /// </summary> 282 /// <param name="str"></param> 283 /// <param name="totalByteCount"></param> 284 /// <returns> 285 /// 返回填充后的文本 286 /// </returns> 287 public static string CenterEx(this string str, int totalByteCount) 288 { 289 return CenterEx(str, totalByteCount, Encoding.Default.BodyName); 290 } 291 292 /// <summary> 293 /// 按指定的字符编码对文本进行定宽居中填充(以半角字符宽度为1单位宽度) 294 /// </summary> 295 /// <param name="str">要居中填充的字符串</param> 296 /// <param name="totalByteCount">填充后的总字节数</param> 297 /// <param name="encodingName">用于在填充过程中进行文本解析的字符编码</param> 298 /// <returns> 299 /// 返回填充后的文本 300 /// </returns> 301 public static string CenterEx(this string str, int totalByteCount, string encodingName) 302 { 303 Encoding coding = Encoding.GetEncoding(encodingName); 304 int width = coding.GetByteCount(str); 305 //总字节数减去原字符串多占的字节数,就是应该添加的空格数 306 int padLen = totalByteCount - width; 307 if (padLen < 0) return str; 308 int padRight = padLen / 2; 309 int padLeft = padLen - padRight; 310 return " ".Repeat(padLeft) + str + " ".Repeat(padRight); 311 } 312 313 /// <summary> 314 /// 按系统默认字符编码对文本使用指定的填充符进行定宽居中填充(以半角字符宽度为1单位宽度) 315 /// </summary> 316 /// <param name="str">要填充的文本</param> 317 /// <param name="totalByteCount">填充后得到的结果包含的总字节数</param> 318 /// <param name="fillWith">填充符</param> 319 /// <returns> 320 /// 返回填充后的文本 321 /// </returns> 322 public static string CenterEx(this string str, int totalByteCount, char fillWith) 323 { 324 return CenterEx(str, totalByteCount, fillWith, Encoding.Default.BodyName); 325 } 326 327 /// <summary> 328 /// 按指定的字符编码对文本使用指定的填充符进行定宽居中填充(以半角字符宽度为1单位宽度) 329 /// </summary> 330 /// <param name="str">要填充的文本</param> 331 /// <param name="totalByteCount">填充后得到的文本需达到的总字节数</param> 332 /// <param name="fillWith">填充符</param> 333 /// <param name="encodingName">用于在填充过程中进行文本解析的字符编码</param> 334 /// <returns> 335 /// 返回填充后的文本 336 /// </returns> 337 public static string CenterEx(this string str, int totalByteCount, 338 char fillWith, string encodingName) 339 { 340 Encoding coding = Encoding.GetEncoding(encodingName); 341 int fillWithWidth = coding.GetByteCount(new char[] { fillWith }); 342 string fillStr = fillWith.ToString(); 343 int width = coding.GetByteCount(str); 344 //总字节数减去原字符串多占的字节数,就是应该添加的空格数 345 int padLen = (totalByteCount - width) / fillWithWidth; 346 if (padLen < 0) return str; 347 int padRight = padLen / 2; 348 int padLeft = padLen - padRight; 349 return fillStr.Repeat(padLeft) + str + fillStr.Repeat(padRight); 350 } 351 352 #endregion 353 354 #region 3.4.PadRight : 定宽右填充 355 356 /// <summary> 357 /// 按系统默认字符编码对文本进行定宽右填充(以半角字符宽度为1单位宽度) 358 /// </summary> 359 /// <param name="str">要填充的文本</param> 360 /// <param name="totalByteCount">要填充到的字节长度</param> 361 /// <returns> 362 /// 返回填充后的文本 363 /// </returns> 364 public static string PadRightEx(this string str, int totalByteCount) 365 { 366 return PadRightEx(str, totalByteCount, Encoding.Default.BodyName); 367 } 368 369 /// <summary> 370 /// 按指定字符编码对文本进行定宽右填充(以半角字符宽度为1单位宽度) 371 /// </summary> 372 /// <param name="str">要填充的文本</param> 373 /// <param name="totalByteCount">要填充到的字节长度</param> 374 /// <param name="encodingName">用于在填充过程中进行文本解析的字符编码</param> 375 /// <returns> 376 /// 返回填充后的文本 377 /// </returns> 378 public static string PadRightEx(this string str, int totalByteCount, string encodingName) 379 { 380 Encoding coding = Encoding.GetEncoding(encodingName); 381 int width = coding.GetByteCount(str); 382 //总字节数减去原字符串多占的字节数,就是应该添加的空格数 383 int padLen = totalByteCount - width; 384 if (padLen <= 0) 385 return str; 386 else 387 return str.PadRight(padLen); 388 } 389 390 /// <summary> 391 /// 按系统默认字符编码对文本使用指定的填充符进行定宽右填充(以半角字符宽度为1单位宽度) 392 /// </summary> 393 /// <param name="str">要填充的文本</param> 394 /// <param name="totalByteCount">要填充到的字节长度</param> 395 /// <param name="fillWith">填充符</param> 396 /// <returns> 397 /// 返回填充后的文本 398 /// </returns> 399 public static string PadRightEx(this string str, int totalByteCount, char fillWith) 400 { 401 return PadRightEx(str, totalByteCount, fillWith, Encoding.Default.BodyName); 402 } 403 404 /// <summary> 405 /// 按指定字符编码对文本使用指定的填充符进行定宽右填充(以半角字符宽度为1单位宽度) 406 /// </summary> 407 /// <param name="str">要填充的文本</param> 408 /// <param name="totalByteCount">要填充到的字节长度</param> 409 /// <param name="fillWith">填充符</param> 410 /// <param name="encodingName">用于在填充过程中进行文本解析的字符编码</param> 411 /// <returns> 412 /// 返回填充后的文本 413 /// </returns> 414 public static string PadRightEx(this string str, int totalByteCount, 415 char fillWith, string encodingName) 416 { 417 Encoding coding = Encoding.GetEncoding(encodingName); 418 int fillWithWidth = coding.GetByteCount(new char[] { fillWith }); 419 int width = coding.GetByteCount(str); 420 //总字节数减去原字符串多占的字节数,再除以填充字符的占的字节数, 421 //就是应该添加的空格数【因为有时候是双字节的填充符,比如中文】 422 int padLen = (totalByteCount - width) / fillWithWidth; 423 if (padLen <= 0) 424 return str; 425 else 426 return str.PadRight(padLen, fillWith); 427 } 428 429 #endregion 430 431 #endregion 432 433 #region 4.Repeat :复制字符串 434 435 /// <summary> 436 /// 取得字符串的指定次重复后的字符串 437 /// </summary> 438 /// <param name="str">原字符串</param> 439 /// <param name="times">要重复的次数</param> 440 /// <returns> 441 /// 返回复制了指定次的字符串 442 /// </returns> 443 public static string Repeat(this string str, int times) 444 { 445 if (times < 0) 446 throw new ArgumentException("参数 times 不能小于0."); 447 448 if (str == null) 449 throw new ArgumentException("要复制的字符串不能为 null."); 450 451 if (str == string.Empty) return string.Empty; 452 453 StringBuilder sb = new StringBuilder(); 454 for (int i = 1; i <= times; i++) 455 { 456 sb.Append(str); 457 } 458 return sb.ToString(); 459 } 460 461 /// <summary> 462 /// 取得字符串的指定次重复后的字符串 463 /// </summary> 464 /// <param name="str">原字符串</param> 465 /// <param name="totalByteCount">要重复到的字符宽度</param> 466 /// <returns> 467 /// 返回复制了指定次的字符串 468 /// </returns> 469 public static string RepeatEx(this string str, int totalByteCount) 470 { 471 return StringEx.RepeatEx(str, totalByteCount, Encoding.Default.BodyName); 472 } 473 474 /// <summary> 475 /// 取得字符串的指定次重复后的字符串 476 /// </summary> 477 /// <param name="str">原字符串</param> 478 /// <param name="totalByteCount">要重复到的字符宽度</param> 479 /// <param name="encodingName">用于在复制过程中进行文本解析的字符编码</param> 480 /// <returns> 481 /// 返回复制了指定次的字符串 482 /// </returns> 483 public static string RepeatEx(this string str, int totalByteCount, string encodingName) 484 { 485 if (totalByteCount < 0) 486 throw new ArgumentException("参数 times 不能小于0."); 487 488 if (str == null) 489 throw new ArgumentException("要复制的字符串不能为 null."); 490 491 if (str == string.Empty) return string.Empty; 492 493 Encoding coding = Encoding.GetEncoding(encodingName); 494 int len = coding.GetByteCount(str); 495 int times = totalByteCount / len; 496 StringBuilder sb = new StringBuilder(); 497 for (int i = 1; i <= times; i++) 498 { 499 sb.Append(str); 500 } 501 return sb.ToString(); 502 } 503 504 #endregion 505 506 #region 5.Parser :从字符串或表式文本提取对象 507 508 #region Parsers : Common Parsers and Parsers Maker 509 510 public delegate bool TryParse<T>(string valueStr, out T value); 511 512 public static readonly Dictionary<Type, object> Parsers = 513 new Dictionary<Type, object>(); 514 515 /// <summary> 516 /// <see cref="char"/> 类型字符串解析器 517 /// </summary> 518 public static TryParse<char> CharParser = 519 (string valueStr, out char value) => Char.TryParse(valueStr, out value); 520 521 /// <summary> 522 /// <see cref="bool"/> 类型字符串解析器 523 /// </summary> 524 public static TryParse<bool> BooleanParser = 525 (string valueStr, out bool value) => Boolean.TryParse(valueStr, out value); 526 527 /// <summary> 528 /// <see cref="byte"/> 类型字符串解析器 529 /// </summary> 530 public static TryParse<byte> ByteParser = 531 (string valueStr, out byte value) => Byte.TryParse(valueStr, out value); 532 533 /// <summary> 534 /// <see cref="sbyte"/> 类型字符串解析器 535 /// </summary> 536 public static TryParse<sbyte> SByteParser = 537 (string valueStr, out sbyte value) => SByte.TryParse(valueStr, out value); 538 539 /// <summary> 540 /// <see cref="short"/> 类型字符串解析器 541 /// </summary> 542 public static TryParse<short> Int16Parser = 543 (string valueStr, out short value) => Int16.TryParse(valueStr, out value); 544 545 /// <summary> 546 /// <see cref="int"/> 类型字符串解析器 547 /// </summary> 548 public static TryParse<int> Int32Parser = 549 (string valueStr, out int value) => Int32.TryParse(valueStr, out value); 550 551 /// <summary> 552 /// <see cref="long"/> 类型字符串解析器 553 /// </summary> 554 public static TryParse<long> Int64Parser = 555 (string valueStr, out long value) => Int64.TryParse(valueStr, out value); 556 557 /// <summary> 558 /// <see cref="ushort"/> 类型字符串解析器 559 /// </summary> 560 public static TryParse<ushort> UInt16Parser = 561 (string valueStr, out ushort value) => UInt16.TryParse(valueStr, out value); 562 563 /// <summary> 564 /// <see cref="uint"/> 类型字符串解析器 565 /// </summary> 566 public static TryParse<uint> UInt32Parser = 567 (string valueStr, out uint value) => UInt32.TryParse(valueStr, out value); 568 569 /// <summary> 570 /// <see cref="ulong"/> 类型字符串解析器 571 /// </summary> 572 public static TryParse<ulong> UInt64Parser = 573 (string valueStr, out ulong value) => UInt64.TryParse(valueStr, out value); 574 575 /// <summary> 576 /// <see cref="float"/> 类型字符串解析器 577 /// </summary> 578 public static TryParse<float> SingleParser = 579 (string valueStr, out float value) => Single.TryParse(valueStr, out value); 580 581 /// <summary> 582 /// <see cref="double"/> 类型字符串解析器 583 /// </summary> 584 public static TryParse<double> DoubleParser = 585 (string valueStr, out double value) => Double.TryParse(valueStr, out value); 586 587 /// <summary> 588 /// <see cref="decimal"/> 类型字符串解析器 589 /// </summary> 590 public static TryParse<decimal> DecimalParser = 591 (string valueStr, out decimal value) => Decimal.TryParse(valueStr, out value); 592 593 /// <summary> 594 /// <see cref="DateTime"/> 类型字符串解析器 595 /// </summary> 596 public static TryParse<DateTime> DateTimeParser = 597 (string valueStr, out DateTime value) => DateTime.TryParse(valueStr, out value); 598 599 /// <summary> 600 /// <see cref="TimeSpan"/> 类型字符串解析器 601 /// </summary> 602 public static TryParse<TimeSpan> TimeSpanParser = 603 (string valueStr, out TimeSpan value) => TimeSpan.TryParse(valueStr, out value); 604 605 /// <summary> 606 /// 获取指定枚举类型的字符串解析器 607 /// </summary> 608 /// <typeparam name="TEnum">给定枚举类型</typeparam> 609 /// <exception cref="ArgumentException"> 610 /// 要求 <typeparamref name="TEnum"/> 是一个具体的枚举类型 611 /// </exception> 612 /// <remarks> 613 /// <para>这里之所以不用约束条件限制死,是为了内部方法如 </para> 614 /// <para><see cref="StringEx.VParse{TValue}(string, TryParse{TValue}, bool)"/> 和</para> 615 /// <see cref="StringEx.VParseTable{TValue}(string, bool, TryParse{TValue}, bool)"/> 616 /// 调用的方便 617 /// </remarks> 618 /// <returns> 619 /// 如果指定的类型参数确实是一个枚举类型,就返回它的字符串解析器;否则,报错 620 /// </returns> 621 public static TryParse<TEnum> GetEnumParser<TEnum>() 622 where TEnum : struct 623 { 624 if (typeof(TEnum).IsEnum) 625 return (string valueStr, out TEnum value) => 626 Enum.TryParse(valueStr, out value); 627 else 628 throw new ArgumentException("必须是枚举类型", nameof(TEnum)); 629 } 630 631 /// <summary> 632 /// 重置解析器容器 633 /// </summary> 634 /// <param name="onlyCommonParser">仅重置常用的类型解析器</param> 635 public static void ResetParsers(bool onlyCommonParser) 636 { 637 if (!onlyCommonParser) StringEx.Parsers.Clear(); 638 StringEx.Parsers[typeof(char)] = StringEx.CharParser; 639 StringEx.Parsers[typeof(bool)] = StringEx.BooleanParser; 640 StringEx.Parsers[typeof(byte)] = StringEx.ByteParser; 641 StringEx.Parsers[typeof(sbyte)] = StringEx.SByteParser; 642 StringEx.Parsers[typeof(short)] = StringEx.Int16Parser; 643 StringEx.Parsers[typeof(int)] = StringEx.Int32Parser; 644 StringEx.Parsers[typeof(long)] = StringEx.Int64Parser; 645 StringEx.Parsers[typeof(ushort)] = StringEx.UInt16Parser; 646 StringEx.Parsers[typeof(uint)] = StringEx.UInt32Parser; 647 StringEx.Parsers[typeof(ulong)] = StringEx.UInt64Parser; 648 StringEx.Parsers[typeof(float)] = StringEx.SingleParser; 649 StringEx.Parsers[typeof(double)] = StringEx.DoubleParser; 650 StringEx.Parsers[typeof(decimal)] = StringEx.DecimalParser; 651 StringEx.Parsers[typeof(DateTime)] = StringEx.DateTimeParser; 652 StringEx.Parsers[typeof(TimeSpan)] = StringEx.TimeSpanParser; 653 } 654 655 /// <summary> 656 /// 通过反射获取指定类型的 bool TryParse(string s, out T value) 静态方法 657 /// </summary> 658 /// <typeparam name="T">要获取方法的类型</typeparam> 659 /// <returns> 660 /// 如果找到这个方法,就返回它;找不到,就报错 661 /// </returns> 662 private static TryParse<T> GetParser<T>() 663 { 664 Type t = typeof(T); 665 666 MethodInfo miTryParse = t.GetMethod("TryParse", BindingFlags.Public | 667 BindingFlags.Static, null, CallingConventions.Any, 668 new Type[] { typeof(string), typeof(T).MakeByRefType() }, null); 669 if (miTryParse == null) 670 throw new Exception("类型[" + t.FullName + 671 "]没有[bool TryParse(string s, out " + t.Name + 672 " value)]静态方法,无法完成解析"); 673 674 return Delegate.CreateDelegate(typeof(TryParse<T>), miTryParse) 675 as TryParse<T>; 676 } 677 678 #endregion 679 680 /// <summary> 681 /// 将字符串,使用指定的解析器方法(如果有提供),解析成可空的目标类型对象 682 /// </summary> 683 /// <typeparam name="TValue">要解析成的目标类型的可空类型所包装的类型</typeparam> 684 /// <param name="valueStr">要解析的字符串</param> 685 /// <param name="parser"> 686 /// 字符串解析器方法 687 /// <para>当某类型的解析器被传入调用一次,该解析器会被缓存;</para> 688 /// <para>解析器是与解析的目标类型绑定的,下次再调用,不需要再传此参数;</para> 689 /// <para>如果再次传入,会视情况(是否相等)覆盖上次调用时所用的解析器</para> 690 /// <para>当您未指定解析器方法时,将尝试取得目标类型的固有解析器,如果没找到,将会报错</para> 691 /// <para>手传解析器,比目标类型原生解析器具有更高的优先级</para> 692 /// <para>本扩展方法类 StringEx,在加载之初,已经初始化了如下常用类型的字符串解析器:</para> 693 /// <para>char/bool/byte/sbyte/short/int/long/ushort/uint/ulong/float/double/decimal/DateTime/TimeSpan</para> 694 /// <para>另外对于枚举类型,也会自动生成 <typeparamref name="TValue"/> 类型的解析器 Enum.TryParse </para> 695 /// </param> 696 /// <param name="temp">解析器是否是临时解析器,如果是,则不会被保存</param> 697 /// <returns> 698 /// 解析成功,返回解析出来的值类型对象;失败,则返回 null 699 /// </returns> 700 /// <remarks> 701 /// <para>如果提供了解析器,本方法通过给定的解析器,解析给定的字符器到目标类型</para> 702 /// <para>否则,本方法尝试通过目标类型的 bool TryParse(string s, out TRefer value) 静态方法来实现功能</para> 703 /// <para>如果指定的目标类型,不包含此种签名的静态方法,则会报错</para> 704 /// </remarks> 705 public static TValue? VParse<TValue>(this string valueStr, 706 TryParse<TValue> parser = null, bool temp = false) 707 where TValue : struct 708 { 709 Type t = typeof(TValue); 710 711 object proc; 712 if (parser == null) 713 { 714 if (StringEx.Parsers.ContainsKey(t)) 715 proc = StringEx.Parsers[t]; 716 else 717 { 718 if (t.IsEnum) 719 proc = StringEx.GetEnumParser<TValue>(); 720 else 721 proc = StringEx.GetParser<TValue>(); 722 } 723 } 724 else 725 proc = parser; 726 727 if (!temp && 728 (!StringEx.Parsers.ContainsKey(t) || 729 StringEx.Parsers[t] != proc)) 730 StringEx.Parsers.Add(t, proc); 731 732 TryParse<TValue> tryParse = proc as TryParse<TValue>; 733 if (tryParse(valueStr, out TValue value)) 734 return value; 735 else 736 return null; 737 } 738 739 /// <summary> 740 /// 将字符串,使用指定的解析器方法(如果有提供),解析成目标类型对象 741 /// </summary> 742 /// <typeparam name="TRefer">要解析成的目标类型</typeparam> 743 /// <param name="valueStr">要解析的字符串</param> 744 /// <param name="parser"> 745 /// 字符串解析器方法 746 /// <para>当某类型的解析器被传入调用一次,该解析器会被缓存;</para> 747 /// <para>解析器是与解析的目标类型绑定的,下次再调用,不需要再传此参数;</para> 748 /// <para>如果再次传入,会视情况(是否相等)覆盖上次调用时所用的解析器</para> 749 /// <para>当您未指定解析器方法时,将尝试取得目标类型的固有解析器,如果没找到,将会报错</para> 750 /// <para>手传解析器,比目标类型原生解析器具有更高的优先级</para> 751 /// <para>本扩展方法类 StringEx,在加载之初,已经初始化了如下常用类型的字符串解析器:</para> 752 /// <para>char/bool/byte/sbyte/short/int/long/ushort/uint/ulong/float/double/decimal/DateTime/TimeSpan</para> 753 /// </param> 754 /// <param name="temp">解析器是否是临时解析器,如果是,则不会被保存</param> 755 /// <returns> 756 /// 解析成功,则返回解析到的对象;失败,则返回 null 757 /// </returns> 758 /// <remarks> 759 /// <para>如果提供了解析器,本方法通过给定的解析器,解析给定的字符器到目标类型</para> 760 /// <para>否则,本方法尝试通过目标类型的 bool TryParse(string s, out TRefer value) 静态方法来实现功能</para> 761 /// <para>如果指定的目标类型,不包含此种签名的静态方法,则会报错</para> 762 /// </remarks> 763 public static TRefer RParse<TRefer>(this string valueStr, 764 TryParse<TRefer> parser = null, bool temp = false) 765 where TRefer : class 766 { 767 Type t = typeof(TRefer); 768 769 object proc; 770 if (parser == null) 771 { 772 if (StringEx.Parsers.ContainsKey(t)) 773 proc = StringEx.Parsers[t]; 774 else 775 proc = StringEx.GetParser<TRefer>(); 776 } 777 else 778 { 779 proc = parser; 780 } 781 782 if (!temp && 783 (!StringEx.Parsers.ContainsKey(t) || 784 StringEx.Parsers[t] != proc)) 785 StringEx.Parsers.Add(t, proc); 786 787 TryParse<TRefer> tryParse = proc as TryParse<TRefer>; 788 tryParse(valueStr, out TRefer value); 789 return value; 790 } 791 792 /// <summary> 793 /// 将表式文本,使用指定的解析器方法(如果有提供),解析成可空的目标类型对象数组 794 /// </summary> 795 /// <typeparam name="TValue">要解析成的目标类型</typeparam> 796 /// <param name="tableText">要解析的表式文本</param> 797 /// <param name="noWhiteLines">是否抛弃空白行</param> 798 /// <param name="parser"> 799 /// 表式文本解析器方法 800 /// <para>当某类型的解析器被传入调用一次,该解析器会被缓存,除非指定它是临时的;</para> 801 /// <para>解析器是与解析的目标类型绑定的,下次再调用,不需要再传此参数;</para> 802 /// <para>如果再次传入,会视情况(是否相等)覆盖上次调用时所用的解析器</para> 803 /// <para>当您未指定解析器方法时,将尝试取得目标类型的固有解析器,如果没找到,将会报错</para> 804 /// <para>手传解析器,比目标类型原生解析器具有更高的优先级</para> 805 /// <para>本扩展方法类 StringEx,在加载之初,已经初始化了如下常用类型的字符串解析器:</para> 806 /// <para>char/bool/byte/sbyte/short/int/long/ushort/uint/ulong/float/double/decimal/DateTime/TimeSpan</para> 807 /// <para>另外对于枚举类型,也会自动生成 <typeparamref name="TValue"/> 类型的解析器 Enum.TryParse </para> 808 /// </param> 809 /// <param name="temp">解析器是否是临时解析器,如果是,则不会被保存</param> 810 /// <returns> 811 /// 解析成功,则返回解析到的对象;失败,则返回 null 812 /// </returns> 813 /// <remarks> 814 /// <para>如果提供了解析器,本方法通过给定的解析器,解析给定的表式文本到目标类型</para> 815 /// <para>否则,本方法尝试通过目标类型的 bool TryParse(string s, out TRefer value) 静态方法来实现功能</para> 816 /// <para>如果指定的目标类型,不包含此种签名的静态方法,则会报错</para> 817 /// </remarks> 818 public static TValue?[] VParseTable<TValue>( 819 this string tableText, bool noWhiteLines = true, 820 TryParse<TValue> parser = null, bool temp = false) 821 where TValue : struct 822 { 823 Type t = typeof(TValue); 824 825 object proc; 826 if (parser == null) 827 { 828 if (StringEx.Parsers.ContainsKey(t)) 829 proc = StringEx.Parsers[t]; 830 else 831 { 832 if (t.IsEnum) 833 proc = StringEx.GetEnumParser<TValue>(); 834 else 835 proc = StringEx.GetParser<TValue>(); 836 } 837 } 838 else 839 proc = parser; 840 841 if (!temp && 842 (!StringEx.Parsers.ContainsKey(t) || 843 StringEx.Parsers[t] != proc)) 844 StringEx.Parsers.Add(t, proc); 845 846 TryParse<TValue> tryParse = proc as TryParse<TValue>; 847 List<TValue?> values = new List<TValue?>(); 848 foreach(string line in tableText.GetLines(noWhiteLines)) 849 { 850 if (tryParse(line, out TValue value)) 851 values.Add(value); 852 else 853 values.Add(null); 854 } 855 856 return values.ToArray(); 857 } 858 859 /// <summary> 860 /// 将表式文本,使用指定的解析器方法(如果有提供),解析成目标类型对象数组 861 /// </summary> 862 /// <typeparam name="TRefer">要解析成的目标类型</typeparam> 863 /// <param name="tableText">要解析的表式文本</param> 864 /// <param name="noWhiteLines">是否抛弃空白行</param> 865 /// <param name="parser"> 866 /// 表式文本解析器方法 867 /// <para>当某类型的解析器被传入调用一次,该解析器会被缓存,除非指定它是临时的;</para> 868 /// <para>解析器是与解析的目标类型绑定的,下次再调用,不需要再传此参数;</para> 869 /// <para>如果再次传入,会视情况(是否相等)覆盖上次调用时所用的解析器</para> 870 /// <para>当您未指定解析器方法时,将尝试取得目标类型的固有解析器,如果没找到,将会报错</para> 871 /// <para>手传解析器,比目标类型原生解析器具有更高的优先级</para> 872 /// <para>本扩展方法类 <see cref="StringEx"/>,在加载之初,已经初始化了如下常用类型的字符串解析器:</para> 873 /// <para>char/bool/byte/sbyte/short/int/long/ushort/uint/ulong/float/double/decimal/DateTime/TimeSpan</para> 874 /// </param> 875 /// <param name="temp">解析器是否是临时解析器,如果是,则不会被保存</param> 876 /// <returns> 877 /// 解析成功,则返回解析到的对象;失败,则返回 null 878 /// </returns> 879 /// <remarks> 880 /// <para>如果提供了解析器,本方法通过给定的解析器,解析给定的表式文本到目标类型</para> 881 /// <para>否则,本方法尝试通过目标类型的 bool TryParse(string s, out TRefer value) 静态方法来实现功能</para> 882 /// <para>如果指定的目标类型,不包含此种签名的静态方法,则会报错</para> 883 /// </remarks> 884 public static TRefer[] RParseTable<TRefer>( 885 this string tableText, bool noWhiteLines = true, 886 TryParse<TRefer> parser = null, bool temp = false) 887 where TRefer : class 888 { 889 Type t = typeof(TRefer); 890 891 object proc; 892 if (parser == null) 893 { 894 if (StringEx.Parsers.ContainsKey(t)) 895 proc = StringEx.Parsers[t]; 896 else 897 proc = StringEx.GetParser<TRefer>(); 898 } 899 else 900 { 901 proc = parser; 902 } 903 904 if (!temp && 905 (!StringEx.Parsers.ContainsKey(t) || 906 StringEx.Parsers[t] != proc)) 907 StringEx.Parsers.Add(t, proc); 908 909 TryParse<TRefer> tryParse = proc as TryParse<TRefer>; 910 911 return tableText.GetLines(noWhiteLines) 912 .Select(line => 913 { 914 tryParse(line, out TRefer value); 915 return value; 916 }).ToArray(); 917 } 918 919 #endregion 920 921 #region Z.Unclassified :未分类 922 923 /// <summary> 924 /// 获取指定字符器的字节宽度 925 /// </summary> 926 /// <param name="str"></param> 927 /// <returns></returns> 928 public static int GetWidth(this string str) 929 { 930 Encoding coding = Encoding.Default; 931 return coding.GetByteCount(str); 932 } 933 934 /// <summary> 935 /// 使用指定格式化器格式化值表 936 /// </summary> 937 /// <param name="format">格式化器</param> 938 /// <param name="values">值表</param> 939 /// <returns> 940 /// 返回格式化后的字符串 941 /// </returns> 942 public static string FormatWith(this string format, params object[] values) 943 { 944 return string.Format(format, values); 945 } 946 947 /// <summary> 948 /// 从指定字符器获取行 949 /// </summary> 950 /// <param name="text">指定的字符器</param> 951 /// <param name="noWhiteLines">是否抛弃空白行</param> 952 /// <returns></returns> 953 public static string[] GetLines(this string text, bool noWhiteLines = false) 954 { 955 if(noWhiteLines) 956 return text.Split(new string[] { Environment.NewLine }, StringSplitOptions.None) 957 .Where(line => !string.IsNullOrWhiteSpace(line)).ToArray(); 958 else 959 return text.Split(new string[] { Environment.NewLine }, StringSplitOptions.None); 960 } 961 962 #endregion 963 964 } 965 }
以下是试验代码:
1 internal class WeekDay 2 { 3 public int Index { get; set; } 4 5 public string Name { get; set; } 6 7 public string ShortName { get; set; } 8 9 public WeekDay(int index, string name, string shortName) 10 { 11 this.Index = index; 12 this.Name = name; 13 this.ShortName = shortName; 14 } 15 16 public override string ToString() 17 { 18 return $"[{ this.Index }, { this.ShortName }, { this.Name }]"; 19 } 20 } 21 22 private static void StringEx_Parse() 23 { 24 Console.WriteLine("值类型字符串解析器".CenterEx(80, '=')); 25 ///使用值类型字符串解析方法解析枚举字符串 26 Console.WriteLine("使用值类型字符串解析方法解析枚举字符串".CenterEx(80, '-')); 27 Console.WriteLine(DayOfWeek.Sunday.ToString().VParse<DayOfWeek>().Value); 28 Console.WriteLine("使用值类型字符串解析方法解析当前时间字符串".CenterEx(80, '-')); 29 Console.WriteLine(DateTime.Now.ToString().VParse<DateTime>().Value); 30 string doubleStr = "1.35923"; 31 ///给定临时解析器,调用值类型字符串解析方法解析双精度浮点型字符串 32 Console.WriteLine("带临时解析器,使用值类型字符串解析方法解析双精度浮点型字符串".CenterEx(80, '-')); 33 Console.WriteLine(doubleStr.VParse<double>( 34 (string s, out double d) => 35 { 36 bool okey = double.TryParse(s, out d); 37 d = (double)Math.Round(d, 2); 38 return okey; 39 }, true)); 40 ///不带解析器,调用值类型字符串解析方法解析双精度浮点型字符串 41 Console.WriteLine("不带解析器,调用值类型字符串解析方法解析双精度浮点型字符串".CenterEx(80, '-')); 42 Console.WriteLine("1.35923".VParse<double>()); 43 Console.WriteLine("1.32e-5".VParse<double>()); 44 ///调用引用类型字符串解析方法解析引用类型字符串 45 Console.WriteLine("调用引用类型字符串解析方法解析引用类型字符串".CenterEx(80, '=')); 46 Console.WriteLine("调用引用类型字符串解析方法解析 Version 类型字符串".CenterEx(80, '-')); 47 Console.WriteLine("1.2.3.4".RParse<Version>()); 48 Console.WriteLine("调用引用类型字符串解析方法解析 IPAddress 类型字符串".CenterEx(80, '=')); 49 Console.WriteLine("127.0.0.1".RParse<System.Net.IPAddress>().AddressFamily); 50 ///给定解析器,调用引用类型字符串解析方法解析引用类型字符串 51 Console.WriteLine("调用引用类型字符串解析方法解析 int[] 类型字符串".CenterEx(80, '=')); 52 var x = "1,2,3".RParse<int[]>((string s, out int[] value) => 53 { 54 string[] values = s.Split(','); 55 value = values.Select(v => int.Parse(v)).ToArray(); 56 return true; 57 }); 58 Console.WriteLine(x.JoinToText(", ", "[", "]")); 59 60 ///表式文本解析器 61 var days = Enum.GetValues(typeof(DayOfWeek)).OfType<DayOfWeek>() 62 .Select(d => ((int)d, d.ToString(), d.ToString().Substring(0, 3))) 63 .Cast<(int Index, string Name, string ShortName)>() 64 .ToArray(); 65 string tableText = days.JoinToText("\r\n"); 66 67 Console.WriteLine("表式文本解析".CenterEx(80, '=')); 68 Console.WriteLine(tableText); 69 ///值类型表式文本解析器 70 var tupleParser = new StringEx.TryParse<(int Number, string Name, string ShortName)>( 71 (string s, out (int Number, string Name, string ShortName) value) => 72 { 73 bool okey = s != null && s.Length > 2 && 74 s.StartsWith("(") && s.EndsWith(")"); 75 if (!okey) 76 { 77 value = default; 78 return false; 79 } 80 81 string[] strs = s.Substring(1, s.Length - 2).Split(','); 82 okey = okey && strs.Length == 3; 83 if (!okey) 84 { 85 value = default; 86 return false; 87 } 88 89 value = (int.Parse(strs[0].Trim()), strs[1].Trim(), strs[2].Trim()); 90 return okey; 91 }); 92 Console.WriteLine("表式文本向 tuple[] 解析".CenterEx(80, '=')); 93 var tuples = tableText.VParseTable(parser: tupleParser); 94 Console.WriteLine(tuples.JoinToText("\r\n")); 95 ///引用类型表式文本解析器 96 var weekDaysParser = new StringEx.TryParse<WeekDay>( 97 (string s, out WeekDay value) => 98 { 99 bool okey = s != null && s.Length > 2 && 100 s.StartsWith("(") && s.EndsWith(")"); 101 if (!okey) 102 { 103 value = default; 104 return false; 105 } 106 107 string[] strs = s.Substring(1, s.Length - 2).Split(','); 108 okey = okey && strs.Length == 3; 109 if (!okey) 110 { 111 value = default; 112 return false; 113 } 114 115 value = new WeekDay( 116 int.Parse(strs[0].Trim()), strs[1].Trim(), strs[2].Trim()); 117 return okey; 118 }); 119 Console.WriteLine("表式文本向 WeekDay[] 解析".CenterEx(80, '=')); 120 var weekDays = tableText.RParseTable(parser: weekDaysParser); 121 Console.WriteLine(weekDays.JoinToText("\r\n")); 122 ///含错误行的表式文本 123 Console.WriteLine("有错误的表式文本解析".CenterEx(80, '=')); 124 tableText = tableText.Replace("(2", "<78").Replace("5, ", "##"); 125 Console.WriteLine(tableText); 126 Console.WriteLine("有错误的表式文本向 tuple[] 解析".CenterEx(80, '-')); 127 tuples = tableText.VParseTable(parser: tupleParser); 128 Console.WriteLine(tuples.JoinToText("\r\n")); 129 Console.WriteLine("有错误的表式文本向 WeekDay[] 解析".CenterEx(80, '-')); 130 weekDays = tableText.RParseTable(parser: weekDaysParser); 131 Console.WriteLine(weekDays.JoinToText("\r\n")); 132 }