Обрежьте начальные пробелы, затем сделайте первую букву каждой строки заглавной и добавьте новую строку + строку + $var из другого файла, если строка начинается с определенной строки

avatar
Scott McCune
9 августа 2021 в 01:42
76
1
2

У меня есть два файла. Один представляет собой структурированные вопросы, а другой — ключ ответа.

SourceQuiz.txt образец:

1)This is the first question.
  a)  option 1
  b)  option 2
  c)  option 3
  d)  option 4
2)This is the second question
  a)  option 1
  b)  option 2
  c)  option 3
  d)  option 4
3)This is the third question.
  a)  option 1
  b)  option 2
  c)  option 3
  d)  option 4
4)This is the fourth question.
  a)  option 1
  b)  option 2
  c)  option 3
  d)  option 4 
etc etc

Образец AnswerKey.txt:

a
b
d
d

Мне нужно, чтобы файл FinalQuiz.txt был в следующем формате: https://docs.moodle.org/311/en/Aiken_format

What is the correct answer to this question?
A. Is it this one?
B. Maybe this answer?
C. Possibly this one?
D. Must be this one!
ANSWER: D

Тот факт, что исходные вопросы имеют ), а не . не имеет значения. Moodle отлично их интерпретирует.

Итак, мне нужно:

  1. Удалите число и правую скобку в начале каждого вопроса
  2. Обрезать начальные пробелы в начале вариантов каждого вопроса
  3. Используйте первую букву каждой строки с заглавной буквы
  4. затем найдите каждую строку, начинающуюся с D, и вставьте новую строку со строкой ANSWER: x, где x — это значение в строке, соответствующей вопросу в AnswerKey.txt

Я знаю, что могу:

Get-Content $SourceQuiz.txt  | Foreach {$_.TrimEnd()}

и я знаю, что могу:

Get-Content C:\Users\Administrator\Desktop\123.txt | ForEach-Object {
  if ($_) {
    $_.Substring(0, 1).ToUpper() + $_.Substring(1)
  } else {
    $_
  }
} > output.txt

И я знаю, что могу использовать if ($_.StarsWIth("D") в цикле foreach для поиска строк, начинающихся с D.

Чего я не знаю, так это вложения циклов foreach друг в друга, чтобы выполнять их все сразу или в определенном порядке, а также как добавить значение правой строки в AnswerKey.txt (в нашем примере выше Question 1 будет ANSWER: A, вопрос 2 будет ANSWER: B, вопрос 3 будет ANSWER: D, вопрос 4 будет ANSWER D.

Возможно, я смогу это сделать, если напишу несколько скриптов, но я не уверен. я немного ломал голову над этим.


Редактировать для уточнения. Вот еще раз источник:

1)This is the first question.
  a)  option 1
  b)  option 2
  c)  option 3
  d)  option 4
2)This is the second question
  a)  option 1
  b)  option 2
  c)  option 3
  d)  option 4
3)This is the third question.
  a)  option 1
  b)  option 2
  c)  option 3
  d)  option 4
4)This is the fourth question.
  a)  option 1
  b)  option 2
  c)  option 3
  d)  option 4 

Результат, который я получаю:

This is the first question.
a)  option 1
b)  option 2
c)  option 3
d)  option 4
ANSWER: D
This is the second question
a)  option 1
b)  option 2
c)  option 3
d)  option 4
ANSWER: C
This is the third question.
a)  option 1
b)  option 2
c)  option 3
d)  option 4
ANSWER: A
This is the fourth question.
a)  option 1
b)  option 2
c)  option 3
d)  option 4 
ANSWER: B

Необходимый результат:

1)This is the first question.
a)  option 1
b)  option 2
c)  option 3
d)  option 4
ANSWER: D
2)This is the second question
a)  option 1
b)  option 2
c)  option 3
d)  option 4
ANSWER: C
3)This is the third question.
a)  option 1
b)  option 2
c)  option 3
d)  option 4
ANSWER: A
4)This is the fourth question.
a)  option 1
b)  option 2
c)  option 3
d)  option 4 
ANSWER: B

Числа отсутствуют. Это индикаторы, которые ищет импортер, чтобы определить конец одного вопроса и начало следующего.

Источник

Ответы (1)

avatar
Santiago Squarzon
9 августа 2021 в 02:13
2

Это лучшее, что я мог придумать, возможно, есть гораздо лучший способ.

Приветствие mklement0 и этот замечательный ответ, где я узнал о подстановках на основе блоков скриптов :)

ПРИМЕЧАНИЕ: Весь этот код предполагает, что данные выглядят точно так, как вы указали в своем вопросе.

# In your case, this would be:
# $sourceQuiz = Get-Content path\to\SourceQuiz.txt

$sourceQuiz = @'
1)This is the first question.
  a)  option 1
  b)  option 2
  c)  option 3
  d)  option 4
