1 | #Region "Microsoft.VisualBasic::5a6d27717c0e477d1e1dbf7faba4b0b5, Microsoft.VisualBasic.Core\Extensions\StringHelpers\StringHelpers.vb" |
2 | |
3 | ' Author: |
4 | ' |
5 | ' asuka (amethyst.asuka@gcmodeller.org) |
6 | ' xie (genetics@smrucc.org) |
7 | ' xieguigang (xie.guigang@live.com) |
8 | ' |
9 | ' Copyright (c) 2018 GPL3 Licensed |
10 | ' |
11 | ' |
12 | ' GNU GENERAL PUBLIC LICENSE (GPL3) |
13 | ' |
14 | ' |
15 | ' This program is free software: you can redistribute it and/or modify |
16 | ' it under the terms of the GNU General Public License as published by |
17 | ' the Free Software Foundation, either version 3 of the License, or |
18 | ' (at your option) any later version. |
19 | ' |
20 | ' This program is distributed in the hope that it will be useful, |
21 | ' but WITHOUT ANY WARRANTY; without even the implied warranty of |
22 | ' MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
23 | ' GNU General Public License for more details. |
24 | ' |
25 | ' You should have received a copy of the GNU General Public License |
26 | ' along with this program. If not, see <http://www.gnu.org/licenses/>. |
27 | |
28 | |
29 | |
30 | ' /********************************************************************************/ |
31 | |
32 | ' Summaries: |
33 | |
34 | ' Module StringHelpers |
35 | ' |
36 | ' Properties: EmptyString, NonStrictCompares, StrictCompares |
37 | ' |
38 | ' Function: __json, AllEquals, CharAtOrDefault, CharString, (+3 Overloads) Count |
39 | ' CreateBuilder, DistinctIgnoreCase, EqualsAny, First, FormatString |
40 | ' FormatZero, GetBetween, GetEMails, GetStackValue, GetString |
41 | ' (+2 Overloads) GetTagValue, GetURLs, IgnoreCase, InStrAny, (+2 Overloads) Intersection |
42 | ' IsEmptyStringVector, JoinBy, LineTokens, Located, Lookup |
43 | ' (+2 Overloads) Match, Matches, MatchPattern, (+2 Overloads) MaxLengthString, NotEmpty |
44 | ' PadEnd, Parts, RepeatString, ReplaceChars, (+2 Overloads) Reverse |
45 | ' RNull, SaveTo, (+2 Overloads) Split, SplitBy, StringEmpty |
46 | ' StringHashCode, StringReplace, StringSplit, StripBlank, Strips |
47 | ' TextEquals, TextLast, TokenCount, TokenCountIgnoreCase, TrimNewLine |
48 | ' TrimNull, WildcardsLocated |
49 | ' |
50 | ' Sub: Parts, RemoveLast |
51 | ' |
52 | ' /********************************************************************************/ |
53 | |
54 | #End Region |
55 | |
56 | Imports System.Drawing |
57 | Imports System.Numerics |
58 | Imports System.Runtime.CompilerServices |
59 | Imports System.Text |
60 | Imports System.Text.RegularExpressions |
61 | Imports Microsoft.VisualBasic.CommandLine.Reflection |
62 | Imports Microsoft.VisualBasic.ComponentModel |
63 | Imports Microsoft.VisualBasic.ComponentModel.DataSourceModel |
64 | Imports Microsoft.VisualBasic.Language |
65 | Imports Microsoft.VisualBasic.Language.Default |
66 | Imports Microsoft.VisualBasic.Linq |
67 | Imports Microsoft.VisualBasic.Linq.Extensions |
68 | Imports Microsoft.VisualBasic.Math.Information |
69 | Imports Microsoft.VisualBasic.Scripting.MetaData |
70 | Imports Microsoft.VisualBasic.Serialization.JSON |
71 | Imports Microsoft.VisualBasic.Text |
72 | Imports Microsoft.VisualBasic.Text.Patterns |
73 | Imports r = System.Text.RegularExpressions.Regex |
74 | |
75 | ''' <summary> |
76 | ''' The extensions module for facilities the string operations. |
77 | ''' </summary> |
78 | <Package("StringHelpers", Publisher:="amethyst.asuka@gcmodeller.org", Url:="http://gcmodeller.org")> |
79 | Public Module StringHelpers |
80 | |
81 | <MethodImpl(MethodImplOptions.AggressiveInlining)> |
82 | <Extension> |
83 | Public Function PadEnd(str$, padLen%, Optional padString As Char = " "c) As String |
84 | Return str.PadRight(padLen, padString) |
85 | End Function |
86 | |
87 | <MethodImpl(MethodImplOptions.AggressiveInlining)> |
88 | <Extension> |
89 | Public Function TrimNull(str As String) As String |
90 | If str Is Nothing Then |
91 | Return Nothing |
92 | Else |
93 | Return str.Trim(ASCII.NUL) |
94 | End If |
95 | End Function |
96 | |
97 | <Extension> |
98 | Public Function CharAtOrDefault(s$, index%, Optional [default] As Char = ASCII.NUL) As Char |
99 | If s.Length <= index Then |
100 | Return [default] |
101 | Else |
102 | Return s(index) |
103 | End If |
104 | End Function |
105 | |
106 | ''' <summary> |
107 | ''' Get the first char of the target <see cref="StringBuilder"/> |
108 | ''' </summary> |
109 | ''' <param name="sb"></param> |
110 | ''' <returns></returns> |
111 | <MethodImpl(MethodImplOptions.AggressiveInlining)> |
112 | <Extension> |
113 | Public Function First(sb As StringBuilder) As Char |
114 | Return sb.Chars(Scan0) |
115 | End Function |
116 | |
117 | <MethodImpl(MethodImplOptions.AggressiveInlining)> |
118 | <Extension> |
119 | Public Function CreateBuilder(s As String) As StringBuilder |
120 | Return New StringBuilder(s) |
121 | End Function |
122 | |
123 | <MethodImpl(MethodImplOptions.AggressiveInlining)> |
124 | Public Function IgnoreCase(flag As Boolean) As CompareMethod |
125 | Return If(flag, CompareMethod.Text, CompareMethod.Binary) |
126 | End Function |
127 | |
128 | ''' <summary> |
129 | ''' Using <see cref="[String].Empty"/> as default value |
130 | ''' </summary> |
131 | Public ReadOnly Property EmptyString As DefaultValue(Of String) = String.Empty |
132 | |
133 | ''' <summary> |
134 | ''' Replace the <see cref="vbCrLf"/> with the specific string. |
135 | ''' </summary> |
136 | ''' <param name="src"></param> |
137 | ''' <param name="replacement"></param> |
138 | ''' <returns></returns> |
139 | <Extension> Public Function TrimNewLine(src$, <Parameter("vbCrLf.Replaced")> Optional replacement$ = " ") As String |
140 | If src Is Nothing Then |
141 | Return "" |
142 | End If |
143 | |
144 | src = src.Replace(vbCrLf, replacement) _ |
145 | .Replace(vbCr, replacement) _ |
146 | .Replace(vbLf, replacement) _ |
147 | .Replace(" ", " ") |
148 | |
149 | Return Strings.Trim(src) |
150 | End Function |
151 | |
152 | <Extension> |
153 | Public Function ReplaceChars(src$, chars As IEnumerable(Of Char), replaceAs As Char) As String |
154 | Dim s As New StringBuilder(src) |
155 | |
156 | For Each c As Char In chars |
157 | Call s.Replace(c, replaceAs) |
158 | Next |
159 | |
160 | Return s.ToString |
161 | End Function |
162 | |
163 | ''' <summary> |
164 | ''' 判断这个字符串数组之中的所有的元素都是空字符串? |
165 | ''' </summary> |
166 | ''' <param name="s$">字符串数组</param> |
167 | ''' <returns></returns> |
168 | <Extension> Public Function IsEmptyStringVector(s$(), Optional RNull As Boolean = False) As Boolean |
169 | If RNull Then |
170 | Return s _ |
171 | .Where(AddressOf StringHelpers.RNull) _ |
172 | .Count = s.Length |
173 | Else |
174 | Return s.Where(Function(c) Not c.StringEmpty).Count = 0 |
175 | End If |
176 | End Function |
177 | |
178 | ''' <summary> |
179 | ''' Is text equals to the R nothing? |
180 | ''' </summary> |
181 | ''' <param name="c$"></param> |
182 | ''' <returns></returns> |
183 | Private Function RNull(c$) As Boolean |
184 | Return c.StringEmpty OrElse |
185 | c.TextEquals("NULL") OrElse |
186 | c.TextEquals("NA") |
187 | End Function |
188 | |
189 | <Extension> |
190 | Public Function AllEquals(s As IEnumerable(Of String), str$) As Boolean |
191 | Return s.All(Function(x) x = str) |
192 | End Function |
193 | |
194 | ''' <summary> |
195 | ''' https://github.com/darkskyapp/string-hash |
196 | ''' </summary> |
197 | ''' <param name="s$"></param> |
198 | ''' <returns></returns> |
199 | <Extension> Public Function StringHashCode(s As String) As Long |
200 | Dim hash& = 5381 |
201 | Dim chars%() = s.Select(AddressOf Convert.ToInt32).ToArray |
202 | |
203 | For i As Integer = s.Length - 1 To 0 Step -1 |
204 | hash = (New BigInteger(hash) * 33 Xor chars(i)).ToTruncateInt64 |
205 | Next |
206 | |
207 | hash = hash >> 0 |
208 | |
209 | Return hash |
210 | End Function |
211 | |
212 | ''' <summary> |
213 | ''' 常用于gdi+绘图操作,和<see cref="Graphics.MeasureString"/>共同使用 |
214 | ''' </summary> |
215 | ''' <param name="source"></param> |
216 | ''' <returns></returns> |
217 | <Extension> |
218 | Public Function MaxLengthString(source As IEnumerable(Of String)) As String |
219 | Return source.OrderByDescending(Function(s) Len(s)).First |
220 | End Function |
221 | |
222 | ''' <summary> |
223 | ''' 获取最大长度的字符串 |
224 | ''' </summary> |
225 | ''' <typeparam name="T"></typeparam> |
226 | ''' <param name="source"></param> |
227 | ''' <param name="getString"></param> |
228 | ''' <returns></returns> |
229 | <Extension> |
230 | Public Function MaxLengthString(Of T)(source As IEnumerable(Of T), getString As Func(Of T, String)) As String |
231 | Return source.Select(getString).MaxLengthString |
232 | End Function |
233 | |
234 | ''' <summary> |
235 | ''' 将<paramref name="replaces"/>列表之中的字符串都替换为空字符串 |
236 | ''' </summary> |
237 | ''' <param name="s$"></param> |
238 | ''' <param name="replaces"></param> |
239 | ''' <returns></returns> |
240 | <Extension> |
241 | Public Function Strips(s$, replaces As IEnumerable(Of String)) As String |
242 | Dim sb As New StringBuilder(s) |
243 | |
244 | For Each r As String In replaces |
245 | Call sb.Replace(r, "") |
246 | Next |
247 | |
248 | Return sb.ToString |
249 | End Function |
250 | |
251 | ''' <summary> |
252 | ''' 将一个任意的目标字符集合转换为字符串对象 |
253 | ''' </summary> |
254 | ''' <param name="chs"></param> |
255 | ''' <returns></returns> |
256 | <Extension> |
257 | Public Function CharString(chs As IEnumerable(Of Char)) As String |
258 | Return New String(chs.ToArray) |
259 | End Function |
260 | |
261 | ' |
262 | ' Summary: |
263 | ' Replaces the format item in a specified string with the string representation |
264 | ' of a corresponding object in a specified array. |
265 | ' |
266 | ' Parameters: |
267 | ' format: |
268 | ' A composite format string. |
269 | ' |
270 | ' args: |
271 | ' An object array that contains zero or more objects to format. |
272 | ' |
273 | ' Returns: |
274 | ' A copy of format in which the format items have been replaced by the string representation |
275 | ' of the corresponding objects in args. |
276 | ' |
277 | ' Exceptions: |
278 | ' T:System.ArgumentNullException: |
279 | ' format or args is null. |
280 | ' |
281 | ' T:System.FormatException: |
282 | ' format is invalid.-or- The index of a format item is less than zero, or greater |
283 | ' than or equal to the length of the args array. |
284 | |
285 | ''' <summary> |
286 | ''' Replaces the format item in a specified string with the string representation |
287 | ''' of a corresponding object in a specified array. |
288 | ''' </summary> |
289 | ''' <param name="s">A composite format string.</param> |
290 | ''' <param name="args">An object array that contains zero or more objects to format.</param> |
291 | ''' <returns>A copy of format in which the format items have been replaced by the string representation |
292 | ''' of the corresponding objects in args.</returns> |
293 | <Extension> |
294 | Public Function FormatString(s$, ParamArray args As Object()) As String |
295 | Return String.Format(s, args) |
296 | End Function |
297 | |
298 | ''' <summary> |
299 | ''' this is to emulate what's evailable in PHP |
300 | ''' </summary> |
301 | <Extension> |
302 | Public Function RepeatString(text$, count%) As String |
303 | Dim sb = New StringBuilder(text.Length * count) |
304 | For i As Integer = 0 To count - 1 |
305 | Call sb.Append(text) |
306 | Next |
307 | Return sb.ToString() |
308 | End Function |
309 | |
310 | ''' <summary> |
311 | ''' Join and contact the text tokens with a specific <paramref name="delimiter"/> string. |
312 | ''' </summary> |
313 | ''' <typeparam name="T"></typeparam> |
314 | ''' <param name="data"></param> |
315 | ''' <param name="delimiter$"></param> |
316 | ''' <returns></returns> |
317 | ''' |
318 | <MethodImpl(MethodImplOptions.AggressiveInlining)> |
319 | <Extension> |
320 | Public Function JoinBy(Of T)(data As IEnumerable(Of T), delimiter$) As String |
321 | Return String.Join(delimiter, data.SafeQuery.Select(AddressOf Scripting.ToString).ToArray) |
322 | End Function |
323 | |
324 | ''' <summary> |
325 | ''' Text parser for the format: ``tagName{<paramref name="delimiter"/>}value`` |
326 | ''' </summary> |
327 | ''' <param name="s"></param> |
328 | ''' <param name="delimiter"></param> |
329 | ''' <param name="trim">Needs Removes all leading and trailing white-space characters from |
330 | ''' the current <see cref="System.String"/> object.</param> |
331 | ''' <returns></returns> |
332 | ''' |
333 | <MethodImpl(MethodImplOptions.AggressiveInlining)> |
334 | <Extension> |
335 | Public Function GetTagValue(s$, Optional delimiter$ = " ", Optional trim As Boolean = False, Optional failureNoName As Boolean = True) As NamedValue(Of String) |
336 | Return s.GetTagValue(delimiter, trim:=If(trim, " ", Nothing), failureNoName:=failureNoName) |
337 | End Function |
338 | |
339 | ''' <summary> |
340 | ''' Text parser for the format: ``tagName{<paramref name="delimiter"/>}value`` |
341 | ''' </summary> |
342 | ''' <param name="s$"></param> |
343 | ''' <param name="delimiter$"></param> |
344 | ''' <param name="trim$">Chars collection for <see cref="String.Trim"/> function</param> |
345 | ''' <param name="failureNoName"></param> |
346 | ''' <returns></returns> |
347 | <Extension> |
348 | Public Function GetTagValue(s$, delimiter$, trim$, Optional failureNoName As Boolean = True) As NamedValue(Of String) |
349 | If s.StringEmpty Then |
350 | Return Nothing |
351 | End If |
352 | |
353 | Dim p% = InStr(s, delimiter, CompareMethod.Text) |
354 | |
355 | If p = 0 Then |
356 | If failureNoName Then |
357 | Return New NamedValue(Of String)("", s, s) |
358 | Else |
359 | Return New NamedValue(Of String)(s, "", s) |
360 | End If |
361 | Else |
362 | Dim key$ = Mid(s, 1, p - 1) |
363 | Dim value$ = Mid(s, p + delimiter.Length) |
364 | |
365 | If Not trim.StringEmpty(whitespaceAsEmpty:=False) Then |
366 | With trim.ToArray |
367 | value = value.Trim(.ByRef) |
368 | key = key.Trim(.ByRef) |
369 | End With |
370 | End If |
371 | |
372 | Return New NamedValue(Of String)(key, value, s) |
373 | End If |
374 | End Function |
375 | |
376 | <Extension> |
377 | Public Function StripBlank(s$, Optional includeNewline As Boolean = True) As String |
378 | If includeNewline Then |
379 | Return s.Trim(" "c, ASCII.TAB, ASCII.LF, ASCII.CR) |
380 | Else |
381 | Return s.Trim(" "c, ASCII.TAB) |
382 | End If |
383 | End Function |
384 | |
385 | ''' <summary> |
386 | ''' Shortcuts for method <see cref="System.String.Equals"/>(s1, s2, <see cref="StringComparison.OrdinalIgnoreCase"/>) |
387 | ''' </summary> |
388 | ''' <param name="s1"></param> |
389 | ''' <param name="s2"></param> |
390 | ''' <returns></returns> |
391 | ''' |
392 | <MethodImpl(MethodImplOptions.AggressiveInlining)> |
393 | <Extension> |
394 | Public Function TextEquals(s1$, s2$) As Boolean |
395 | 'If {s1, s2}.All(Function(s) s Is Nothing) Then |
396 | ' Return True ' null = null ?? |
397 | 'End If |
398 | Return String.Equals(s1, s2, StringComparison.OrdinalIgnoreCase) |
399 | End Function |
400 | |
401 | ''' <summary> |
402 | ''' <see cref="RegexOptions.IgnoreCase"/> + <see cref="RegexOptions.Singleline"/> |
403 | ''' </summary> |
404 | Public Const RegexICSng As RegexOptions = RegexOptions.IgnoreCase Or RegexOptions.Singleline |
405 | |
406 | ''' <summary> |
407 | ''' <see cref="RegexOptions.IgnoreCase"/> + <see cref="RegexOptions.Multiline"/> |
408 | ''' </summary> |
409 | Public Const RegexICMul As RegexOptions = RegexOptions.IgnoreCase Or RegexOptions.Multiline |
410 | |
411 | ''' <summary> |
412 | ''' <paramref name="s"/> Is Nothing, <see cref="System.String.IsNullOrEmpty"/>, <see cref="System.String.IsNullOrWhiteSpace"/> |
413 | ''' </summary> |
414 | ''' <param name="s">The input test string</param> |
415 | ''' <returns></returns> |
416 | <Extension> Public Function StringEmpty(s$, Optional whitespaceAsEmpty As Boolean = True) As Boolean |
417 | If s Is Nothing OrElse String.IsNullOrEmpty(s) Then |
418 | Return True |
419 | Else |
420 | If String.IsNullOrWhiteSpace(s) Then |
421 | Return whitespaceAsEmpty |
422 | Else |
423 | Return False |
424 | End If |
425 | End If |
426 | End Function |
427 | |
428 | ''' <summary> |
429 | ''' Not <see cref="StringEmpty(String, Boolean)"/> |
430 | ''' </summary> |
431 | ''' <param name="s$"></param> |
432 | ''' <param name="whitespaceAsEmpty"></param> |
433 | ''' <returns></returns> |
434 | <MethodImpl(MethodImplOptions.AggressiveInlining)> |
435 | <Extension> |
436 | Public Function NotEmpty(s$, Optional whitespaceAsEmpty As Boolean = True) As Boolean |
437 | Return Not s.StringEmpty(whitespaceAsEmpty) |
438 | End Function |
439 | |
440 | ''' <summary> |
441 | ''' Call <see cref="StringBuilder.Remove"/>(<see cref="StringBuilder.Length"/> - 1, 1) for removes the last character in the string sequence. |
442 | ''' </summary> |
443 | ''' <param name="s"></param> |
444 | ''' |
445 | <MethodImpl(MethodImplOptions.AggressiveInlining)> |
446 | <Extension> Public Sub RemoveLast(s As StringBuilder) |
447 | Call s.Remove(s.Length - 1, 1) |
448 | End Sub |
449 | |
450 | ''' <summary> |
451 | ''' Returns a reversed version of String s. |
452 | ''' </summary> |
453 | ''' <param name="sb"></param> |
454 | ''' <returns></returns> |
455 | <Extension> Public Function Reverse(ByRef sb As StringBuilder) As StringBuilder |
456 | Dim s As String = New String(sb.ToString.Reverse.ToArray) |
457 | sb = New StringBuilder(s) |
458 | Return sb |
459 | End Function |
460 | |
461 | ''' <summary> |
462 | ''' Returns a reversed version of String s. |
463 | ''' </summary> |
464 | ''' <param name="s"></param> |
465 | ''' <returns></returns> |
466 | ''' |
467 | <MethodImpl(MethodImplOptions.AggressiveInlining)> |
468 | Public Function Reverse(s As String) As String |
469 | Return New String(s.Reverse.ToArray) |
470 | End Function |
471 | |
472 | Public ReadOnly Property StrictCompares As StringComparison = StringComparison.Ordinal |
473 | ''' <summary> |
474 | ''' String compares with ignored chars' case.(忽略大小写为非严格的比较) |
475 | ''' </summary> |
476 | ''' <returns></returns> |
477 | Public ReadOnly Property NonStrictCompares As StringComparison = StringComparison.OrdinalIgnoreCase |
478 | |
479 | ''' <summary> |
480 | ''' Split long text data into seperate lines by the specific <paramref name="len"/> value. |
481 | ''' </summary> |
482 | ''' <param name="s"></param> |
483 | ''' <param name="len"></param> |
484 | ''' <returns></returns> |
485 | ''' <remarks>Using for the Fasta sequence writer.</remarks> |
486 | <ExportAPI("s.Parts")> |
487 | <Extension> Public Function Parts(s$, len%) As String |
488 | Dim sbr As New StringBuilder |
489 | Call Parts(s, len, sbr) |
490 | Return sbr.ToString |
491 | End Function |
492 | |
493 | Public Sub Parts(s As String, len As String, ByRef sbr As StringBuilder) |
494 | Do While Not String.IsNullOrEmpty(s) |
495 | Call sbr.Append(Mid(s, 1, len)) |
496 | s = Mid(s, len + 1) |
497 | If String.IsNullOrEmpty(s) Then |
498 | Return |
499 | End If |
500 | Dim fs As Integer = InStr(s, " ") |
501 | |
502 | If fs = 0 Then |
503 | Call sbr.AppendLine(s) |
504 | Return |
505 | End If |
506 | |
507 | Dim Firts As String = Mid(s, 1, fs - 1) |
508 | s = Mid(s, fs + 1) |
509 | Call sbr.AppendLine(Firts) |
510 | Loop |
511 | End Sub |
512 | |
513 | ''' <summary> |
514 | ''' Regex expression for parsing E-Mail URL |
515 | ''' </summary> |
516 | Const REGEX_EMAIL As String = "[a-z0-9\._-]+@[a-z0-9\._-]+" |
517 | ''' <summary> |
518 | ''' Regex exprression for parsing the http/ftp URL |
519 | ''' </summary> |
520 | Const REGEX_URL As String = "(ftp|http(s)?)[:]//[a-z0-9\.-_]+\.[a-z]+/*[^""]*" |
521 | |
522 | <ExportAPI("Parsing.E-Mails")> |
523 | Public Function GetEMails(s As String) As String() |
524 | Dim values$() = Regex _ |
525 | .Matches(s, REGEX_EMAIL, RegexICSng) _ |
526 | .ToArray |
527 | Return values |
528 | End Function |
529 | |
530 | <ExportAPI("Parsing.URLs")> |
531 | Public Function GetURLs(s As String) As String() |
532 | Dim values$() = Regex _ |
533 | .Matches(s, REGEX_URL, RegexICSng) _ |
534 | .ToArray |
535 | Return values |
536 | End Function |
537 | |
538 | ''' <summary> |
539 | ''' Counts the specific char that appeared in the input string. |
540 | ''' (计数在字符串之中所出现的指定的字符的出现的次数) |
541 | ''' </summary> |
542 | ''' <param name="str"></param> |
543 | ''' <param name="ch"></param> |
544 | ''' <returns></returns> |
545 | ''' |
546 | <ExportAPI("Count", Info:="Counting the specific char in the input string value.")> |
547 | <Extension> Public Function Count(str$, ch As Char) As Integer |
548 | If String.IsNullOrEmpty(str) Then |
549 | Return 0 |
550 | Else |
551 | Return str.Count(Function(c) c = ch) |
552 | End If |
553 | End Function |
554 | |
555 | ''' <summary> |
556 | ''' 计算目标字符串在序列之中出现的次数 |
557 | ''' </summary> |
558 | ''' <param name="source"></param> |
559 | ''' <param name="target$"></param> |
560 | ''' <param name="method"></param> |
561 | ''' <returns></returns> |
562 | <MethodImpl(MethodImplOptions.AggressiveInlining)> |
563 | <Extension> |
564 | Public Function Count(source As IEnumerable(Of String), target$, Optional method As StringComparison = StringComparison.Ordinal) As Integer |
565 | Return source _ |
566 | .Where(Function(s) String.Equals(s, target, method)) _ |
567 | .Count |
568 | End Function |
569 | |
570 | ''' <summary> |
571 | ''' Count the phrase in <paramref name="text"/> |
572 | ''' </summary> |
573 | ''' <param name="text$"></param> |
574 | ''' <param name="phrase$"></param> |
575 | ''' <param name="method"></param> |
576 | ''' <returns></returns> |
577 | <Extension> |
578 | Public Function Count(text$, phrase$, Optional method As CompareMethod = CompareMethod.Binary) As Integer |
579 | Dim n% |
580 | Dim pos% = InStr(text, phrase, method) |
581 | |
582 | Do While pos > 0 |
583 | n += 1 |
584 | pos = InStr(pos + 1, text, phrase, method) |
585 | Loop |
586 | |
587 | Return n |
588 | End Function |
589 | |
590 | ''' <summary> |
591 | ''' 获取""或者其他字符所包围的字符串的值,请注意,假若只有一个<paramref name="wrapper"/>的话,字符串将不会进行任何处理 |
592 | ''' </summary> |
593 | ''' <param name="s"></param> |
594 | ''' <param name="wrapper"></param> |
595 | ''' <returns></returns> |
596 | ''' <remarks></remarks> |
597 | ''' |
598 | <ExportAPI("Wrapper.Trim")> |
599 | <Extension> Public Function GetString(s$, Optional wrapper As Char = ASCII.Quot) As String |
600 | If String.IsNullOrEmpty(s) OrElse Len(s) = 1 Then |
601 | Return s |
602 | End If |
603 | If s.First = wrapper AndAlso s.Last = wrapper Then |
604 | Return Mid(s, 2, Len(s) - 2) |
605 | Else |
606 | Return s |
607 | End If |
608 | End Function |
609 | |
610 | ''' <summary> |
611 | ''' Get sub string value from the region that between the <paramref name="left"/> and <paramref name="right"/>. |
612 | ''' (这个函数是直接分别查找左右两边的定位字符串来进行切割的) |
613 | ''' </summary> |
614 | ''' <param name="str$"></param> |
615 | ''' <param name="left$"></param> |
616 | ''' <param name="right$"></param> |
617 | ''' <returns></returns> |
618 | <ExportAPI("Get.Stackvalue")> |
619 | <Extension> |
620 | Public Function GetStackValue(str$, left$, right$) As String |
621 | If Len(str) < 2 Then |
622 | Return "" |
623 | End If |
624 | |
625 | Dim p As Integer = InStr(str, left) + left.Length |
626 | Dim q As Integer = InStrRev(str, right) |
627 | |
628 | If p = 0 Or q = 0 Then |
629 | Return str |
630 | ElseIf p >= q Then |
631 | Return "" |
632 | Else |
633 | str = Mid(str, p, q - p) |
634 | Return str |
635 | End If |
636 | End Function |
637 | |
638 | ''' <summary> |
639 | ''' 和<see cref="GetStackValue(String, String, String)"/>相似,这个函数也是查找起始和终止字符串之间的字符串, |
640 | ''' 但是这个函数是查找相邻的两个标记,而非像<see cref="GetStackValue(String, String, String)"/>函数一样 |
641 | ''' 直接查找字符串的两端的定位结果 |
642 | ''' </summary> |
643 | ''' <param name="str$"></param> |
644 | ''' <param name="strStart$"></param> |
645 | ''' <param name="strEnd$"></param> |
646 | ''' <returns></returns> |
647 | ''' |
648 | <Extension> |
649 | Public Function GetBetween(str$, strStart$, strEnd$) As String |
650 | Dim start%, end% |
651 | |
652 | If str.StringEmpty Then |
653 | Return Nothing |
654 | End If |
655 | |
656 | If str.Contains(strStart) AndAlso str.Contains(strEnd) Then |
657 | start = str.IndexOf(strStart, 0) + strStart.Length |
658 | [end] = str.IndexOf(strEnd, start) |
659 | |
660 | Return str.Substring(start, [end] - start) |
661 | Else |
662 | Return Nothing |
663 | End If |
664 | End Function |
665 | |
666 | ''' <summary> |
667 | ''' 在字符串前面填充指定长度的00序列,假若输入的字符串长度大于fill的长度,则不再进行填充 |
668 | ''' </summary> |
669 | ''' <typeparam name="T">限定类型为字符串或者数值基础类型</typeparam> |
670 | ''' <param name="n"></param> |
671 | ''' <param name="fill"></param> |
672 | ''' <returns></returns> |
673 | <ExportAPI("FormatZero")> |
674 | <Extension> Public Function FormatZero(Of T As {IComparable(Of T)})(n As T, Optional fill$ = "00") As String |
675 | Dim s As String = n.ToString |
676 | Dim d As Integer = Len(fill) - Len(s) |
677 | |
678 | If d < 0 Then |
679 | Return s |
680 | Else |
681 | Return Mid(fill, 1, d) & s |
682 | End If |
683 | End Function |
684 | |
685 | ''' <summary> |
686 | ''' 求交集 |
687 | ''' </summary> |
688 | ''' <param name="source"></param> |
689 | ''' <returns></returns> |
690 | ''' <remarks></remarks> |
691 | ''' |
692 | <ExportAPI("Intersection")> |
693 | <Extension> Public Function Intersection(source As IEnumerable(Of IEnumerable(Of String))) As String() |
694 | Dim union As New List(Of String) |
695 | |
696 | source = (From line As IEnumerable(Of String) |
697 | In source |
698 | Select (From s As String |
699 | In line |
700 | Select s |
701 | Distinct |
702 | Order By s Ascending).ToArray).ToArray |
703 | |
704 | For Each line As String() In source |
705 | union += line |
706 | Next |
707 | |
708 | union = (From s As String |
709 | In union |
710 | Select s |
711 | Distinct |
712 | Order By s Ascending).AsList '获取并集,接下来需要从并集之中去除在两个集合之中都不存在的 |
713 | |
714 | For Each Line As IEnumerable(Of String) In source |
715 | For Each row In source '遍历每一个集合 |
716 | Dim LQuery As IEnumerable(Of String) = From s As String |
717 | In row |
718 | Where Array.IndexOf(Line, s) = -1 |
719 | Select s |
720 | For Each s As String In LQuery |
721 | Call union.Remove(s) '假若line之中存在不存在的元素,则从并集之中移除 |
722 | Next |
723 | Next |
724 | Next |
725 | |
726 | Return union.ToArray ' 剩下的元素都是在所有的序列之中都存在的,既交集元素 |
727 | End Function |
728 | |
729 | ''' <summary> |
730 | ''' 求交集 |
731 | ''' </summary> |
732 | ''' <returns></returns> |
733 | ''' <remarks></remarks> |
734 | ''' |
735 | <ExportAPI("Intersection")> |
736 | Public Function Intersection(ParamArray values$()()) As String() |
737 | Return values.Intersection |
738 | End Function |
739 | |
740 | ''' <summary> |
741 | ''' Does this input string is matched by the specific regex expression? |
742 | ''' (判断所输入的整个字符串是否为进行判断的<paramref name="regex"/>模式, |
743 | ''' 即使用正则表达式所匹配的结果字符串和所输入的字符串一致) |
744 | ''' </summary> |
745 | ''' <param name="str"></param> |
746 | ''' <param name="regex"></param> |
747 | ''' <returns></returns> |
748 | ''' |
749 | <MethodImpl(MethodImplOptions.AggressiveInlining)> |
750 | <ExportAPI("Matched?")> |
751 | <Extension> Public Function MatchPattern(str$, regex$, Optional opt As RegexOptions = RegexICSng) As Boolean |
752 | If str.StringEmpty Then |
753 | Return False |
754 | Else |
755 | Return r.Match(str, regex, opt).Success |
756 | End If |
757 | End Function |
758 | |
759 | ''' <summary> |
760 | ''' Searches the specified input string for the first occurrence of the specified regular expression. |
761 | ''' </summary> |
762 | ''' <param name="input">The string to search for a match.</param> |
763 | ''' <param name="pattern">The regular expression pattern to match.</param> |
764 | ''' <param name="options"></param> |
765 | ''' <returns></returns> |
766 | <ExportAPI("Regex", Info:="Searches the specified input string for the first occurrence of the specified regular expression.")> |
767 | <Extension> Public Function Match(<Parameter("input", "The string to search for a match.")> input$, |
768 | <Parameter("Pattern", "The regular expression pattern to match.")> pattern$, |
769 | Optional options As RegexOptions = RegexOptions.Multiline) As String |
770 | If input.StringEmpty Then |
771 | Return "" |
772 | Else |
773 | Return r.Match(input, pattern, options).Value |
774 | End If |
775 | End Function |
776 | |
777 | ''' <summary> |
778 | ''' Get regex match value from the target input string. |
779 | ''' </summary> |
780 | ''' <param name="input"></param> |
781 | ''' <param name="pattern"></param> |
782 | ''' <param name="options"></param> |
783 | ''' <returns></returns> |
784 | <ExportAPI("Match")> |
785 | <Extension> Public Function Match(input As Match, pattern$, Optional options As RegexOptions = RegexOptions.Multiline) As String |
786 | Return r.Match(input.Value, pattern, options).Value |
787 | End Function |
788 | |
789 | <MethodImpl(MethodImplOptions.AggressiveInlining)> |
790 | <Extension> |
791 | Public Function Matches(input$, pattern$, Optional options As RegexOptions = RegexICSng) As IEnumerable(Of String) |
792 | If input Is Nothing OrElse input.Length = 0 Then |
793 | Return {} |
794 | Else |
795 | Return r.Matches(input, pattern, options).EachValue |
796 | End If |
797 | End Function |
798 | |
799 | ''' <summary> |
800 | ''' Save this string dictionary object as json file. |
801 | ''' </summary> |
802 | ''' <param name="dict"></param> |
803 | ''' <param name="path"></param> |
804 | ''' <returns></returns> |
805 | ''' <remarks> |
806 | ''' 其实,对于字典类型是可以直接使用JSON序列化得到json字符串的,但是在这里是需要 |
807 | ''' 保存接口类型的对象,但是在这里不能够将接口类型进行json序列化,所以进行字符串 |
808 | ''' 的序列化然后拼接出json数据 |
809 | ''' </remarks> |
810 | <Extension> |
811 | <ExportAPI("Write.Dictionary")> |
812 | Public Function SaveTo(dict As IDictionary(Of String, String), path$) As Boolean |
813 | Dim lines$() = dict.Select(AddressOf __json).ToArray |
814 | Dim json$ = "{" & |
815 | vbTab & String.Join("," & vbCrLf & vbTab, lines) & |
816 | "}" |
817 | |
818 | Return json.SaveTo(path, TextEncodings.UTF8WithoutBOM) |
819 | End Function |
820 | |
821 | <MethodImpl(MethodImplOptions.AggressiveInlining)> |
822 | Private Function __json(x As KeyValuePair(Of String, String)) As String |
823 | Return x.Key.GetJson & ": " & x.Value.GetJson |
824 | End Function |
825 | |
826 | ''' <summary> |
827 | ''' Count the string value numbers.(请注意,这个函数是倒序排序的) |
828 | ''' </summary> |
829 | ''' <param name="tokens"></param> |
830 | ''' <returns></returns> |
831 | ''' <remarks></remarks> |
832 | <ExportAPI("Tokens.Count", Info:="Count the string value numbers.")> |
833 | <Extension> Public Function TokenCount(tokens As IEnumerable(Of String), Optional ignoreCase As Boolean = False) As Dictionary(Of String, Integer) |
834 | If Not ignoreCase Then ' 大小写敏感 |
835 | With From s As String |
836 | In tokens |
837 | Select s |
838 | Group s By s Into Count |
839 | |
840 | Return .ToDictionary(Function(x) x.s, |
841 | Function(x) x.Count) |
842 | End With |
843 | Else |
844 | Return tokens.TokenCountIgnoreCase |
845 | End If |
846 | End Function |
847 | |
848 | <Extension> |
849 | Public Function TokenCountIgnoreCase(tokens As IEnumerable(Of String)) As Dictionary(Of String, Integer) |
850 | With tokens.ToArray |
851 | Dim uniques = From s As String |
852 | In .Distinct |
853 | Let data As String = s |
854 | Select UNIQUE_KEY = s.ToLower, data |
855 | Group By UNIQUE_KEY Into Group |
856 | |
857 | Dim LQuery = From ustr |
858 | In uniques |
859 | Let s As String = ustr.UNIQUE_KEY |
860 | Let count As Integer = .Count(s, StringComparison.OrdinalIgnoreCase) |
861 | Select key = ustr.Group.First.data, count |
862 | Order By count Descending |
863 | |
864 | Dim result = LQuery.ToDictionary( |
865 | Function(x) x.key, |
866 | Function(x) x.count) |
867 | |
868 | Return result |
869 | End With |
870 | End Function |
871 | |
872 | ''' <summary> |
873 | ''' This method is used to replace most calls to the Java <see cref="[String].Split"/> method. |
874 | ''' </summary> |
875 | ''' <param name="source"></param> |
876 | ''' <param name="pattern"><see cref="Regex"/> patterns</param> |
877 | ''' <param name="trimTrailingEmptyStrings"></param> |
878 | ''' <returns></returns> |
879 | ''' <remarks></remarks> |
880 | <ExportAPI("StringsSplit", Info:="This method is used to replace most calls to the Java String.split method.")> |
881 | <Extension> Public Function StringSplit(source$, pattern$, |
882 | Optional TrimTrailingEmptyStrings As Boolean = False, |
883 | Optional opt As RegexOptions = RegexICSng) As String() |
884 | If source.StringEmpty Then |
885 | Return {} |
886 | End If |
887 | |
888 | Dim splitArray$() = Regex.Split(source, pattern, options:=opt) |
889 | |
890 | If Not TrimTrailingEmptyStrings OrElse splitArray.Length <= 1 Then |
891 | Return splitArray |
892 | Else |
893 | Return splitArray _ |
894 | .Where(Function(s) |
895 | Return Not String.IsNullOrEmpty(s) |
896 | End Function) _ |
897 | .ToArray |
898 | End If |
899 | End Function |
900 | |
901 | ''' <summary> |
902 | ''' Alias for <see cref="Strings.Split(String, String, Integer, CompareMethod)"/> |
903 | ''' </summary> |
904 | ''' <param name="str$"></param> |
905 | ''' <param name="deli$"></param> |
906 | ''' <returns></returns> |
907 | <MethodImpl(MethodImplOptions.AggressiveInlining)> |
908 | <Extension> |
909 | Public Function SplitBy(str$, deli$, Optional compares As CompareMethod = CompareMethod.Text) As String() |
910 | Return Strings.Split(str, deli, Compare:=compares) |
911 | End Function |
912 | |
913 | ''' <summary> |
914 | ''' 将正则匹配成功的字符串替换为指定的目标字符串:<paramref name="replaceAs"/> |
915 | ''' </summary> |
916 | ''' <param name="s$"></param> |
917 | ''' <param name="pattern$"></param> |
918 | ''' <param name="replaceAs$"></param> |
919 | ''' <param name="opt"></param> |
920 | ''' <returns></returns> |
921 | <Extension> |
922 | Public Function StringReplace(s$, pattern$, replaceAs$, Optional opt As RegexOptions = RegexICSng) As String |
923 | Dim targets$() = r.Matches(s, pattern, opt).ToArray |
924 | Dim sb As New StringBuilder(s) |
925 | |
926 | For Each t As String In targets |
927 | Call sb.Replace(t, replaceAs) |
928 | Next |
929 | |
930 | Return sb.ToString |
931 | End Function |
932 | |
933 | ''' <summary> |
934 | ''' String collection tokenized by a certain delimiter string element. |
935 | ''' </summary> |
936 | ''' <param name="source"></param> |
937 | ''' <param name="delimiter"> |
938 | ''' Using ``String.Equals`` or Regular expression function to determined this delimiter |
939 | ''' </param> |
940 | ''' <returns></returns> |
941 | <Extension> Public Function Split(source As IEnumerable(Of String), |
942 | delimiter$, |
943 | Optional regex As Boolean = False, |
944 | Optional opt As RegexOptions = RegexOptions.Singleline) As IEnumerable(Of String()) |
945 | |
946 | Dim delimiterTest As Assert(Of String) |
947 | |
948 | If regex Then |
949 | With New Regex(delimiter, opt) |
950 | delimiterTest = Function(line) |
951 | Return .Match(line).Value = line |
952 | End Function |
953 | End With |
954 | Else |
955 | delimiterTest = Function(line) |
956 | Return String.Equals(delimiter, line, StringComparison.Ordinal) |
957 | End Function |
958 | End If |
959 | |
960 | Return source.Split(delimiterTest, includes:=False) |
961 | End Function |
962 | |
963 | ''' <summary> |
964 | ''' 这个函数适合将一个很大的数组进行分割 |
965 | ''' </summary> |
966 | ''' <param name="source"></param> |
967 | ''' <param name="assertionDelimiter">分隔符断言,判断当前的对象是不是分隔符</param> |
968 | ''' <param name="includes"></param> |
969 | ''' <returns></returns> |
970 | <Extension> |
971 | Public Iterator Function Split(source As IEnumerable(Of String), |
972 | assertionDelimiter As Assert(Of String), |
973 | Optional includes As Boolean = True) As IEnumerable(Of String()) |
974 | |
975 | Dim list As New List(Of String) |
976 | Dim first As Boolean = True ' first line |
977 | |
978 | For Each line As String In source |
979 | If True = assertionDelimiter(line) Then |
980 | If first Then |
981 | first = False |
982 | Else |
983 | Yield list.ToArray |
984 | |
985 | list.Clear() |
986 | End If |
987 | |
988 | If includes Then |
989 | list.Add(line) |
990 | End If |
991 | Else |
992 | Call list.Add(line) |
993 | End If |
994 | |
995 | first = False |
996 | Next |
997 | |
998 | If list.Count > 0 Then |
999 | Yield list.ToArray |
1000 | End If |
1001 | End Function |
1002 | |
1003 | ''' <summary> |
1004 | ''' String compares using <see cref="String.Equals"/>, if the target value could not be located, |
1005 | ''' then -1 will be return from this function. |
1006 | ''' </summary> |
1007 | ''' <param name="collection"></param> |
1008 | ''' <param name="text"></param> |
1009 | ''' <param name="caseSensitive"></param> |
1010 | ''' <param name="fuzzy"> |
1011 | ''' If fuzzy, then <see cref="InStr"/> will be used if ``String.Equals`` method have no result. |
1012 | ''' </param> |
1013 | ''' <returns></returns> |
1014 | <ExportAPI("Located", Info:="String compares using String.Equals")> |
1015 | <Extension> Public Function Located(collection As IEnumerable(Of String), |
1016 | text As String, |
1017 | Optional caseSensitive As Boolean = True, |
1018 | Optional fuzzy As Boolean = False) As Integer |
1019 | |
1020 | Dim method As StringComparison = |
1021 | If(caseSensitive, |
1022 | StringComparison.Ordinal, |
1023 | StringComparison.OrdinalIgnoreCase) |
1024 | Dim method2 As CompareMethod = |
1025 | If(caseSensitive, |
1026 | CompareMethod.Binary, |
1027 | CompareMethod.Text) |
1028 | |
1029 | For Each str As SeqValue(Of String) In collection.SeqIterator |
1030 | If String.Equals(str.value, text, method) Then |
1031 | Return str.i |
1032 | ElseIf fuzzy Then |
1033 | If InStr(str.value, text, method2) > 0 Then |
1034 | Return str.i |
1035 | End If |
1036 | End If |
1037 | Next |
1038 | |
1039 | Return -1 |
1040 | End Function |
1041 | |
1042 | ''' <summary> |
1043 | ''' |
1044 | ''' </summary> |
1045 | ''' <param name="collection"></param> |
1046 | ''' <param name="text">可以使用通配符</param> |
1047 | ''' <param name="caseSensitive"></param> |
1048 | ''' <returns></returns> |
1049 | <Extension> |
1050 | Public Function WildcardsLocated(collection As IEnumerable(Of String), text$, Optional caseSensitive As Boolean = True) As Integer |
1051 | For Each s As SeqValue(Of String) In collection.SeqIterator |
1052 | If text.WildcardMatch(s.value, Not caseSensitive) Then |
1053 | Return s.i |
1054 | End If |
1055 | Next |
1056 | |
1057 | Return -1 |
1058 | End Function |
1059 | |
1060 | ''' <summary> |
1061 | ''' Search the string by keyword in a string collection. Unlike search function <see cref="StringHelpers.Located(IEnumerable(Of String), String, Boolean, Boolean)"/> |
1062 | ''' using function <see cref="String.Equals"/> function to search string, this function using <see cref="Strings.InStr(String, String, CompareMethod)"/> |
1063 | ''' to search the keyword. |
1064 | ''' </summary> |
1065 | ''' <param name="source"></param> |
1066 | ''' <param name="keyword"></param> |
1067 | ''' <param name="caseSensitive"></param> |
1068 | ''' <returns>返回第一个找到关键词的行数,没有找到则返回-1</returns> |
1069 | <ExportAPI("Lookup", Info:="Search the string by keyword in a string collection.")> |
1070 | <Extension> |
1071 | Public Function Lookup(source As IEnumerable(Of String), keyword As String, Optional caseSensitive As Boolean = True) As Integer |
1072 | Dim method As CompareMethod = If(caseSensitive, CompareMethod.Binary, CompareMethod.Text) |
1073 | Dim i As Integer |
1074 | |
1075 | For Each line As String In source |
1076 | If InStr(line, keyword, method) > 0 Then |
1077 | Return i |
1078 | Else |
1079 | i += 1 |
1080 | End If |
1081 | Next |
1082 | |
1083 | Return -1 |
1084 | End Function |
1085 | |
1086 | ''' <summary> |
1087 | ''' 判断目标字符串是否与字符串参数数组之中的任意一个字符串相等,大小写不敏感,假若没有相等的字符串,则会返回空字符串,假若找到了相等的字符串,则会返回该字符串 |
1088 | ''' </summary> |
1089 | ''' <param name="source"></param> |
1090 | ''' <param name="compareTo"></param> |
1091 | ''' <returns></returns> |
1092 | <Extension> |
1093 | <ExportAPI("Equals.Any")> |
1094 | Public Function EqualsAny(source$, ParamArray compareTo As String()) As String |
1095 | Dim index As Integer = compareTo.Located(source, False) |
1096 | |
1097 | If index = -1 Then |
1098 | Return "" |
1099 | Else |
1100 | Return compareTo(index) |
1101 | End If |
1102 | End Function |
1103 | |
1104 | ''' <summary> |
1105 | ''' 查找到任意一个既返回位置,大小写不敏感,假若查找不到,则返回-1值,判断是否查找成功,可以使用 <0 来完成, |
1106 | ''' 因为是通过InStr来完成的,所以查找成功的时候,最小的值是1,即字符串序列的第一个位置,也是元素0位置 |
1107 | ''' </summary> |
1108 | ''' <param name="text"></param> |
1109 | ''' <param name="find"></param> |
1110 | ''' <returns></returns> |
1111 | <ExportAPI("InStr.Any")> |
1112 | <Extension> Public Function InStrAny(text$, ParamArray find$()) As Integer |
1113 | For Each token As String In find |
1114 | Dim idx% = Strings.InStr(text, token, CompareMethod.Text) |
1115 | |
1116 | If idx > 0 Then |
1117 | Return idx |
1118 | End If |
1119 | Next |
1120 | |
1121 | Return -1 |
1122 | End Function |
1123 | |
1124 | ''' <summary> |
1125 | ''' Removes the duplicated string from the source <paramref name="strings"/> collection |
1126 | ''' with string compare ignore case. |
1127 | ''' </summary> |
1128 | ''' <param name="strings"></param> |
1129 | ''' <returns></returns> |
1130 | <Extension> |
1131 | Public Function DistinctIgnoreCase(strings As IEnumerable(Of String)) As IEnumerable(Of String) |
1132 | Dim list = strings.Distinct.ToArray |
1133 | Dim lowerBuffers As New Dictionary(Of String, List(Of String)) |
1134 | |
1135 | For Each s As String In list |
1136 | With s.ToLower |
1137 | If Not lowerBuffers.ContainsKey(.ByRef) Then |
1138 | lowerBuffers(.ByRef) = New List(Of String) |
1139 | End If |
1140 | lowerBuffers(.ByRef).Add(s) |
1141 | End With |
1142 | Next |
1143 | |
1144 | Dim distinct = lowerBuffers _ |
1145 | .Select(Function(pack) |
1146 | Dim n$() = pack _ |
1147 | .Value _ |
1148 | .Where(Function(s) s <> pack.Key) _ |
1149 | .ToArray |
1150 | |
1151 | ' 尽量不返回全部都是小写的字符串 |
1152 | If n.Length > 0 Then |
1153 | Return n.First |
1154 | Else |
1155 | Return pack.Key |
1156 | End If |
1157 | End Function) _ |
1158 | .ToArray |
1159 | |
1160 | Return distinct |
1161 | End Function |
1162 | |
1163 | ''' <summary> |
1164 | ''' Line tokens. **=> Parsing the text into lines by using <see cref="vbCr"/>, <see cref="vbLf"/>**. |
1165 | ''' (函数对文本进行分行操作,由于在Windows(<see cref="VbCrLf"/>)和 |
1166 | ''' Linux(<see cref="vbCr"/>, <see cref="vbLf"/>)平台上面所生成的文本文件的换行符有差异, |
1167 | ''' 所以可以使用这个函数来进行统一的分行操作) |
1168 | ''' </summary> |
1169 | ''' <param name="s"></param> |
1170 | ''' <returns></returns> |
1171 | ''' <param name="trim"> |
1172 | ''' Set <see cref="Boolean.FalseString"/> to avoid a reader bug in the csv data reader <see cref="BufferedStream"/> |
1173 | ''' </param> |
1174 | ''' <param name="escape"> |
1175 | ''' 是否需要将字符串之中的``\n``转义为换行之后再进行分割?默认不进行转义 |
1176 | ''' </param> |
1177 | <ExportAPI("LineTokens")> |
1178 | <Extension> Public Function LineTokens(s$, Optional trim As Boolean = True, Optional escape As Boolean = False) As String() |
1179 | If String.IsNullOrEmpty(s) Then |
1180 | Return {} |
1181 | ElseIf escape Then |
1182 | s = s.Replace("\n", ASCII.LF) |
1183 | End If |
1184 | |
1185 | Dim lf As Boolean = InStr(s, vbLf) > 0 |
1186 | Dim cr As Boolean = InStr(s, vbCr) > 0 |
1187 | |
1188 | If Not (cr OrElse lf) Then ' 没有分行符,则只有一行 |
1189 | Return {s} |
1190 | End If |
1191 | |
1192 | If lf AndAlso cr Then |
1193 | If trim Then ' 假若将这个换行替换掉,在Csv文件读取模块会出现bug。。。。。不清楚是怎么回事 |
1194 | s = s.Replace(vbCr, "") |
1195 | End If |
1196 | |
1197 | Return Splitter.Split(s, vbLf, True) |
1198 | End If |
1199 | |
1200 | If lf Then |
1201 | Return Splitter.Split(s, vbLf, True) |
1202 | Else |
1203 | Return Splitter.Split(s, vbCr, True) |
1204 | End If |
1205 | End Function |
1206 | |
1207 | ''' <summary> |
1208 | ''' Does the <paramref name="token"/> is located at the last of <paramref name="s"/> text string. |
1209 | ''' </summary> |
1210 | ''' <param name="s$"></param> |
1211 | ''' <param name="token$"></param> |
1212 | ''' <returns></returns> |
1213 | <Extension> Public Function TextLast(s$, token$) As Boolean |
1214 | Dim lastIndex% = s.Length - token.Length |
1215 | ' 因为token子字符串可能会在s字符串之中出现多次,所以直接使用正向的InStr函数 |
1216 | ' 可能会导致匹配到第一个字符串而无法正确的匹配上最后一个token,所以在这里使用 |
1217 | ' InstrRev来避免这个问题 |
1218 | Dim val% = InStrRev(s, token) |
1219 | Return lastIndex = val |
1220 | End Function |
1221 | End Module |