ModValidate.vb 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380
  1. '提供不同的输入验证方法,名称以 Validate 开头
  2. Public Module ModValidate
  3. ''' <summary>
  4. ''' 进行输入验证,并返回错误原因。
  5. ''' 如果没有错误则返回空字符串。
  6. ''' </summary>
  7. Public Function Validate(Text As String, ValidateRules As IEnumerable(Of Validate)) As String
  8. Dim Result As String = ""
  9. For Each ValidateRule As Validate In ValidateRules
  10. Result = ValidateRule.Validate(Text)
  11. If Result Is Nothing Then Return ""
  12. If Result <> "" Then Return Result
  13. Next
  14. Return Result
  15. End Function
  16. End Module
  17. ''' <summary>
  18. ''' 输入验证规则基类。要查看所有的输入验证规则,可在输入 Validate 后查看自动补全。
  19. ''' </summary>
  20. Public MustInherit Class Validate
  21. ''' <summary>
  22. ''' 验证某字符串是否符合验证要求。若符合,返回空字符串;若不符合,返回错误原因;若需要中断检查并直接通过,返回 Nothing。
  23. ''' </summary>
  24. Public MustOverride Function Validate(Str As String) As String
  25. End Class
  26. ''' <summary>
  27. ''' 若为空则直接通过检查。
  28. ''' </summary>
  29. Public Class ValidateNullable
  30. Inherits Validate
  31. Public Sub New()
  32. End Sub
  33. Public Overrides Function Validate(Str As String) As String
  34. If IsNothing(Str) OrElse String.IsNullOrEmpty(Str) Then Return Nothing
  35. Return ""
  36. End Function
  37. End Class
  38. ''' <summary>
  39. ''' 不能为 Nothing 或空字符串(不包括全空格检查)。
  40. ''' </summary>
  41. Public Class ValidateNullOrEmpty
  42. Inherits Validate
  43. Public Sub New()
  44. End Sub
  45. Public Overrides Function Validate(Str As String) As String
  46. If IsNothing(Str) OrElse String.IsNullOrEmpty(Str) Then Return "输入内容不能为空!"
  47. Return ""
  48. End Function
  49. End Class
  50. ''' <summary>
  51. ''' 不能为 Nothing 或空字符串(包括全空格检查)。
  52. ''' </summary>
  53. Public Class ValidateNullOrWhiteSpace
  54. Inherits Validate
  55. Public Sub New()
  56. End Sub
  57. Public Overrides Function Validate(Str As String) As String
  58. If IsNothing(Str) OrElse String.IsNullOrWhiteSpace(Str) Then Return "输入内容不能为空!"
  59. Return ""
  60. End Function
  61. End Class
  62. ''' <summary>
  63. ''' 必须满足正则表达式。
  64. ''' </summary>
  65. Public Class ValidateRegex
  66. Inherits Validate
  67. Public Property Regex As String
  68. Public Property ErrorDescription As String = "正则检查失败!"
  69. Public Sub New()
  70. End Sub '用于 XAML 初始化
  71. Public Sub New(Regex As String, Optional ErrorDescription As String = "正则检查失败!")
  72. Me.Regex = Regex
  73. Me.ErrorDescription = ErrorDescription
  74. End Sub
  75. Public Overrides Function Validate(Str As String) As String
  76. If Not RegexCheck(Str, Regex) Then Return ErrorDescription
  77. Return ""
  78. End Function
  79. End Class
  80. ''' <summary>
  81. ''' 必须是一个完整网址。
  82. ''' </summary>
  83. Public Class ValidateHttp
  84. Inherits Validate
  85. Public Sub New()
  86. End Sub '用于 XAML 初始化
  87. Public Overrides Function Validate(Str As String) As String
  88. If Str.EndsWithF("/") Then Str = Str.Substring(0, Str.Length - 1)
  89. If Not RegexCheck(Str, "^(http[s]?)\://") Then Return "输入的网址无效!"
  90. Return ""
  91. End Function
  92. End Class
  93. ''' <summary>
  94. ''' 必须为整数。
  95. ''' </summary>
  96. Public Class ValidateInteger
  97. Inherits Validate
  98. Public Property Min As Integer
  99. Public Property Max As Integer = Integer.MaxValue
  100. Public Sub New()
  101. End Sub '用于 XAML 初始化
  102. Public Sub New(Min As Integer, Max As Integer)
  103. Me.Min = Min
  104. Me.Max = Max
  105. End Sub
  106. Public Overrides Function Validate(Str As String) As String
  107. If Str.Length > 9 Then Return "请输入一个大小合理的数字!"
  108. Dim Valed As Integer = Val(Str)
  109. If Valed.ToString <> Str Then Return "请输入一个整数!"
  110. If Val(Str) > Max Then Return "不可超过 " & Max & "!"
  111. If Val(Str) < Min Then Return "不可低于 " & Min & "!"
  112. Return ""
  113. End Function
  114. End Class
  115. ''' <summary>
  116. ''' 长度限制。
  117. ''' </summary>
  118. Public Class ValidateLength
  119. Inherits Validate
  120. Public Property Min As Integer = 0
  121. Public Property Max As Integer = Integer.MaxValue
  122. Public Sub New()
  123. End Sub '用于 XAML 初始化
  124. Public Sub New(Min As Integer, Optional Max As Integer = Integer.MaxValue)
  125. Me.Min = Min
  126. Me.Max = Max
  127. End Sub
  128. Public Overrides Function Validate(Str As String) As String
  129. If Len(Str) <> Max AndAlso Max = Min Then Return $"长度必须为 {Max} 个字符!"
  130. If Len(Str) > Max Then Return $"长度最长为 {Max} 个字符!"
  131. If Len(Str) < Min Then Return $"长度至少需 {Min} 个字符!"
  132. Return ""
  133. End Function
  134. End Class
  135. ''' <summary>
  136. ''' 不能包含某些特定字符串。忽略大小写。
  137. ''' </summary>
  138. Public Class ValidateExcept
  139. Inherits Validate
  140. Public Property Excepts As ObjectModel.Collection(Of String) = New ObjectModel.Collection(Of String)
  141. Public Property ErrorMessage As String
  142. Public Sub New()
  143. ErrorMessage = "输入内容不能包含 %"
  144. End Sub '用于 XAML 初始化
  145. Public Sub New(Excepts As ObjectModel.Collection(Of String), Optional ErrorMessage As String = "输入内容不能包含 %")
  146. Me.Excepts = Excepts
  147. Me.ErrorMessage = ErrorMessage
  148. End Sub
  149. Public Sub New(Excepts As IEnumerable, Optional ErrorMessage As String = "输入内容不能包含 %")
  150. Me.Excepts = New ObjectModel.Collection(Of String)
  151. Me.ErrorMessage = ErrorMessage
  152. For Each Data As String In Excepts
  153. Me.Excepts.Add(Data)
  154. Next
  155. End Sub
  156. Public Overrides Function Validate(Str As String) As String
  157. For Each Ch As String In Excepts
  158. If Str.IndexOfF(Ch, True) >= 0 Then
  159. If IsNothing(ErrorMessage) Then ErrorMessage = ""
  160. Return ErrorMessage.Replace("%", Ch)
  161. End If
  162. Next
  163. Return ""
  164. End Function
  165. End Class
  166. ''' <summary>
  167. ''' 不能与某些特定字符串相同。
  168. ''' </summary>
  169. Public Class ValidateExceptSame
  170. Inherits Validate
  171. Public Property Excepts As ObjectModel.Collection(Of String) = New ObjectModel.Collection(Of String)
  172. Public Property ErrorMessage As String
  173. Public Property IgnoreCase As Boolean = False
  174. Public Sub New()
  175. End Sub
  176. Public Sub New(Excepts As ObjectModel.Collection(Of String), Optional ErrorMessage As String = "输入内容不能为 %", Optional IgnoreCase As Boolean = False)
  177. Me.Excepts = Excepts
  178. Me.ErrorMessage = ErrorMessage
  179. Me.IgnoreCase = IgnoreCase
  180. End Sub
  181. Public Sub New(Excepts As IEnumerable, Optional ErrorMessage As String = "输入内容不能为 %", Optional IgnoreCase As Boolean = False)
  182. Me.Excepts = New ObjectModel.Collection(Of String)
  183. For Each Data As String In Excepts
  184. Me.Excepts.Add(Data)
  185. Next
  186. Me.ErrorMessage = ErrorMessage
  187. Me.IgnoreCase = IgnoreCase
  188. End Sub
  189. Public Overrides Function Validate(Str As String) As String
  190. If Str Is Nothing Then Return ErrorMessage.Replace("%", "null")
  191. For Each Ch As String In Excepts
  192. If IgnoreCase Then
  193. If Str.ToLower = Ch.ToLower Then Return ErrorMessage.Replace("%", Ch)
  194. Else
  195. If Str.Equals(Ch) Then Return ErrorMessage.Replace("%", Ch) '使用 = 不确定是否会忽略大小写
  196. End If
  197. Next
  198. Return ""
  199. End Function
  200. End Class
  201. ''' <summary>
  202. ''' 对文件夹名的粗略的特化检测。
  203. ''' </summary>
  204. Public Class ValidateFolderName
  205. Inherits Validate
  206. Public Property Path As String
  207. Public Property UseMinecraftCharCheck As Boolean = True
  208. Public Property IgnoreCase As Boolean = True
  209. Private ReadOnly PathIgnore As IEnumerable(Of DirectoryInfo)
  210. Public Sub New()
  211. End Sub
  212. Public Sub New(Path As String, Optional UseMinecraftCharCheck As Boolean = True, Optional IgnoreCase As Boolean = True)
  213. Me.Path = Path
  214. Me.IgnoreCase = IgnoreCase
  215. Me.UseMinecraftCharCheck = UseMinecraftCharCheck
  216. On Error Resume Next
  217. PathIgnore = New DirectoryInfo(Path).EnumerateDirectories
  218. End Sub
  219. Public Overrides Function Validate(Str As String) As String
  220. Try
  221. '检查是否为空
  222. Dim LengthCheck As String = New ValidateNullOrWhiteSpace().Validate(Str)
  223. If LengthCheck <> "" Then Return LengthCheck
  224. '检查空格
  225. If Str.StartsWithF(" ") Then Return "文件夹名不能以空格开头!"
  226. If Str.EndsWithF(" ") Then Return "文件夹名不能以空格结尾!"
  227. '检查长度
  228. LengthCheck = New ValidateLength(1, 100).Validate(Str)
  229. If LengthCheck <> "" Then Return LengthCheck
  230. '检查尾部小数点
  231. If Str.EndsWithF(".") Then Return "文件夹名不能以小数点结尾!"
  232. '检查特殊字符
  233. Dim CharactCheck As String = New ValidateExcept(IO.Path.GetInvalidFileNameChars() & If(UseMinecraftCharCheck, "!;", ""), "文件夹名不可包含 % 字符!").Validate(Str)
  234. If CharactCheck <> "" Then Return CharactCheck
  235. '检查特殊字符串
  236. Dim InvalidStrCheck As String = New ValidateExceptSame({"CON", "PRN", "AUX", "CLOCK$", "NUL", "COM0", "COM1", "COM2", "COM3", "COM4", "COM5", "COM6", "COM7", "COM8", "COM9", "LPT0", "LPT1", "LPT2", "LPT3", "LPT4", "LPT5", "LPT6", "LPT7", "LPT8", "LPT9"}, "文件夹名不可为 %!", True).Validate(Str)
  237. If InvalidStrCheck <> "" Then Return InvalidStrCheck
  238. '检查 NTFS 8.3 文件名(#4505)
  239. If RegexCheck(Str, ".{2,}~\d") Then Return "文件夹名不能包含这一特殊格式!"
  240. '检查文件夹重名
  241. Dim Arr As New List(Of String)
  242. If PathIgnore IsNot Nothing Then
  243. For Each Folder As DirectoryInfo In PathIgnore
  244. Arr.Add(Folder.Name)
  245. Next
  246. End If
  247. Dim SameNameCheck = New ValidateExceptSame(Arr, "不可与现有文件夹重名!", IgnoreCase).Validate(Str)
  248. If Not SameNameCheck = "" Then Return SameNameCheck
  249. Return ""
  250. Catch ex As Exception
  251. Log(ex, "检查文件夹名出错")
  252. Return "错误:" & ex.Message
  253. End Try
  254. End Function
  255. End Class
  256. ''' <summary>
  257. ''' 对文件名的粗略的特化检测。
  258. ''' </summary>
  259. Public Class ValidateFileName
  260. Inherits Validate
  261. Public Property Name As String
  262. Public Property UseMinecraftCharCheck As Boolean = True
  263. Public Property IgnoreCase As Boolean = True
  264. Public Property ParentFolder As String = Nothing
  265. Public Property RequireParentFolderExists = True
  266. Public Sub New()
  267. End Sub
  268. Public Sub New(Name As String, Optional UseMinecraftCharCheck As Boolean = True, Optional IgnoreCase As Boolean = True)
  269. Me.Name = Name
  270. Me.IgnoreCase = IgnoreCase
  271. Me.UseMinecraftCharCheck = UseMinecraftCharCheck
  272. End Sub
  273. Public Overrides Function Validate(Str As String) As String
  274. Try
  275. '检查是否为空
  276. Dim LengthCheck As String = New ValidateNullOrWhiteSpace().Validate(Str)
  277. If Not LengthCheck = "" Then Return LengthCheck
  278. '检查空格
  279. If Str.StartsWithF(" ") Then Return "文件名不能以空格开头!"
  280. If Str.EndsWithF(" ") Then Return "文件名不能以空格结尾!"
  281. '检查长度
  282. LengthCheck = New ValidateLength(1, 253).Validate(Str & If(ParentFolder, ""))
  283. If LengthCheck <> "" Then Return LengthCheck
  284. '检查尾部小数点
  285. If Str.EndsWithF(".") Then Return "文件名不能以小数点结尾!"
  286. '检查特殊字符
  287. Dim CharactCheck As String = New ValidateExcept(IO.Path.GetInvalidFileNameChars() & If(UseMinecraftCharCheck, "!;", ""), "文件名不可包含 % 字符!").Validate(Str)
  288. If CharactCheck <> "" Then Return CharactCheck
  289. '检查特殊字符串
  290. Dim InvalidStrCheck As String = New ValidateExceptSame({"CON", "PRN", "AUX", "CLOCK$", "NUL", "COM0", "COM1", "COM2", "COM3", "COM4", "COM5", "COM6", "COM7", "COM8", "COM9", "LPT0", "LPT1", "LPT2", "LPT3", "LPT4", "LPT5", "LPT6", "LPT7", "LPT8", "LPT9"}, "文件名不可为 %!", True).Validate(Str)
  291. If InvalidStrCheck <> "" Then Return InvalidStrCheck
  292. '检查 NTFS 8.3 文件名(#4505)
  293. If RegexCheck(Str, ".{2,}~\d") Then Return "文件名不能包含这一特殊格式!"
  294. '检查文件重名
  295. If ParentFolder IsNot Nothing Then
  296. Dim DirInfo As New DirectoryInfo(ParentFolder)
  297. If DirInfo.Exists Then
  298. Dim SameNameCheck = New ValidateExceptSame(DirInfo.EnumerateFiles("*").Select(Function(f) f.Name),
  299. "不可与现有文件重名!", IgnoreCase).Validate(Str)
  300. If Not SameNameCheck = "" Then Return SameNameCheck
  301. Else
  302. If RequireParentFolderExists Then Return $"父文件夹不存在:{ParentFolder}"
  303. End If
  304. End If
  305. Return ""
  306. Catch ex As Exception
  307. Log(ex, "检查文件名出错")
  308. Return "错误:" & ex.Message
  309. End Try
  310. End Function
  311. End Class
  312. ''' <summary>
  313. ''' 要求输入一个可用的文件夹路径。
  314. ''' </summary>
  315. Public Class ValidateFolderPath
  316. Inherits Validate
  317. Public Property UseMinecraftCharCheck As Boolean = True
  318. Public Sub New()
  319. End Sub
  320. Public Sub New(UseMinecraftCharCheck As Boolean)
  321. Me.UseMinecraftCharCheck = UseMinecraftCharCheck
  322. End Sub
  323. Public Overrides Function Validate(Str As String) As String
  324. '去除尾部斜线,统一为 \
  325. Str = Str.Replace("/", "\")
  326. If Not Str.TrimEnd("\").EndsWithF(":") Then Str = Str.TrimEnd("\")
  327. '检查是否为空
  328. Dim LengthCheck As String = New ValidateNullOrWhiteSpace().Validate(Str)
  329. If LengthCheck <> "" Then Return LengthCheck
  330. '检查长度
  331. LengthCheck = New ValidateLength(1, 254).Validate(Str)
  332. If LengthCheck <> "" Then Return LengthCheck
  333. '检查开头
  334. If Str.StartsWithF("\\Mac\") Then GoTo Fin
  335. For Each Drive As DriveInfo In My.Computer.FileSystem.Drives
  336. If Str.ToUpper = Drive.Name Then Return ""
  337. If Str.StartsWithF(Drive.Name, True) Then GoTo Fin
  338. Next
  339. Return "文件夹路径头存在错误!"
  340. Fin:
  341. '对首层以外的路径检查
  342. For i = If(Str.StartsWithF("\\Mac\"), 2, 1) To Str.Split("\").Count - 1
  343. Dim SubStr As String = Str.Split("\")(i)
  344. '检查是否为空
  345. Dim SubLengthCheck As String = New ValidateNullOrWhiteSpace().Validate(SubStr)
  346. If Not SubLengthCheck = "" Then Return "文件夹路径存在错误!"
  347. '检查特殊字符
  348. Dim CharactCheck As String = New ValidateExcept(IO.Path.GetInvalidFileNameChars() & If(UseMinecraftCharCheck, "!;", ""), "路径中存在无效字符!").Validate(SubStr)
  349. If Not CharactCheck = "" Then Return CharactCheck
  350. '检查头部空格
  351. If SubStr.StartsWithF(" ") Then Return "文件夹名不能以空格开头!"
  352. If SubStr.EndsWithF(" ") Then Return "文件夹名不能以空格结尾!"
  353. '检查尾部小数点
  354. If SubStr.EndsWithF(".") Then Return "文件夹名不能以小数点结尾!"
  355. '检查特殊字符串
  356. Dim InvalidStrCheck As String = New ValidateExceptSame({"CON", "PRN", "AUX", "CLOCK$", "NUL", "COM0", "COM1", "COM2", "COM3", "COM4", "COM5", "COM6", "COM7", "COM8", "COM9", "LPT0", "LPT1", "LPT2", "LPT3", "LPT4", "LPT5", "LPT6", "LPT7", "LPT8", "LPT9"}, "文件夹名不可为 %!").Validate(SubStr)
  357. If Not InvalidStrCheck = "" Then Return InvalidStrCheck
  358. '检查 NTFS 8.3 文件名(#4505)
  359. If RegexCheck(Str, ".{2,}~\d") Then Return "文件夹名不能包含这一特殊格式!"
  360. Next
  361. Return ""
  362. End Function
  363. End Class