2)This is the second question
  a)  option 1
  b)  option 2
  c)  option 3
  d)  option 4
3)This is the third question.
  a)  option 1
  b)  option 2
  c)  option 3
  d)  option 4
4)This is the fourth question.
  a)  option 1
  b)  option 2
  c)  option 3
  d)  option 4 
'@ -split '\r?\n'

# And this would be:
# $answers = Get-Content path\to\answers.txt

$answers = @'
a
b
d
d
'@ -split '\r?\n'

$answerIndex = 0
$loopIndex = 1
$txtInfo = (Get-Culture).TextInfo

$sourceQuiz -split '^\d+\)' -ne '' | ForEach-Object {

    [regex]::Replace($_, '\s+(\w+)\)\s+', {
        param($s)
        
        $txtInfo.ToTitleCase($s.Groups[1].Value) + '. '
    })
    
    if($loopIndex % 5 -eq 0)
    {
        'ANSWER: {0}' -f $txtInfo.ToTitleCase($answers[$answerIndex])
        ''
        $answerIndex++
    }
    
    $loopIndex++
}

Вышеуказанное приводит к:

This is the first question.
A. option 1
B. option 2
C. option 3
D. option 4
ANSWER: A

This is the second question
A. option 1
B. option 2
C. option 3
D. option 4
ANSWER: B

This is the third question.
A. option 1
B. option 2
C. option 3
D. option 4
ANSWER: D

This is the fourth question.
A. option 1
B. option 2
C. option 3
D. option 4 
ANSWER: D

РЕДАКТИРОВАТЬ

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

Обратите внимание на использование -Raw в Get-Content.

# In your case, this would be:
# $sourceQuiz = Get-Content path\to\SourceQuiz.txt -Raw # => -Raw is important here

$sourceQuiz = @'
1)This is the first question.
  a)  option 1
  b)  option 2
  c)  option 3
  d)  option 4
2)This is the second question
  a)  option 1
  b)  option 2
  c)  option 3
  d)  option 4
  e)  option 5
  f)  option 6
3)This is the third question.
  a)  option 1
  b)  option 2
'@

# And this would be:
# $answers = Get-Content path\to\answers.txt # => Here don't use -Raw

$answers = @'
a
b
d
'@ -split '\r?\n'

$answerIndex = 0
$txtInfo = (Get-Culture).TextInfo

$sourceQuiz -split '\d+\)' -ne '' | ForEach-Object {
    
    $lines = $_ -split '\r?\n' -ne ''

    for($i=0;$i -lt $lines.Count;$i++)
    {
        switch($i)
        {
            {$i -eq 0}
            {
                $lines[$i]
                break
            }
            {$i -gt 0}
            {
                [regex]::Replace($lines[$i],'\s+(\w+)\)\s+',{
                    param($s)
        
                    $txtInfo.ToTitleCase($s.Groups[1].Value)+'. '
                })
            }
            {$i -eq $lines.Count-1}
            {
                'ANSWER: {0}' -f $txtInfo.ToTitleCase($answers[$answerIndex])
                ''
            }
        }
    }

    $answerIndex++
}

Теперь это будет выглядеть так:

This is the first question.
A. option 1
B. option 2
C. option 3
D. option 4
ANSWER: A

This is the second question
A. option 1
B. option 2
C. option 3
D. option 4
E. option 5
F. option 6
ANSWER: B

This is the third question.
A. option 1
B. option 2
ANSWER: D

  • Почему -Raw нужен для второго фрагмента кода?

Поскольку при чтении файла, если вы используете -Raw, содержимое будет одним multiline string вместо массива строк string[].

  • Почему требуется один multiline string?

Поскольку во втором фрагменте кода предполагается, что количество возможных вариантов не всегда равно 4, нам нужно найти способ определить, сколько вариантов существует.

Для этого он берет содержимое файла как один string и разделяет его на любое количество цифр, за которым следует ) (т. разделяет каждый вопрос и соответствующие ему варианты.

Как только у нас есть вопросы и их параметры, разделенные на разные массивы/фрагменты, мы можем снова разделить каждый фрагмент на carriage returns или new lines, и здесь мы можем предположить, что position 0 массива всегда будет вопросом :

{$i -eq 0}
{
    $lines[$i]
    break
}

Позиции greater than 0 массива будут вариантами, здесь мы trim начальные пробелы и заменяем символ, за которым следует ), на тот же символ, за которым следует . (т.е.: a) для A.).

{$i -gt 0}
{
    [regex]::Replace($lines[$i],'\s+(\w+)\)\s+',{
        param($s)

        $txtInfo.ToTitleCase($s.Groups[1].Value)+'. '
    })
}

