奇怪的字符在输出中

huangapple go评论63阅读模式
英文:

Weird characters in output

问题

I have extracted and translated the code portions for you. Here's the translated code:

typedef struct {
    char *title;
    char isbn[MAX_ISBN];
    char *authors;
    char *publisher;
} Book;

typedef struct {
    Book **refs;
    int size;
    int space;
} VecBookRef;

typedef struct {
    VecBookRef *titleVec;
    VecBookRef *isbnVec;
} DynCollection;

#include <stdio.h>
#include <stdlib.h>
#include "vector.h"
#include "book.h"
#include "collection.h"
#include "splitfield.h"

int main() {
    char input[100];
    DynCollection *col;

    FILE *f = fopen("dados.csv", "r");
    if (f == NULL) {
        printf("Error opening file");
        return 0;
    }
    vecRefCreate();
    col = collecBuild(f);
    if (col == NULL) {
        printf("Error creating collection");
        return 0;
    }
    fclose(f);

    do {
        printf("\nEnter a command (t, i + isbn, q): ");
        fgets(input, sizeof(input), stdin);
        int inputLen = strlen(input);

        if (inputLen == 2 && input[0] == 't') {
            vecRefScan(col->titleVec, NULL, printBook);
        } else if (inputLen > 2 && input[0] == 'i' && input[1] == ' ') {
            char isbn[MAX_ISBN];
            sscanf(input + 2, "%s", isbn);
            if (strlen(isbn) != 13) {
                printf("\nInvalid ISBN\n");
                continue;
            }
            Book *b = vecRefSearchIsbn(col->isbnVec, isbn);
            if (b != NULL) {
                printBook(b, NULL);
            } else {
                printf("\nNo book was found with the entered ISBN\n");
                continue;
            }
        }
        if (inputLen == 2 && input[0] == 'q') {
            collecFree(col);
            printf("Memory freed");
        }
    } while (input[0] != 'q');

    return 0;
}

Book *vecRefSearchIsbn(VecBookRef *vec, char *isbn) {

    int comparator(const void *key, const void *book) {
        const char *isbn = (const char *)key;
        const Book *b = *(const Book **)book;
        return strcmp(isbn, b->isbn);
    }
    Book *b = (Book *)bsearch(isbn, vec->refs, vec->size, sizeof(Book *), comparator);
    if (b != NULL) {
        return b;
    } else return NULL;
}

void printBook(Book *b, void *context) {
    printf("\nTitle: %s\nISBN: %s\nAuthors: %s\nPublisher: %s\n", b->title, b->isbn, b->authors, b->publisher);
}

Please let me know if you need any further assistance with this code.

英文:

I have this program that extracts book data(title, isbn, authors and publisher) from a file and then populates 3 structs. The structs are all correctly populated and the command "t" that prints all the books alphabetically in order is working correctly. So everything up until that point works but then if I try to use the "i 'isbn'" (ex.: i 9780321942050) command it doesn't work. It prints some weird characters and I've tried everything in the range of function of my brain to try and fix it but nothing worked. From what I could understand the problem is in the vecRefSearchIsbn function and what it returns because everything is being passed correctly up until that point. I don't have that much knowledge about pointers so I may be missing something but I've tried a lot of things so I'm not going to list them all. I provided the main function and the vecRefSearchIsbn function because that is where I think the problem resides but if someones thinks that the problem is elsewhere I won't hesitate to provide any other functions.

This is what I get in the output:

