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         }

 

posted @ 2020-08-30 18:39  nutix  阅读(805)  评论(0编辑  收藏  举报