ModValidate.vb 16 KB

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