Digite um comando (t, i + isbn, q): i 9780321942050
Title:p▬┼%7☻
ISBN: 0z┼%7☻
Authors: ░&#201;┼%7☻
Publisher: `&#161;┼%7☻
typedef struct {
char *title;
char isbn[MAX_ISBN];
char *authors;
char *publisher;
} Book;
typedef struct{
Book **refs;
int size;
int space;
} VecBookRef;
typedef struct{
VecBookRef *titleVec;
VecBookRef *isbnVec;
} DynCollection; 
#include &lt;stdio.h&gt;
#include &lt;stdlib.h&gt;
#include &quot;vector.h&quot;
#include &quot;book.h&quot;
#include &quot;collection.h&quot;
#include &quot;splitfield.h&quot;
int main(){
char input[100];
DynCollection *col;
FILE *f=fopen(&quot;dados.csv&quot;, &quot;r&quot;);
if(f==NULL){
printf(&quot;Erro ao abrir ficheiro&quot;);
return 0;
}
vecRefCreate();
col=collecBuild(f);
if(col==NULL){
printf(&quot;Erro ao criar cole&#231;&#227;o&quot;);
return 0;
}
fclose(f);
do{
printf(&quot;\nDigite um comando (t, i + isbn, q): &quot;);
fgets(input, sizeof(input), stdin);
int inputLen=strlen(input);
if(inputLen==2 &amp;&amp; input[0]==&#39;t&#39;){
vecRefScan(col-&gt;titleVec, NULL, printBook);
}
else if(inputLen&gt;2 &amp;&amp; input[0]==&#39;i&#39; &amp;&amp; input[1]==&#39; &#39;){ 
char isbn[MAX_ISBN];
sscanf(input+2, &quot;%s&quot;, isbn);
if(strlen(isbn)!=13){
printf(&quot;\nISBN invalido\n&quot;);
continue;
}
Book *b=vecRefSearchIsbn(col-&gt;isbnVec, isbn);
if(b!=NULL){
printBook(b, NULL);
}else{
printf(&quot;\nNao foi encontrado um livro com o ISBN inserido\n&quot;);
continue;
}
}
if(inputLen==2 &amp;&amp; input[0]==&#39;q&#39;){
collecFree(col);
printf(&quot;Memoria limpa&quot;);
}
}while(input[0]!=&#39;q&#39;);
return 0;
}

Book *vecRefSearchIsbn(VecBookRef *vec, char *isbn){

int comparator(const void *key, const void *book){
const char *isbn=(const char *)key;
const Book *b=*(const Book **)book;
return strcmp(isbn, b-&gt;isbn);
}
Book *b=(Book*)bsearch(isbn, vec-&gt;refs, vec-&gt;size, sizeof(Book *), comparator);
if(b!=NULL){
return b;
}else return NULL;

}

void printBook(Book *b, void *context){
printf(&quot;\nTitle:%s\nISBN: %s\nAuthors: %s\nPublisher: %s\n&quot;, b-&gt;title, b-&gt;isbn, b-&gt;authors, b-&gt;publisher);
}

答案1

得分: 0

始终在使用 `*scanf()` 读取字符串时使用最大字段宽度,否则用户输入过多数据会导致缓冲区溢出。如果事后再检查,已经为时太晚:

```c
			char isbn[MAX_ISBN];
			sscanf(input+2, "%s", isbn);
            if(strlen(isbn)!=13){

否则,数据破坏/错误可能出现在您未显示的代码中。我做了以下更改:

  1. MAX_ISBN14 更改为 13(必需与 str() 宏一致)。
  2. isbn 定义更改为使用 MAX_ISBN+1
  3. 定义 INPUT_LEN 替代内联的魔术数字 100。
  4. (未修复)检查 fgets() 是否返回 NULL。
  5. input 中删除尾随的换行符,因为您可能不关心它。如果关心,请检查 overflow 是否为 \n
  6. 不要调用 strlen(),而是使用可以处理可能太小的输入的函数,即 strcmp()strncmp()。话虽如此,我可能会考虑在 input[0] 上开关,即使这意味着现在您需要使用 goto 退出循环,而不是使用 break。
  7. 为过大的 isbn 更改错误处理,并使用最大字段宽度读取它。
  8. 使用 break/goto 而不是两次测试退出条件。
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#define MAX_ISBN 13
#define INPUT_LEN 100
#define str(s) str2(s)
#define str2(s) #s

typedef struct {
    char *title;
    char isbn[MAX_ISBN+1];
    char *authors;
    char *publisher;
} Book;

int main(void) {
    char input[INPUT_LEN];
    for(;;) {
        printf("\nDigite um comando (t, i + isbn, q): ");

        fgets(input, sizeof(input), stdin);
        input[strcspn(input, "\n")] = '\0';
        if(!strncmp(input, "i ", 2)) {
            char isbn[MAX_ISBN+1];
            char overflow;
            if(sscanf(input+2, "%" str(MAX_ISBN) "s%c", isbn, &overflow) != 1) {
                printf("\nISBN invalido\n");
                continue;
            }
            printf("%s\n", isbn);
        } else if(!strcmp(input, "q")) {
            printf("Memoria limpa");
            break;
        }
    }
}

示例会话:

Digite um comando (t, i + isbn, q): i 9780321942050
9780321942050

Digite um comando (t, i + isbn, q): i 9780321942050x

ISBN invalido

Digite um命令ti + isbnq):q
英文:

Always use a maximum field width when reading a string with *scanf() as this will cause a buffer overflow if user enters too much data. It's too late to check after the fact:

			char isbn[MAX_ISBN];
sscanf(input+2, &quot;%s&quot;, isbn);
if(strlen(isbn)!=13){

Otherwise the corruption/error is in code you haven't shown us. I made the following changes:

  1. Changed MAX_ISBN from 14 to 13 (required for the str() macro).
  2. Changed isbn definition to use MAX_ISBN+1.
  3. Define INPUT_LEN instead of the magic number 100 inline.
  4. (Not fixed) Check if fgets() returns NULL.
  5. Strip the trailing newline from input as you probably don't care about it. If you do then you check that overflow is \n.
  6. Don't call strlen() just use functions that can deal with input that is possible too small, i.e. strcmp(), strncmp(). That said, I might be tempted to switch on input[0] even if means you now need to use a goto to exit the loop instead of the break.
  7. Changed error handling for too large a isbn, and used a maximum field width to read it.
  8. Use break/goto instead of testing the exit condition twice.
#include &lt;stdio.h&gt;
#include &lt;stdlib.h&gt;
#include &lt;string.h&gt;
#define MAX_ISBN 13
#define INPUT_LEN 100
#define str(s) str2(s)
#define str2(s) #s
typedef struct {
char *title;
char isbn[MAX_ISBN+1];
char *authors;
char *publisher;
} Book;
int main(void) {
char input[INPUT_LEN];
for(;;) {
printf(&quot;\nDigite um comando (t, i + isbn, q): &quot;);
fgets(input, sizeof(input), stdin);
input[strcspn(input, &quot;\n&quot;)] = &#39;\0&#39;;
if(!strncmp(input, &quot;i &quot;, 2)) {
char isbn[MAX_ISBN+1];
char overflow;
if(sscanf(input+2, &quot;%&quot; str(MAX_ISBN) &quot;s%c&quot;, isbn, &amp;overflow) != 1) {
printf(&quot;\nISBN invalido\n&quot;);
continue;
}
printf(&quot;%s\n&quot;, isbn);
} else if(!strcmp(input, &quot;q&quot;)) {
printf(&quot;Memoria limpa&quot;);
break;
}
}
}

and example session:

Digite um comando (t, i + isbn, q): i 9780321942050
9780321942050
Digite um comando (t, i + isbn, q): i 9780321942050x
ISBN invalido
Digite um comando (t, i + isbn, q): q

huangapple
  • 本文由 发表于 2023年5月18日 06:22:41
  • 转载请务必保留本文链接:https://go.coder-hub.com/76276550.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定