Проблема с шестнадцатеричным литералом в сравнении строк
Я читаю в файле ROM NES, где первые четыре байта являются "\ x4e \ x45 \ x53 \x1a" или NES \x1a. В моем реальном коде данный файл может быть произвольным, поэтому я хочу проверить, чтобы этот заголовок был здесь. Тем не менее, я столкнулся с некоторыми проблемами, которые демонстрирует следующий код:
#include <stdio.h>
#include <string.h>
int main()
{
FILE *fp;
fp = fopen("mario.nes", "rb");
char nes[4];
char real_nes[4] = "NES\x1a";
fread(nes, 4, 1, fp);
printf("A: %x\n", nes[3]);
printf("B: %x\n", real_nes[3]);
printf("C: %s\n", nes);
printf("D: %s\n", real_nes);
if (strcmp(nes, real_nes) != 0) {
printf("not a match\n");
}
fclose(fp);
return 0;
}
который возвращает:
A: 1a
B: 1a
C: NES?
D: NES
not a match
где вопросительный знак \x1a.
Я новичок в C, так что возможно я упускаю что-то неуловимое (или очевидное) о том, почему две строки не совпадают, и почему вопросительный знак не отображается при печати строки D, чтобы показать, что \x1a там, в конце строки, строка B, кажется, указывает, что это должно быть.
5 ответов
Основная проблема в вашем коде:
char real_nes[4] = "NES\x1a";
Это не строка, так как она не заканчивается символом nul-terminator ('\0'). Это та же проблема для "нет".
Просто объявите их как:
char real_nes[] = "NES\x1a"; /* this is a string, ended by '\0' */
char nes[sizeof real_nes];
Чтобы быть уверенным, что есть место для '\0'.
Теперь вы можете использовать спецификатор%s или strcmp(). В любом случае, я рекомендую вместо этого использовать strncmp(), как в:
if(0 != strncmp(real_nes, nes, sizeof real_nes)) { /* some stuff */ }
НТН.
Некоторые замечания и предложения:
открывайте файлы в двоичном режиме - в противном случае в системах, отличных от POSIX, могут происходить забавные вещи (исправлено)
fp = fopen("mario.nes", "rb");
завершите нулевые буферы, если вы хотите распечатать или сравнить их или использовать такие функции, как
strncmp()
которые принимают длину строки в качестве дополнительного аргументаprintf("C: %.4s\n", nes); printf("D: %.4s\n", real_nes); if (strncmp(nes, real_nes, 4) != 0) {
'\x1a'
является неграфическим символом замены^Z
- проверьте возвращаемые значения функций io на наличие ошибок
Ну, одна проблема - это использование strcmp. Эта функция ожидает строку с нулевым разрешением (ни nes, ни real_nes не являются строкой с нулевым символом в вашем коде).
Еще одна проблема - фред. Используйте это так:
fread(nes, 1, 4, fp); // first size_t param is size and second is member count
Измените свой код следующим образом:
int main()
{
FILE *fp;
fp = fopen("mario.nes", "rb");
char nes[5];
char real_nes[5] = "NES\x1a";
fread(nes, 1, 4, fp);
nes[4] = '\0';
printf("A: %x\n", nes[3]);
printf("B: %x\n", real_nes[3]);
printf("C: %s\n", nes);
printf("D: %s\n", real_nes);
if (strcmp(nes, real_nes) != 0) {
printf("not a match\n");
}
fclose(fp);
return 0;
}
И посмотри, работает ли это.
Не используйте строковые функции в байтовых массивах с нулевым символом в конце.
Проблема в том, что у вас есть два 4-байтовых массива, которые должны содержать строку "NES\x1a" (для '\0' не осталось места, поскольку она уже имеет длину 4 байта), но формат%s и strcmp нуждаются в '\ 0 конец в конце, чтобы знать конец строки. Вот почему это не работает правильно.
1.: Не используйте printf с форматом%s в этом байтовом массиве. 2.: Используйте memcmp для сравнения байтов.
Попробуйте это вместо этого:
int i;
printf("Read bytes: 0x");
for(i = 0; i < sizeof(nes); i ++)
printf("%02X", nes[i]);
printf("\n");
if (memcmp(nes, real_nes, sizeof(nes)) != 0) {
printf("not a match\n");
}
Возможно, немного уже поздно, но вот как я это делаю:
// Read the 16 byte iNES header
char header[16];
fread( header, 16, 1, file );
// Search for the "NES^Z" signature
if( memcmp( header, "NES\x1A", 4 ) )
{
Как предложил Ксено, с memcmp вам нет дела до нулевых терминаторов. В конце концов, вы на самом деле не используете строки, а скорее как массивы символов, что не совпадает с нулевыми терминаторами. Поскольку на самом деле вам не нужно печатать подпись, кроме как для отладки, вам совершенно не нужно использовать строковые функции.