| 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 |