Проверка данных VBScript - числовое значение 1 приводит к бесконечному циклу - PullRequest
1 голос
/ 24 февраля 2011

ОТКАЗ ОТ ОТВЕТСТВЕННОСТИ: Я все еще uber-n00b с концепциями программирования и знаю достаточно VBS, чтобы навредить себе, поэтому я, скорее всего, оскорблю / убью некоторые термины / концепции / идеи, которые связаны с этой программой, которую я Я пытаюсь написать. Вы, превосходящий программист, который имеет полное право поджигать меня, были предупреждены.

Я пытался написать VBScript для проверки данных, особенно дат. Поскольку мои пользователи плохо разбираются в клавиатурах, я решил, что упросту задачу, разделив ввод каждой части даты (только месяц и день, у меня год установлен статически).

Раньше у меня были проблемы с проверкой чисел только с 1 «Do ... Loop», так как я пытался проверить, был ли это числовой ввод, и одновременно проверять, находился ли он в указанном диапазоне (1 - 12, за 12 месяцев года).

Вот как примерно выглядел предыдущий код:

Do
    ' If...Then Statements Here
Loop Until (dtFirstMonth > 0) _
    And (dtFirstMonth < 13) _
    And IsNumeric(dtFirstMonth) _
    And (dtFirstMonth <> "") _
    And (dtFirstMonth <> vbNull)

Это часто приводило к ошибкам «Несоответствие типов данных», поэтому мне пришлось разделить критерий проверки на два отдельных оператора «Do ... Loop», как вы можете видеть в текущем коде, который я имею ниже:

Sub srTest()
    Do
        dtFirstMonth = InputBox("Please Enter a Numeric Month for the Starting Range", _
            "Starting Range Month")

        If (dtFirstMonth = vbNull) _
            Or (dtFirstMonth = "") _
            Or Not IsNumeric(dtFirstMonth) Then
                MsgBox "Please Enter a Valid Numeric Month",, "Enter Month Number"
        ElseIf (dtFirstMonth <> vbNull) _
            And (dtFirstMonth <> "") _
            And IsNumeric(dtFirstMonth) Then
                Do
                    dtFirstMonth = Round(dtFirstMonth)
                    Wscript.Echo dtFirstMonth ' Infinite Loop Here (Basically, As Soon As We Get Into Loop with a Value of 1, We're Stuck)
                    dtFirstMonth = CInt(dtFirstMonth)
                        ' Must Convert User Input to Integer to 
                        '   Prevent Data Mismatch Errors In 
                        '   Following "If" Statement; Besides, 
                        '   It Passed the First Test to be a
                        '   Numeric Value in the First Place

                    If (dtFirstMonth < 1) Or (dtFirstMonth > 12) Then
                        MsgBox "Please Enter a Valid Numeric Month",, "Enter Month Number"
                        Exit Do
                            ' Drop Out of 2nd Level Loop to 
                            '   Enter Into 1st Level Loop
                    End If
                Loop Until (dtFirstMonth > 0) _
                    And (dtFirstMonth < 13) _
                    And IsNumeric(dtFirstMonth) _
                    And (dtFirstMonth <> "") _
                    And (dtFirstMonth <> vbNull)

                If (dtFirstMonth < 1) Or (dtFirstMonth > 12) Then
                    dtFirstMonth = ""
                End If
                    ' dtFirstMonth Was Converted to Integer Earlier
                    ' This is to Meet the Value that Didn't Pass 
                    '   the Nested Do & If Statement (Level 2 Do Loop)
                    ' Sets dtFirstMonth to "Empty String" to Continue 
                    '   Looping in the Level 1 "Do...Loop" Statement; 
                    '   If Omitted, Level 1 "Do...Loop" is Satisfied, 
                    '   Thus Ending the Subroutine (Since the Value 
                    '   of dtFirstMonth is Still a Numeric Value)
        End If
Loop Until IsNumeric(dtFirstMonth) _
    And (dtFirstMonth <> "") _
    And (dtFirstMonth <> vbNull)

    Wscript.Echo dtFirstMonth
End Sub

srTest

Мне пришлось настроить 1-й цикл "Do ... Loop", чтобы проверить, что пользовательский ввод (dtFirstMonth) действительно является числовым значением, а не нулевым значением или пустой строкой. Вложенный оператор «Do ... Loop» или 2-й оператор «Do ... Loop» - это то, где у меня есть те же критерии плюс дополнительные критерии, определяющие желаемые диапазоны (любое число от 1 до 12).

Это прекрасно работает для номера 2-12, но когда скрипт анализирует число 1, я вхожу в бесконечный цикл.

Я проверил, чтобы убедиться, что бесконечный цикл происходит во 2-м цикле «Do ... Loop», заменив весь 2-й раздел «Do ... Loop» на «Wscript.Echo dtFirstMonth». Делая это, я получаю ожидаемые результаты: один Echo, а не бесконечное их количество (технически я получаю 2, так как у меня есть другая строка "Wscript.Echo dtFirstMonth" в нижней части подпрограммы для отладки). , но в любом случае, это не бесконечный цикл).

