1

下午好,

我是 VB.net 的新手,正在为我的 VB.Net windows 窗体项目使用 Visual Studio Express 2012。

我需要一些关于我的代码的帮助,因为我不确定如何做我想做的事情。

场景是这样的:

用户在表单上选择一个目录并按下一个按钮。然后应用程序将查找一些文件并将它们移动到另一个目录。在该新目录中,它将找到包含三个字符代码的文件名。

然后,应用程序将为 xml 文件中的每个代码分配适当的 docgroup、doctype 和 dosubtype 值。然后将其输出到文本文件。

让应用程序知道要根据文件文件名使用的 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

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

谁能帮我完成这段代码?

亲切的问候,

4

1 回答 1

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 中为给定元素混合文本和子元素(至少我不知道)。

接下来,我将解析 XML 一次,并使用 Dictionary 来保存数据,其中 code 作为键,aList<string>保存 docgroup、doctype 和 dosubtype 值。

此外,我不会使用正则表达式在 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 = dosubtype)。

您可以从字典中获取正确的 docgroup、doctype 和 dosubtype。

把它们放在一起你会得到这样的东西:

首先,替换这些代码行:

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 文件,将其解析为字典(以 code 作为键,List(Of String)将 doc 类型 info 的 a 作为值。然后,通过String.Join调用Keys字典的属性。

现在,当您将信息写入文本文件时,您可以根据代码获取正确的值。这里也有一些变化。

首先,我会在为文件-创建行之前拆分文件名,因为您需要它来获取正确的代码。假设您的所有代码都是三个字符,那么您只需要拆分数组中第三个元素的前三个字符。您将该值作为键传递,以获取代码的正确文档类型值。

其次,我建议使用StringBuilder(您可能需要添加Imports System.Text到您的程序中)。这样做的原因是字符串连接可能会变得昂贵(想象一下,如果您有 1,000 个文件要处理)。字符串是不可变的——这意味着它们不能被改变。所以当你有类似的东西时:

Dim str1 As String = "Hello"

str1 = str1 & " World"

您可能认为 str1 中添加了“World”。实际上,创建了一个新字符串,其中 str1 添加了“World” - 所以想象一下如果你这样做 1,000 次会发生什么。为了以下示例的可读性,我还使用了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 根的属性 - 如果其他开发人员没有仔细查看代码,可能会使他们感到困惑。

最后,您需要包含错误处理(防御性编码和/或 Try-Catch 块),以防找不到给定的键,或者 XML 文件中的元素丢失等。

我希望这会有所帮助 - 如果您有任何问题,请告诉我。

于 2013-06-20T06:43:12.850 回答