下午好,
我是VB.net的新手,我正在使用Visual Studio Express 2012进行VB.Net Windows窗体项目。
我需要一些关于代码的帮助,因为我不确定如何做我想做的事情。
情景如下:
用户选择表单上的目录并按下按钮。然后,应用程序将查找某些文件并将其移动到另一个目录。在该新目录中,它将找到包含三个字符代码的文件名。
然后,应用程序将为xml文件中的每个代码分配相应的docgroup,doctype和docsubtype值。然后将其输出到文本文件。
让应用程序知道xml文件中要使用的docgroup,doctype和docsubtype值取决于文件文件名,这就是我对如何做的迷失。
我的xml文件结构如下。请注意,这些值不是静态的,用户可以在我的设置表单中随时更改这些值。
<Settings>
<ApplicationSettings>
<code>FTO</code>
<docgroup>Operations</docgroup>
<doctype>Funds Transfer</doctype>
<docsubtype>Out</docsubtype>
<code>FTI</code>
<docgroup>null</docgroup>
<doctype>null</doctype>
<docsubtype>null</docsubtype>
<code>ACL</code>
<docgroup>Documentation</docgroup>
<doctype>Client Documentation</doctype>
<docsubtype>Termination</docsubtype>
<code>TBA</code>
<docgroup>Operations</docgroup>
<doctype>Funds Transfer Credit</doctype>
<docsubtype>Reversed</docsubtype>
</ApplicationSettings>
</Settings>
例如:
用户选择\ ServerA \ ITDept \ files目录,此目录中的所有文件将始终具有以下命名约定:
AccNum-YYYYMMDD-code示例:123456-20130610-FTO
\服务器A \ ITDept \文件
12345-20130610-FTO已审核并已扫描.pdf
54265-20130512-FTI A1.pdf
45752-20121204-TBA.pdf
因此,如果我能弄清楚如何编写此代码,这些文件的输出将如下所示:
\\ServerA\ITDept\files\12345-20130610-FTO Reviewed and scanned.pdf|12345|_||Operations| Funds Transfer|Out|swfoi6848484|06/10/2013|
\\ServerA\ITDept\files\54265-20130512-FTI A1.pdf|54265|_||NULL| NULL|NULL|swfoi15157|05/12/2013|
\\ServerA\ITDept\files\45752-20121204-TBA.pdf|45752|_||Operations|Funds Transfer|Reversed|swfoi54572258|12/04/2012|
目录中还有其他文件包含其他代码,如果xml文件中没有这些代码,则应忽略这些文件。
我的代码如下。一切工作“除了”将xml文件中的特定docgroup,doctype和docsubtype值合并到输出文件的最后一步。
Imports System
Imports System.Xml
Imports System.Text.RegularExpressions
Public Class Userform
Dim xmlfile As String = "\\ServerA\ITDept\XML\Settings.xml"
Private Sub Userform_Load(sender As Object, e As EventArgs) Handles MyBase.Load
'Check if Setting.xml exists, if not show message box and close application.
If IO.File.Exists(xmlfile) = False Then
MessageBox.Show("Cannot locate Settings.xml file. Please contact IT Department for assistance.", "ERROR")
Me.Close()
End If
End Sub
Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
If FolderBrowserDialog1.ShowDialog() = Windows.Forms.DialogResult.OK Then
TextBox1.Text = FolderBrowserDialog1.SelectedPath
End If
End Sub
Private Sub filebtn_Click(sender As Object, e As EventArgs) Handles filebtn.Click
'New thread will run main tasks of program
BackgroundWorker1.RunWorkerAsync()
End Sub
'Function used to get date in file name and use value as MM/DD/YYYY in output file
Private Function GetFormattedDateFromFileName(ByVal fileName As String) As String
Dim parts() As String = fileName.Split("-")
If parts.Length = 3 Then
Dim dt As DateTime
If DateTime.TryParseExact(parts(1), "yyyyMMdd", Nothing, Globalization.DateTimeStyles.None, dt) Then
Return dt.ToString("MM/dd/yyyy")
End If
End If
Return ""
End Function
Private Sub BackgroundWorker1_DoWork(sender As Object, e As ComponentModel.DoWorkEventArgs) Handles BackgroundWorker1.DoWork
'create directory in input folder with timestamp as the directory name.
Dim destdir As String = [String].Format("\\ServerA\ITDept\files\{0}", DateTime.Now.ToString("MMddyyyyhhmmss"))
System.IO.Directory.CreateDirectory(destdir)
'read directory and look for filenames that match pattern and have code elements from xml file
Dim regElemName As New Regex("^code")
Dim root = XElement.Load(xmlfile)
Dim codeElements = root.Element("ApplicationSettings").Elements().Where(Function(xe) regElemName.IsMatch(xe.Name.LocalName)).Select(Function(xe) xe.Value)
Dim codes = String.Join("|", codeElements.ToArray())
Dim regFileName As New Regex(String.Format("^\d+\-(?<Year>(19|20)[0-9][0-9])(?<Month>0[1-9]|12|11|10)(?<Day>[12]\d|0[1-9]|3[01])\-{0}$", codes))
Dim files = IO.Directory.GetFiles(TextBox1.Text, "*.pdf", IO.SearchOption.TopDirectoryOnly).Where(Function(path) regFileName.IsMatch(IO.Path.GetFileName(path)))
For Each file As String In files
System.IO.File.Move(file, System.IO.Path.Combine(destdir, System.IO.Path.GetFileName(file)))
Next
'Define random numbers
Dim randomclass As New System.Random()
Dim randomnumber As Integer
'create txt file from destdir of all files for output.
Dim str As String = String.Empty
For Each rfiles As String In System.IO.Directory.GetFiles(destdir)
randomnumber = randomclass.Next(10000, 99999)
Dim formattedDate As String = GetFormattedDateFromFileName(rfiles)
str = str & rfiles & "|" & System.IO.Path.GetFileNameWithoutExtension(rfiles).Split("-")(0).Trim & "|" & "_" & "||" & "docgroup_value" & "|" & "doctype_value" & "|" & "docsubtype_value" & "|" & "swfoi" & randomnumber & "|" & formattedDate & "|" & Environment.NewLine
Next
Dim outputname As String = [String].Format("\\ServerA\ITDept\Index\swfoi{0}.txt", DateTime.Now.ToString("MMddyyyyhhmmss"))
System.IO.File.WriteAllText(outputname, str)
End Sub
End Class
有人可以帮我完成此代码吗?
亲切的问候, 甲
答案 0 :(得分:0)
你几乎走在正确的轨道上 - 我建议你做一些事情来完成你的代码。我的建议是一种方式 - 毫无疑问是其他方式,因为编程中的大多数事情都可以通过多种方式完成。有时候一种方式比另一种方式更好或更优选,有时它只是归结为个人偏好或给定团队的编码标准。
首先,我修改了您的XML文件,以便docgroup,doctype和docsubtype值是代码元素的子代。 XML本质上是分层的(考虑Windows资源管理器中的文件夹和文件),以这种方式组织XML使搜索更容易。
所以:
<Settings>
<ApplicationSettings>
<code id="FTO">
<docgroup>Operations</docgroup>
<doctype>Funds Transfer</doctype>
<docsubtype>Out</docsubtype>
</code>
<code id="FTI">
<docgroup>null</docgroup>
<doctype>null</doctype>
<docsubtype>null</docsubtype>
</code>
<code id="ACL">
<docgroup>Documentation</docgroup>
<doctype>Client Documentation</doctype>
<docsubtype>Termination</docsubtype>
</code>
<code id="TBA">
<docgroup>Operations</docgroup>
<doctype>Funds Transfer Credit</doctype>
<docsubtype>Reversed</docsubtype>
</code>
</ApplicationSettings>
</Settings>
请注意,我将<code>
元素的值移动到属性,因为您不能将XML中的text和children元素混合为给定元素(至少不是我所知道的)。 / p>
接下来,我将解析XML一次,并使用Dictionary来保存数据,代码作为键,List<string>
保存docgroup,doctype和docsubtype值。
另外,我不会使用正则表达式来查找XML中的匹配项 - 正则表达式非常适合模式匹配(比如查找适合某种模式的文件名),但在很多其他场景中都是过度杀伤。另外,另一种搜索正确代码的方法是:
Dim codeElements = root.Descendants("code").Select(Function(xe) xe.Attribute("id").Value).ToArray()
上面的代码基本上是说给我所有名为“code”的节点,它们是根元素的子节点,并将它们的“id”属性的值放在数组中。
要从XML构建Dictionary(Of String, List(Of String))
(使用我发布的XML格式),您可以这样做:
Dim docTypes As Dictionary(Of String, String) = root.Descendants("code") _
.ToDictionary(Function(xe) xe.Attribute("id").Value,
Function(xe) New List(Of String)(New String() _
{ xe.Element("docgroup").Value, _
xe.Element("doctype").Value, _
xe.Element("docsubtype").Value }))
(您可能需要在IDE中使用格式化一点 - 我在此处将其分解为可读性)。这会给你一个字典,其中键是代码,子元素是一个列表(元素0 = docgroup,1 = doctype和2 = docsubtype)。
您可以从字典中获取正确的docgroup,doctype和docsubtype。
总而言之,你会有这样的事情:
首先,替换以下代码行:
Dim regElemName As New Regex("^code")
Dim root = XElement.Load(xmlfile)
Dim codeElements = root.Element("ApplicationSettings").Elements().Where(Function(xe) regElemName.IsMatch(xe.Name.LocalName)).Select(Function(xe) xe.Value)
Dim codes = String.Join("|", codeElements.ToArray())
有了这个(以下说明):
Dim xml As XElement = XElement.Load(xmlfile)
Dim CodeInfo As Dictionary(Of String, List(Of String)) = _
xml.Descendants("code") _
.ToDictionary(Function(xe) xe.Attribute("id").Value, _
Function(xe) New List(Of String)(New String() _
{ xe.Element("docgroup").Value, _
xe.Element("doctype").Value, _
xe.Element("docsubtype").Value }))
Dim codes As String = String.Join("|", CodeInfo.Keys)
简而言之,上面的代码加载XML文件,将其解析为字典(代码作为键,文档类型信息的List(Of String)
作为值。然后使用的代码的字符串通过在字典的String.Join
属性上调用Keys
来创建正则表达式。
现在,当您将信息写入文本文件时,您可以根据代码获取正确的值。这里也有一些变化。
首先,在创建文件行之前,我会在-
上拆分文件名,因为您需要它来获取正确的代码。假设您的所有代码都是三个字符,那么您只需要分割数组中第三个元素的前三个字符。您将传递该值作为获取代码的正确doc类型值的键。
其次,我建议使用StringBuilder
(您可能需要在程序中添加Imports System.Text
)。原因是字符串连接可能会变得昂贵(想象一下,如果你有1000个文件可以通过)。字符串是不可变的 - 意味着它们不能被改变。所以当你有类似的东西时:
Dim str1 As String = "Hello"
str1 = str1 & " World"
您可能会认为“世界”已添加到str1中。实际上,创建了一个新的字符串,其中str1添加了“World” - 所以想象一下如果你这样做了1000次就会发生什么。为了便于阅读,我还使用了String.Format
。
替换此行:
Dim str As String = String.Empty
以下内容:
Dim str As New StringBuilder()
Dim fileNameParts As String()
Dim docCode As String = String.Empty
Dim str As New StringBuilder()
然后我会用以下代码替换您的str = str & rfiles &...
行:
fileNameParts = System.IO.Path.GetFileNameWithoutExtension(rfiles).Split("-")
docCode = fileNameParts(2).Substring(0, 3)
str.Append(String.Format("{0}|{1}|_|||{2}|{3}|{4}|swfoi{5}|{6}|{7}", _
rfiles, _
fileNameParts(0).Trim(), _
CodeInfo(docCode)(0),
CodeInfo(docCode)(1),
CodeInfo(docCode)(2),
randomnumber,
formattedDate,
Environment.NewLine))
上面的代码执行以下操作:
首先,它将当前文件名拆分为“ - ”。然后,它从分割(文档代码)中获取第三个元素的前三个字符,并将其分配给docCode
变量。
然后使用StringBuilder str
向String.Format
添加一行。请注意我如何从字典中获取文档类型信息,使用代码作为键并引用与该键关联的List(Of String)
值的正确元素:
CodeInfo(docCode)(0) returns the `docgroup` value for that code.
最后,您需要在ToString()
上致电str
以输出StringBuilder
的内容:
System.IO.File.WriteAllText(outputname, str.ToString())
要考虑的其他几点:
我会仔细检查你的正则表达式的文件名模式匹配,因为日期部分似乎有点偏。此外,您可以删除指定的组(例如?<Year>
),除非您稍后使用它们。
最后,我为XML文件选择的变量名称不是root
。在LINQ to XML中,Root
是一个返回XML根的属性 - 如果它们看起来不够密切,它可能会让其他开发人员感到困惑。
最后,如果找不到给定的密钥,或者缺少XML文件中的元素等,您将需要包含错误处理(防御性编码和/或Try-Catch块)。
我希望这有帮助 - 如果您有任何问题,请告诉我。