#c
#c
Вопрос:
Я пытаюсь прочитать шестнадцатеричные числа длиной 14 цифр из файла, а затем распечатать их. Моя идея состоит в том, чтобы использовать long long int и читать строки из файлов с помощью fscanf, как если бы они были строками, а затем превратить строку в шестнадцатеричное число с помощью atoll. Проблема в том, что я получаю значение seg в моей строке fscanf в соответствии с valgrind, и я абсолютно понятия не имею, почему. Вот код:
#include<stdio.h>
int main(int argc, char **argv){
if(argc != 2){
printf("error argc!= 2n");
return 0;
}
char *fileName = argv[1];
FILE *fp = fopen( fileName, "r");
if(fp == NULL){
return 0;
}
long long int num;
char *line;
while( fscanf(fp, "%s", line) == 1 ){
num = atoll(line);
printf("%xn", num);
}
return 0;
}
Комментарии:
1. вы не выделяете никакого места для
line
.2. попробуйте
char line[1024]; while (fgets(line,sizeof(line),fp) != NULL) {..}
вместо
Ответ №1:
Вы уверены, что хотите читать свои числа в виде символьных строк? Почему бы не позволить scanf
выполнить работу за вас?
long long int num;
while( fscanf(fp, "%llx", amp;num) == 1 ){ // read a long long int in hex
printf("%llxn", num); // print a long long int in hex
}
Кстати, обратите внимание на ll
спецификатор размера для %x
преобразования в printf
— он определяет, что целочисленное значение будет иметь long long
тип.
Редактировать
Вот простой пример двух циклов, считывающих 3-строчный ввод (с двумя, без и тремя числами в последовательных строках) в формате ‘hex int’ и в формате ‘string’:
http://ideone.com/ntzKEi
Вызов rewind
позволяет второму циклу считывать те же входные данные.
Комментарии:
1. @davmac Не уверен в этом. Функции Scanf сканируют
%s
до первого белого символа; если в строке несколько слов, цифр или других «черных строк», цикл должен прочитать их все, одно за другим. Здесь нет кода для удаления остальной части строки ввода после первого числа.
Ответ №2:
Эта line
переменная не инициализирована, поэтому при fscanf()
разыменовании ее вы получаете неопределенное поведение.
Вы должны использовать:
char line[1024];
while(fgets(line, sizeof line, fp) != NULL)
Для выполнения загрузки.
Если вы используете C99, вы можете использовать uint64_t
для хранения числа, поскольку это дает понять, что подойдут 14-значные шестнадцатеричные числа (4 * 14 = 56).
Ответ №3:
Другие ответы хороши, но я хочу прояснить фактическую причину сбоя, который вы видите. Проблема в том, что:
fscanf(fp, "%s", line)
… по сути, означает «прочитать строку из файла и сохранить ее в буфере, на который указывает line
«. В этом случае ваша line
переменная не была инициализирована, поэтому она никуда не указывает. Технически это неопределенное поведение; на практике результатом часто будет то, что вы выполняете запись в некотором произвольном месте в адресном пространстве вашего процесса; более того, поскольку он часто будет указывать на незаконный адрес, операционная система может обнаружить и сообщить об этом как о нарушении сегмента или подобном, что вы действительно видите.
Обратите внимание, что fscanf
при %s
преобразовании необязательно будет прочитана целая строка — она считывает строку, разделенную пробелом. Он может пропускать строки, если они пустые, и он может считывать несколько строк из одной строки. Это может не иметь значения, если вы знаете точный формат входного файла (и он всегда имеет одно значение в строке, например).
Хотя в этом случае кажется, что вы, вероятно, можете просто использовать соответствующий модификатор для чтения шестнадцатеричного числа ( fscanf(fp, "%llx", amp;num)
), а не читать строку и пытаться выполнить преобразование, существуют различные ситуации, когда вам действительно нужно читать строки и особенно целые строки. Существуют различные решения этой проблемы, в зависимости от того, на какой платформе вы находитесь. Если это система GNU (обычно включая Linux), и вас не волнует переносимость, вы могли бы использовать m
модификатор и изменить line
на amp;line
:
fscanf(fp, "%ms", amp;line);
При этом передается указатель на line
на fscanf
, а не его значение (которое неинициализировано), что m
приводит fscanf
к выделению буфера и сохранению его адреса в line
. Затем вам следует free
очистить буфер, когда вы закончите с этим. Подробности см. в руководстве Glibc. Приятная особенность этого подхода заключается в том, что вам не нужно заранее знать длину строки.
Если вы не используете систему GNU или вас волнует переносимость, используйте fgets
вместо fscanf
— это более прямолинейно и позволяет ограничить длину считываемой строки, что означает, что вы не переполните фиксированный буфер — просто имейте в виду, что он будет считывать целую строку за раз, в отличие fscanf
от того, что обсуждалось выше. Вы должны объявить line
как char
-array, а не char *
и выбрать для него подходящий размер. (Обратите внимание, что вы также можете указать «максимальную ширину поля» для fscanf
, например fscanf(fp, "00s", line)
, но вы действительно могли бы также использовать fgets
).