【问题标题】:VB.NET "Value cannot be Null Parameter Name: Source" when using a Boolean value of False使用布尔值 False 时,VB.NET“值不能为空参数名称:源”
【发布时间】:2021-07-19 21:17:41
【问题描述】:

我开始在 Visual Studio 2019 中使用 VB.NET 进行编码。我遇到了一个我不明白的奇怪问题。 我的程序很简单,目前打算运行一个非常大的 .txt 文件(近 300 万行),找到某个字符串值(这些值由“,”分隔 - 它是逗号分隔值)并让我知道是否已找到字符串值,如果已找到,则为行号。如果没有,我只想放置一个标签来显示“找不到文件”。该程序有效,如果我键入相应的值或“找不到文件”,我确实会得到“在(行号)上找到的文件”,但是在程序完成并运行之后,我得到一个参数空异常,说“值不能为 Null,参数名称:Source”。我不确定它反对什么。可能是因为如果找不到字符串值,我创建了一个布尔值“False”,但我搜索了整个 Microsoft 文档,但找不到任何暗示不允许这样做的内容。

这是我的代码:

Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
    Dim File_name As String
    File_name = "C:\New Folder\Programming\Residual Stressn.txt"

    Dim entr As String
    entr = TextBox1.Text

    Dim Txter As New FileIO.TextFieldParser(File_name)

    Txter.TextFieldType = FileIO.FieldType.Delimited
    Txter.SetDelimiters(",")


    While Not Txter.EndOfData
        Txter.ReadFields()
        If Txter.ReadFields.Contains(entr) = True Then Label1.Text = "File Found on line " & Txter.LineNumber
        If Txter.ReadFields.Contains(entr) = False Then Label2.Text = "File Not found"
    End While

    MsgBox("Enter next command")

End Sub

正如我所说,程序运行,两个标签都有效。它只是在完成后启动了 boobsy-die。 该参数,如果它引用“entr”,则不为空。在运行时,我总是在文本框中输入一个字符串值。在我添加 label2 和“false”值之前,它并没有让我生气。但如果允许我输入一个布尔值,我可以使用 readfields.contains 输入,那么我应该可以输入“false”吗?谁能帮帮我?

【问题讨论】:

  • try...catch 块包围代码以获取有关异常的更多详细信息。您也可以在这里找到示例代码 - docs.microsoft.com/en-us/dotnet/visual-basic/developing-apps/…
  • 您的 While 循环在找到某些内容后永远不会结束 - 即使在找到某些内容后它也会继续并不断更新 Label1 或 Label2 直到文件结束。

标签: vb.net


【解决方案1】:

这很糟糕:

While Not Txter.EndOfData
    Txter.ReadFields()
    If Txter.ReadFields.Contains(entr) = True Then Label1.Text = "File Found on line " & Txter.LineNumber
    If Txter.ReadFields.Contains(entr) = False Then Label2.Text = "File Not found"
End While

你每次迭代都读三行,你经常会得到一个不正确的结果。

每次调用ReadFields 时,您都在读取文件的一行。您在该循环中对ReadFields 进行了三次调用,因此您将在每次迭代中读取文件的三行。第一行读取文件的一行并将其丢弃。第二行读取文件的另一行,并且仅当它包含指定的文本时才执行某些操作。第三行读取文件的另一行,并且仅当该行不包含指定文本时才执行某些操作。这只是偶然的,不会在所有情况下都有效。该代码应如下所示:

Dim lineNumber As Integer? = Nothing

While Not Txter.EndOfData
    If Txter.ReadFields.Contains(entr) Then
        lineNumber = Txter.LineNumber
        Exit While
    End If
End While

If lineNumber.HasValue Then
    Label1.Text = "File Found on line " & lineNumber
Else
    Label2.Text = "File Not found"
End If

一次只读取并检查文件的一行。如果该行包含指定的文本,则搜索结束。如果搜索到了文件的末尾而没有找到指定的文本,那么它才会报告它没有找到。我一直认为您使用了两个不同的Labels 并且文本大小写错误。

为了说明现有代码有多糟糕,假设您有一个这样的文件:

X 是的 Z X 是的 Z

您要查找的值是“Z”。在循环的第一次迭代中,您的代码将首先读取“X”并将其丢弃,然后读取“Y”,Contains 将返回 False,因此它不会记录找到的值,然后读取“Z”而Contains 将返回True,因此该值也不会被记录为未找到。第二次迭代也会发生同样的事情。在循环的两次迭代中,您将读取文件的六行,尽管其中两行存在该值,但您不会将该值记录为找到或未找到。就像我说的,当那个代码起作用时,这完全是靠运气。

