Как прочитать и распечатать шестнадцатеричные числа из файла на C

#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 ).