#arrays #c #letter #toupper
Вопрос:
Таким образом, цель этой программы состоит в том, чтобы в основном взять 26-буквенный «ключ» в терминале (сквозной argv[]
) и использовать его индекс в качестве ориентира для замены. Таким образом, в терминале есть 2 входа, которые вы вводите, один в argv[]
, и один-это просто обычный get_string()
вход. argv[]
Ввод будет выглядеть следующим образом: ./s YTNSHKVEFXRBAUQZCLWDMIPGJO
где s
находится имя файла. И тогда get_string()
входные данные будут выглядеть так: plaintext: HELLO
. (Входные данные есть HELLO
). Затем программа выполнит перебор всех букв в вводе открытого текста и заменит его алфавитный индекс в соответствии с индексом argv[]
ключа. Например, H
имеет алфавитный указатель 7
(где a
= 0 и z
= 25), поэтому мы смотрим на 7-й индекс в ключе YTNSHKV(E)FXRBAUQZCLWDMIPGJO
, который в данном случае является E
. Он делает это для каждой буквы на входе, и в итоге мы получим результат ciphertext: EHBBQ
. Вот как это должно выглядеть в терминале:
./s YTNSHKVEFXRBAUQZCLWDMIPGJO
plaintext: HELLO
ciphertext: EHBBQ
Но мой вывод таков EHBB
, так как он по какой-то причине отсекает последнюю букву, когда я использую toupper()
.
Кроме того, прописные и строчные буквы зависят от ввода открытого текста, если бы ввод открытого текста был hello, world
, а argv[]
ключ был YTNSHKVEFXRBAUQZCLWDMIPGJO
, вывод был бы jrssb, ybwsp
, и если бы ввод был HellO, world
с тем же ключом, вывод был бы JrssB, ybwsp
.
Я в основном покончил с проблемой, моя программа заменяет открытый текст, указанный в правильном зашифрованном тексте, на основе ключа, который был введен через командную строку. Прямо сейчас, скажем , если ввод открытого текста был HELLO
, и ключ был vchprzgjntlskfbdqwaxeuymoi
(все в нижнем регистре), то он должен вернуться HELLO
, а не hello
. Это связано с тем, что моя программа помещает все буквы в ключе командной строки в массив длиной 26, и я перебираю все буквы открытого текста и сопоставляю значение ascii (минус определенное число, чтобы перевести его в диапазон индексов 0-25) с индексом в ключе. У So E
есть алфавитный индекс 4 , так что в этом случае моя программа будет строчной p
, но мне это нужно P
, поэтому я использую toupper()
.
Когда я использую tolower()
, все работало нормально, и как только я начал использовать toupper()
, последняя буква ciphertext
по какой-то причине обрезается. Вот мои выходные данные перед использованием toupper()
:
ciphertext: EHBBQ
И вот мой вывод после того, как я использую toupper()
:
ciphertext: EHBB
Вот мой код:
int main(int argc, string argv[]) {
string plaintext = get_string("plaintext: ");
// Putting all the argv letters into an array called key
char key[26]; // change 4 to 26
for (int i = 0; i < 26; i ) // change 4 to 26
{
key[i] = argv[1][i];
}
// Assigning array called ciphertext, the length of the inputted text, to hold cipertext chars
char ciphertext[strlen(plaintext)];
// Looping through the inputted text, checking for upper and lower case letters
for (int i = 0; i < strlen(plaintext); i )
{
// The letter is lower case
if (islower(plaintext[i]) != 0)
{
int asciiVal = plaintext[i] - 97; // Converting from ascii to decimal value and getting it into alphabetical index (0-25)
char l = tolower(key[asciiVal]); // tolower() works properly
//printf("%c", l);
strncat(ciphertext, amp;l, 1); // Using strncat() to append the converted plaintext char to ciphertext
}
// The letter is uppercase
else if (isupper(plaintext[i]) != 0)
{
int asciiVal = plaintext[i] - 65; // Converting from ascii to decimal value and getting it into alphabetical index (0-25)
char u = toupper(key[asciiVal]); // For some reason having this cuts off the last letter
strncat(ciphertext, amp;u, 1); // Using strncat() to append the converted plaintext char to ciphertext
}
// If its a space, comma, apostrophe, etc...
else
{
strncat(ciphertext, amp;plaintext[i], 1);
}
}
// prints out ciphertext output
printf("ciphertext: ");
for (int i = 0; i < strlen(plaintext); i )
{
printf("%c", ciphertext[i]);
}
printf("n");
printf("%cn", ciphertext[1]);
printf("%cn", ciphertext[4]);
//printf("%sn", ciphertext);
return 0;
}
Комментарии:
1. Не уверен, повторяю ли я это, но «зашифрованный текст[strlen(открытый текст)];» выглядит подозрительно для меня. Я бы предпочел видеть «зашифрованный текст char[strlen(открытый текст) 1];», с 1 для размещения , который завершает строку.
2. По крайней мере:
char ciphertext[strlen(plaintext)]
->char ciphertext[strlen(plaintext) 1]
3. Не пиши
65
. Вместо этого напишите'A'
4.
ciphertext
является неинициализированным, какstrncat
и неопределенное поведение. Вам нужен нулевой терминатор, которого нет, когда он неинициализирован.5. @chqrlie, да, я использую <cs50.h>. Спасибо вам за вашу помощь, ваши предложения по улучшению действительно помогли.
Ответ №1:
strncat
Функция ожидает, что ее первым аргументом будет строка с нулевым завершением, к которой она добавляется. Вы вызываете его, ciphertext
пока он неинициализирован. Это означает, что вы читаете неинициализированную память, возможно, читаете дальше конца массива, вызывая неопределенное поведение.
Вам нужно создать ciphertext
пустую строку, прежде чем вызывать strncat
ее. Кроме того, вам необходимо добавить 1 к размеру этого массива, чтобы учесть завершающий нулевой байт в завершенной строке, чтобы предотвратить списание ее конца.
char ciphertext[strlen(plaintext) 1];
ciphertext[0] = 0;
Комментарии:
1. Дополнительный совет:
plaintext
необходимо быть свободным в конце программы. Это причина#define
, по которой использование типов указателей не является хорошей идеей: оно затемняет указатель.2. @Юн: Боюсь, вы ошибаетесь.
get_string()
действительно выделяет память, но пользователь не несет ответственности за освобождение этих выделенных объектов, поскольку библиотека регистрирует функцию atexit для выполнения очистки. cf cs50.readthedocs.io/libraries/cs50/c/#c.get_string3. @Yun: эта библиотека действительно проблематична во многих отношениях. Он подходит только для начинающих, которым будет полезно не изучать тайные ловушки и подводные
scanf()
камни и другие функции ввода, но которые могут приобрести контрпродуктивные привычки. Самое главное, что они не узнают об указателях, что является самой сложной концепцией для усвоения в C.4. @chqrlie, может быть, ты и прав! Я думал
get_string
, что это определяемая пользователем функция, которая просто не была дана для краткости. Опять же, ничто в вопросе не относится к этой сторонней библиотеке, насколько я могу судить.5. @Yun: операция только что подтвердила использование
<cs50.h>
. Эта библиотека довольно популярна, и хотя она дает новичкам преимущество в написании небольших программ, решающих конкретные проблемы, некоторые из их идей кажутся контрпродуктивными, например, скрыватьсяchar *
за typedef (string
).
Ответ №2:
В коде есть несколько проблем:
- вы не проверяете наличие и длину аргумента командной строки
- массив должен быть выделен с 1 дополнительным байтом для нулевого терминатора и инициализирован как пустая строка для
strncat()
правильной работы. - вместо жесткого кодирования ASCII-значений , таких как
97
и65
, используйте символьные константы, такие как'a'
и'A'
strncat()
это перебор для вашей цели. Вы могли бы просто написатьciphertext[i] = l;
вместоstrncat(ciphertext, amp;l, 1)
islower()
иisupper()
определяются только для положительных значений типаunsigned char
и специального отрицательного значенияEOF
. Вы должны приводитьchar
аргументы,(unsigned char)c
чтобы избежать неопределенного поведения в байтах, отличных от ASCII, на платформах, гдеchar
используется подписанный тип.- избегайте избыточных тестов, таких как
islower(xxx) != 0
. Более идиоматично просто писатьif (islower(xxx))
Вот измененная версия:
#include <ctype.h>
#include <stdio.h>
#include <string.h>
#include <cs50.h>
int main(int argc, string argv[]) {
// Testing the argument
if (argc < 2 || strlen(argv[1]) != 26) {
printf("invalid or missing argumentn");
return 1;
}
// Putting all the argv letters into an array called key
char key[26];
memcpy(key, argv[1], 26);
string plaintext = get_string("plaintext: ");
int len = strlen(plaintext);
// Define an array called ciphertext, the length of the inputted text, to hold ciphertext chars and a null terminator
char ciphertext[len 1];
// Looping through the inputted text, checking for upper and lower case letters
for (int i = 0; i < len; i ) {
unsigned char c = plaintext[i];
if (islower(c)) { // The letter is lower case
int index = c - 'a'; // Converting from ascii to decimal value and getting it into alphabetical index (0-25)
ciphertext[i] = tolower((unsigned char)key[index]);
} else
if (isupper(c)) {
// The letter is uppercase
int index = c - 'A'; // Converting from ascii to decimal value and getting it into alphabetical index (0-25)
ciphertext[i] = toupper((unsigned char)key[index]);
} else {
// other characters are unchanged
ciphertext[i] = c;
}
}
ciphertext[len] = ''; // set the null terminator
printf("ciphertext: %sn", ciphertext);
return 0;
}