【讨论】:

  • 我在 (integer).HasValue 中遇到语法错误。这似乎不是一个公认的命令。可能是因为我使用的 Visual Studio 版本与您不同?无论如何,感谢您的反馈,非常有帮助。
  • 整数没有“HasValue”。整数? = Nullable 整数具有此属性。检查 '?'在类型的末尾
  • @Mimsy32,正如建议的那样,lineNumber 不是类型 Integer。类型为Nullable(Of Integer),缩写为Integer?。可空值类型是在 .NET 2.0 和 VB 2005 中引入的,我相信在 VB 2008 或 VB 2010 中引入了速记。您使用的 VS 版本与我完全相同。
  • 好的,我所做的是编写代码:(整数)=>0。效果很好。
  • @Mimsy32,这肯定会起作用,但它并不正确。如果可能的话,应该避免使用这样的幻数。在可空值类型之前,最初在 VB 中是不可能的,这就是为什么您会发现在这种情况下经常使用 -1。如果您要使用Integer,那么我建议您也使用-1,但如果您使用Integer? 会更好,这就是我这样做的原因。
【解决方案2】:

我个人从未使用过(甚至听说过)TextFieldParser 类。一些谷歌搜索显示了一些性能问题。但是,我会使用 String-/StreamReader(或者这里使用 System.IO.File.ReadAllLines ...)。 通常,在您的程序中,您在 TextBox 中输入文件名并查找文件名是否存在,这意味着,每次您一次又一次地解析整个文件?将文件读入列表并在列表上进行查找不是一种相当不错的方法吗?

我假设你使用 WinForms 作为 GUI,所以你可以在 Form.vb 中放置一个私有变量

Dim contents as List(Of String)
Dim File_name = "C:\New Folder\Programming\Residual Stressn.txt" 'Since this seems to be static, you can declare it on class level

第一次点击读取文件

Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click

  If contents Is Nothing Then
    contents = System.IO.File.ReadAllLines (File_Name).ToList() ' Add: Imports System.Linq
  End If

  Dim entr = TextBox1.Text 'TextBox.Text is always string, don't need to declare the type

  'Since you only care about the Filename contained in the line, and don't need the structured field contents
  Dim index = contents.FindIndex(0, contents.Count -1, Function (str as String) str.Contains(entr))
  If  index <> -1 Then
    Label1.Text = "File Found on line " & index
  Else
    Label2.Text = "File Not found"
  End If
End Sub

好久没用VB了,希望能编译,不过思路应该很清楚

【讨论】:

  • 您提出的解决方案存在一些问题。问题中的代码仅匹配您匹配部分值甚至组合值的完整字段值。如果一行是“ABC,123,XYZ”并且感兴趣的值是“2”,那么您的代码将记录匹配,而原始代码不会。这是一个 CSV 文件,正在对字段值进行匹配,但您不允许这样做。
  • 另外,TextFieldParser 的一个优点是它专门设计用于读取分隔文件,因此它可以处理可能包含或不包含字段分隔符和/或记录分隔符的引用文本。在这种情况下,这可能不是问题,但正确的 CSV 文件可能有一个用引号括起来并包含换行符的字段。您的代码会将其视为两个单独的记录。如果您要在逗号上分割一行,那么您必须允许一个可以用引号括起来并包含逗号的字段。这可能就是为什么 TextFieldParser 可能会更慢但它做的事情正确。
  • 您的评论是正确的,具体取决于 csv(未提供信息)结构和它可能适合的查找样式(即使使用拆分扩展,或引入行数据的类型,等)
【解决方案3】:

如下修改你的循环:

Dim Found As String = Nothing
While Not Txter.EndOfData AndAlso Found Is Nothing
    Found = Txter.ReadFields()
    If Found.Contains(entr) Then Label1.Text = "File Found on line " & Txter.LineNumber Else Found = Nothing
End While
If Found Is Nothing Then Label2.Text = "File Not found"

一旦找到东西就会停止搜索。

【讨论】:

  • 该代码在我看来不正确。是否应该否定循环条件中的第二个条件?
  • 我不这么认为。只要Found 为空,循环就应该继续。
  • 是的,但是ReadFields 的结果在第一次迭代时被分配给Found,而不是Nothing,因此循环将在第二次迭代之前退出。你熟悉TextFieldParser 类和ReadFields 方法吗?也许不是。它将返回一个 String 数组,其中包含 CSV 文件中一行文本中的字段值。只要有数据要读取,就不会是Nothing
【解决方案4】:

正如其他人所说,您的原始代码在循环的每个循环中读取三行

我假设您希望在找到搜索字符串的第一个匹配项后立即停止搜索,所以它可能看起来像这样..

它与其他答案相似,这是我的看法。它看起来很干净,但总是赞赏建设性的批评

    Dim stringFound As Boolean = False
    While Not Txter.EndOfData And Not stringFound
        Dim fields As String() = Txter.ReadFields()
        If fields.Contains(entr) = True Then
            Label1.Text = "File Found on line " & Txter.LineNumber
            stringFound = True
        End If
    End While
    If Not stringFound Then Label2.Text = "File Not found"

【讨论】:

    猜你喜欢
    • 2013-04-23
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-06-12
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多