Наконец, чтобы определить, когда мы достигли конца доступных опций и знать, когда вставить ANSWER:, поскольку я использую цикл for и переменную $i как index, на каждой итерации цикла switch спрашивает, равно ли $i $lines.Count - 1 (если $i достиг конца массива или последнего элемента массива).

{$i -eq $lines.Count-1}
{
    'ANSWER: {0}' -f $txtInfo.ToTitleCase($answers[$answerIndex])
    ''
}
Daniel
9 августа 2021 в 02:59
1

Мне нравится, если это имеет значение +1

Santiago Squarzon
9 августа 2021 в 03:17
1

@ Даниэль Конечно, это важно для меня. Спасибо Даниэль.

Scott McCune
9 августа 2021 в 06:50
0

всегда есть только 4 ответа. однако количество вопросов (и соответственно ответов) варьируется

Santiago Squarzon
9 августа 2021 в 12:49
0

Первый фрагмент кода должен работать нормально, тогда @ScottMcCune

Scott McCune
13 августа 2021 в 20:21
0

Отлично работает, спасибо @SantiagoSquarzon. Я думаю, что на самом деле я собираюсь использовать второй, на тот случай, если мы столкнемся с аномалией или в будущем будет принято решение, которое изменит масштаб. Можете ли вы объяснить причину -RAW на Get-Content? Почему это важно? Я хочу учиться здесь, а не просто делать работу. Есть ли причина, по которой вы закомментировали (в обоих фрагментах кода) путь\к строкам Get-Content? Предполагая, что я просто удалю #. В конце концов, я просто перехожу к файлу после финального }? то есть '| Вне файла FinalQuiz.txt?

Santiago Squarzon
13 августа 2021 в 21:46
0

@ScottMcCune смотрите мое последнее редактирование, я сделал все возможное, чтобы объяснить логику, но сам код немного сложен для понимания, если вы только начинаете работать со сценариями или с самим powershell. Пожалуйста, если ответ был полезен, рассмотрите принятие его.

Scott McCune
14 августа 2021 в 03:34
0

Ценю это @santiago, однако есть одна проблема со сценарием. Как я уже говорил, он обрезает первые два пробела КАЖДОЙ строки, а не только два начальных/пустых пробела перед каждым вариантом вопроса. Он должен обрезать только пробелы в строках, а не в числах и) в вопросах. Тот факт, что цифры отсутствуют, нарушает импорт

Scott McCune
15 августа 2021 в 16:56
0

@SantiagoSquarzon почему-то вчера не смог отметить тебя с телефона

Santiago Squarzon
15 августа 2021 в 19:07
0

@ScottMcCune, я не уверен, что вы с последним комментарием, вывод моего кода выглядит почти так же, как ожидаемый вывод из вашего ответа. Если вы хотите отредактировать свой вопрос, показав, что вы получаете в результате, и каким будет ожидаемый результат.

Scott McCune
16 августа 2021 в 16:29
0

@SantiagoSquarzon отредактировано в соответствии с просьбой. Число и круглые скобки отсутствуют в начале каждого вопроса в выходных данных. Я не хочу удалять первые 2 символа из каждой строки, я хочу только удалить пустые пробелы в начале каждой строки.

Santiago Squarzon
16 августа 2021 в 17:00
0

@ScottMcCune Если вы хотите этого, вам просто нужно изменить $sourceQuiz -split '\d+\)' -ne '' на $sourceQuiz -split '(?=\d+\))' -ne '', однако параметры отображаются также с символом, за которым следует ), когда вы заявили, что хотите использовать символ с заглавной буквы и изменить ) для .

Scott McCune
18 августа 2021 в 01:03
0

Да, я извиняюсь @SantiagoSquarzon, инструкции по импортеру были неясными, и я сделал предположение, основанное на его примере. Первый критерий, который я перечислил Strip the number and right parenthesis from the beginning of each question, был неверным. На самом деле мне нужно сохранить число и круглые скобки в первой строке, но мне нужно удалить начальные пробелы в других строках.

Scott McCune
18 августа 2021 в 03:40
1

@SantiagoSquarzon Я смог сделать это легко, добавив '(?=\d))', как вы указали выше, но отслеживая ошибку немного дальше, я обнаружил, что мне также нужно добавить пробел после каждого числа и скобки. Итак, я провел некоторое исследование и обнаружил, что ?= — это просмотр вперед, а \d определяет, что мы ищем цифру, а не слово или любой другой символ, и я смог вставить пробел перед это, но я не могу понять, как вставить пробел после ) перед первым символом следующего слова.

Scott McCune
18 августа 2021 в 04:21
1

@SantiagoSquarzon, неважно. Я понял. это было не то. импортер выдал ошибку, потому что исходящий файл сохранялся как UTF-16 LE, а импортеру требуется UTF-8. Я добавил -Encoding "UTF8" в командлет Out-File, и он отлично работает. извините, что такая ворчливая, но большое спасибо. Я узнал совсем немного.