Я также изменил критерий для нижнего диапазона, чтобы он был таким, но это не исправляет ошибку:

Do
    ' If...Then Statements Here
Loop Until (dtFirstMonth >= 1)

Я тоже пробовал это, но безуспешно:

Do
    ' If...Then Statements Here
Loop Until (dtFirstMonth >= CInt(1))

В действительности, этот сегмент действительно не нужен, так как я все равно преобразовал входные данные пользователя в целое число.

Поскольку это начало сбивать с толку, я решил добавить инструкцию «Round» до того, как скрипт передаст пользовательский ввод в функцию «CInt», надеясь, что он не будет пойман как 0 значение или десятичное значение как-то; да, это иррациональная мысль с моей стороны, но я все еще хотел изучить все возможности (есть также тот факт, что у меня есть некоторые пользователи с синдромом "Fat Finger Syndrome" и некоторые другие с менталитетом "злоупотреблять программой", поэтому я решил, что ' убедитесь, что в скрипте учтены десятичные записи). Я добавил строку «Round» до и после вложенного цикла «Do ... Loop», и у меня все еще была проблема с Infinite Loop.

Насколько я смог это сделать, и теперь я застрял.

Я понимаю, что, вероятно, есть более эффективные способы проверки даты / времени в VBScript, и я, безусловно, открыт для любых новых предложений, но я бы хотел решить это просто ради назидания.

Ответы [ 3 ]

2 голосов
/ 28 февраля 2011

Проблема с вашей попыткой проверить нулевые значения:

(dtFirstMonth = vbNull)

Правильный способ проверки на нулевые значения, как показано в ответе AutomatedChaos, заключается в использовании функции IsNull. vbNull на самом деле является константой, которая используется с функцией VarType. Значение vbNull равно 1, поэтому это конкретное значение ведет себя не так, как другие записи. Это фундаментальная проблема, и если вы замените каждый dtFirstMonth = vbNull на IsNull(dtFirstMonth), вы не получите бесконечный цикл при вводе 1.

Теперь фактическое место, где ваш код бесконечно зацикливается, интересно. Я ожидаю, что первое условное значение If (dtFirstMonth = vbNull) будет иметь значение true для записи «1», и вы получите сообщение «Пожалуйста, введите действительный числовой месяц». Однако условие Else срабатывает. Это странно, потому что обычно, когда вы сравниваете строку с числом, VBScript пытается преобразовать число в строку или наоборот, поэтому, если dtFirstMonth равно «1», оно должно быть равно vbNull (что 1). Однако при сравнении строки переменная с целым числом переменная возникает особый случай. Смотрите этот пример:

' vbNull = 1, built-in constant
dtFirstMonth = "1"
MsgBox (dtFirstMonth = vbNull) ' False
MsgBox ("1" = vbNull) ' True
MsgBox (dtFirstMonth = 1) ' True
MsgBox (CInt(dtFirstMonth) = vbNull) ' True

Я не знаю, является ли это ошибкой или просто неясной деталью о неявном преобразовании VBScript, но это иллюстрирует, что неявное преобразование может быть непредсказуемым в VBScript.

Что касается альтернативных методов, вас может заинтересовать функция IsDate, которая возвращает True, если данное выражение можно преобразовать в дату. Возможно, он не идеален для ваших пользователей и их навыков работы на клавиатуре, но уменьшит ваш код до:

Do
    str = InputBox("Enter a date (MM/DD)")
    If IsDate(str) Then
        Exit Do
    Else
        WScript.Echo "Please enter a valid date"
    End If
Loop
dt = CDate(str)
' Put your static year here; CDate will default to the current year
dtActual = DateSerial(2007, Month(dt), Day(dt))
WScript.Echo (dtActual) & " - Thanks!"

Обратите внимание, что IsDate должен возвращать False для типичных крайних случаев ("", Null, Empty и т. Д.), Поэтому нет необходимости в отдельной проверке.

2 голосов
/ 10 июля 2011

Я нашел простую программу для генерации даты и времени из http://rindovincent.blogspot.com/p/vbscript-programs.html. Я вставляю ту же программу с разрешения.

2 голосов
/ 24 февраля 2011

Слишком много кода для простого ввода числа. Просто постарайтесь сделать это коротким и простым. Пример:

Do
    dtm = InputBox("Please Enter a Numeric Month for the Starting Range", _
                "Starting Range Month")
    Select Case True
        Case isNull(dtm), (not isNumeric(dtm)), dtm = "", dtm = empty, (dtm < 1 OR dtm > 12)
            ' too exhaustive, but just for the sake of the example.
             MsgBox "Please enter an amount between 1 and 12"
        Case else
            ' Hey, this seems to be a valid amount!
            Exit do
    End Select
Loop While True

'  Do something with dtm

Только что показал вам несколько креативных Select Casing, он поддерживает ленивый выход, поэтому, если значение равно Null, оно сбрасывается до оценки, где оценка может выдать ошибку.

...