【问题标题】:C - How can I print random word inside game board (2d array)?C - 如何在游戏板(二维数组)内打印随机单词?
【发布时间】:2020-07-30 06:52:48
【问题描述】:

我正在使用 C 语言和 Cygwin 终端编写一个打字游戏。

我从 .txt 文件中读取了 1000 个单词,然后我打印了一个随机单词。我需要在二维数组框“游戏板”中打印这个随机词

Link to: Image of current output. Need to move word from outside of box to inside of box.

如何在方框内打印我的随机单词?

单词需要出现在框的顶行随机水平位置。

注意:当我说盒子时,我的意思是一个 20(高)乘 80(宽)的盒子,由破折号和星号组成。

任何帮助将不胜感激。非常感谢您。

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <time.h>

void main(){

    int g, h; //index for use in for loop

        //creates box
    const int boxLength = 20;
    const int boxWidth = 75;
    char box[boxLength][boxWidth];
    for(int g = 0; g < boxLength; g++){
        for(int h = 0; h < boxWidth; h++){
            if(g == 0 || g == boxLength - 1)
                box[g][h] = '-';
            else if(h == 0 || h == boxWidth - 1)
                box[g][h] = '|';
            else
                box[g][h] = ' ';
            }
    }       

            FILE *myFilePointer2 = fopen("wordList.txt", "r");

            srand(time(0));
            int size = 1000;

            if(myFilePointer2 == NULL){
                printf("Unable to open file wordList.txt");
                exit(0);
            }

            char** words = (char**)malloc(sizeof(char**)*size); //2d pointer array, dynamically allocated, to store words from text file

            char wordBankArray[1050];//wordBankArray

            int wordQuantity = 0;

            while(fgets(wordBankArray, 1050, myFilePointer2) != NULL){// read data from myFilePointer line by line and store it into words array
                words[wordQuantity] = (char*)malloc(sizeof(char)*(strlen(wordBankArray)+1)); //dynamically allocates memory for words array
                strcpy(words[wordQuantity], wordBankArray); //copying words from text file to wordBankArray
                wordQuantity++;
            }

            printf("Randomly generated word from .txt file: ");
            int index = rand()%wordQuantity;   // psuedo randomly generates an index in range of 0 to wordQuantity)

            printf("%s\n", words[index]); //prints randomly generated word from index

            for(int g = 0; g < boxLength; g++){ //prints 2d box
                for(int h = 0; h < boxWidth; h++){
                    printf("%c", box[g][h]);
                }
                printf("\n");
                fclose(myFilePointer2); //close file for reading
            }
    }

【问题讨论】:

  • 您说您可以“从文本文件中打印一个随机单词”。这与无法“在框中打印我的随机单词”有何关系?不清楚你不知道怎么做的部分,因为“遇到麻烦”不是很准确。
  • 请尝试提供minimal verifiable example。也就是说,删除所有与核心问题无关的代码。或者创建一个新的最小示例来演示您遇到问题的特定概念。
  • 您好,感谢您回复我的帖子。我会将代码修改到最低限度,并将我的问题修改得非常具体。谢谢你的提示。
  • 在打印盒子的过程中只打印单词。也就是说,您不能轻易地打印该框,然后稍后插入单词(好吧,您可以但需要使用像ncurses 这样的终端库)。
  • “在打印盒子的过程中打印单词”到底是什么意思?我被告知 ncurses 是这个项目的禁区。谢谢@kaylum

标签: c arrays pointers struct function-pointers


【解决方案1】:

你可以试试这样的:

获取单词数组并选择一个随机单词,就像您已经在代码中所做的那样。在这里,我假设随机词是“randomWord”。
然后通过rand()函数获取随机词位置,如下代码所示。

char randomWord[20]="Program";
int wordLen = strlen(randomWord);
srand(time(0));
//Generates the random position of the word
//Subtract '2' because of the borders of the box and the word length so it can fit in the box
int wordPos = rand() % (boxWidth-wordLen-2);

for(int g = 0; g < boxLength; g++){
    for(int h = 0; h < boxWidth; h++){
        if(g == 1 && h == wordPos){ //Inserts the word at the top and at the random position
            for(int i = 0; i < wordLen; i++){
                box[g][h] = randomWord[i];
                h++;
            }
            box[g][h] = ' ';
        }
        if(g == 0 || g == boxLength - 1)
            box[g][h] = '-';
        else if(h == 0 || h == boxWidth - 1)
            box[g][h] = '|';
        else
            box[g][h] = ' ';
    }
}

然后像您已经在做的那样简单地打印该框。
您应该会看到如下内容:
Result of the box with the random word inside

希望对您有所帮助!

【讨论】:

  • 非常感谢 leticiabma 提供的有用信息。我最终使用 strLen 在我的代码中加入了类似的想法。再次感谢!
【解决方案2】:

您有许多小错误,如果您正在读取一个包含 1000 个字但只分配 100 个指针的文件 - 您会调用未定义的行为来尝试写入不存在的指针。

for(int g = 0; g &lt; boxLength; g++){ 循环中不需要fclose()(这会关闭文件boxlength 次数)。

如果您正在定义 const int ...,那么您正在创建一个 2D VLA(可变长度数组),这很好,但如果在编译时间之前就知道边界,请改为使用 #define 声明常量并避免使用 VLA在 C89/90 中不存在,在 C99 中引入并在 C11 中成为可选功能。

您还应该将-Wshadow 添加到您的编译字符串中,您可以隐藏变量:

int g, h; //index for use in for loop

当你声明循环变量时:

for(int g = 0; g < boxLength; g++){
    for(int h = 0; h < boxWidth; h++){
    ...

(注意你的变量声明——在这里你以后不要使用gh,但在其他情况下,阴影变量可能会产生可怕的后果)

考虑到这一点,您可以将所需的常量定义为:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <time.h>

#define BXLEN   20      /* if you need a constant, #define one (or more) */
#define BXWDT   75
#define WRDSZ  100
#define ARRSZ 1050

(你可以选择任何你喜欢的名字)

除非您在独立环境(没有操作系统)中工作,否则您对void main() 的声明是错误的。在符合标准的实现中,main 的允许声明为 int main (void)int main (int argc, char *argv[])(您将看到用等效的 char **argv 编写)。见:C11 Standard - §5.1.2.2.1 Program startup(p1)

参数int argc, and char **argv 允许您在命令行上将信息传递到程序中。不要硬编码文件名。 (如果您在嵌入式系统上 - 这是一个例外,因为您可能无法在命令行上传递文件名)否则,您可以这样做:

int main (int argc, char **argv)
{
    int nptrs = WRDSZ, index, wordQuantity = 0;     /* declare/initialize vars */
    char box[BXLEN][BXWDT] =  {{0}},
        **words = NULL,
        wordBankArray[ARRSZ] = "";
    FILE *fp = NULL;

    if (argc < 2 ) {    /* validate 1 argument given for filename */
        fprintf (stderr, "error: insufficient input,\n"
                         "usage: %s filename\n", argv[0]);
        return 1;
    }

    /* open file/validate file open for reading */
    if ((fp = fopen (argv[1], "r")) == NULL) {
        perror ("fopen-argv[1]");
        return 1;
    }

注意:我将myFilePointer2缩短为fp

当你在C中使用malloccallocrealloc动态分配内存时,不需要强制转换malloc的返回值,没有必要。请参阅:Do I cast the result of malloc?。如果你使用取消引用的指针来设置你的类型——你永远不会弄错。您可以为words 分配指针:

    if (!(words = malloc (nptrs * sizeof *words))) {    /* allocate/validate */
        perror ("malloc-words");
        return 1;
    }

注意:您必须验证每个分配)

注意上面,你只分配了一个初始的WRDSZ (100) 指针。如果您的文件有 1000 个字,则必须跟踪已填充的指针数(您的 wordQuantity),并且您必须跟踪分配的指针数(例如 nptrs)。当wordQuantity == nptrs 时,您必须在尝试使用另一个指针之前通过words 获得realloc 可用的指针数量(通常将当前分配的数量加倍是一个合理的增长方案)。添加额外的测试和重新分配,您的读取循环将变为:

    while (fgets (wordBankArray, ARRSZ, fp) != NULL) {  /* read each line in file */
        size_t len;     /* save length, then memcpy */
        if (wordQuantity == nptrs) {    /* check if all pointers used - realloc */
            /* always realloc using a temporary pointer -- not the pointer itself */
            void *tmp = realloc (words, 2 * nptrs * sizeof *words);
            if (!tmp) {     /* validate realloc succeeds */
                perror ("realloc-words");
                break;      /* don't exit, original words pointer still valid */
            }
            words = tmp;    /* assign reallocated block to original pointer */
            nptrs *= 2;     /* update number of pointers allocated */
        }
        if (!(words[wordQuantity] = malloc ((len = strlen (wordBankArray)) + 1))) {
            perror ("malloc-words[wordQuantity]");
            return 1;
        }
        memcpy (words[wordQuantity], wordBankArray, len + 1);
        wordQuantity++;
    }
    fclose (fp);

(注意:你只需要调用一次strlen(),保存大小,然后用memcpy()复制字符串。如果你调用strcpy(),你只是再次扫描您在调用strlen()时已经拥有的字符串的结尾)

阅读完毕后,请致电fclose()。另请注意 sizeof (char)1 并且应该从您的大小乘法中省略。您所拥有的其余部分将打印该框并输出一个随机字符串,但请注意输出中不需要转换的地方,无需调用printfputsfputs 可以(一个好的编译器会在幕后为你做出改变)

你错过的是释放你分配的内存。为此,您可以这样做:

    for (int i = 0; i < wordQuantity; i++)      /* free allocated strings */
        free (words[i]);
    free (words);                               /* free pointers */

如果你把它放在一起,你可以这样做:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <time.h>

#define BXLEN   20      /* if you need a constant, #define one (or more) */
#define BXWDT   75
#define WRDSZ  100
#define ARRSZ 1050

int main (int argc, char **argv)
{
    int nptrs = WRDSZ, index, wordQuantity = 0;     /* declare/initialize vars */
    char box[BXLEN][BXWDT] =  {{0}},
        **words = NULL,
        wordBankArray[ARRSZ] = "";
    FILE *fp = NULL;

    if (argc < 2 ) {    /* validate 1 argument given for filename */
        fprintf (stderr, "error: insufficient input,\n"
                         "usage: %s filename\n", argv[0]);
        return 1;
    }

    for (int g = 0; g < BXLEN; g++) {                   /* initialize box */
        for (int h = 0; h < BXWDT; h++) {
            if (g == 0 || g == BXLEN - 1)
                box[g][h] = '-';
            else if (h == 0 || h == BXWDT - 1)
                box[g][h] = '|';
            else
                box[g][h] = ' ';
        }
    }

    /* open file/validate file open for reading */
    if ((fp = fopen (argv[1], "r")) == NULL) {
        perror ("fopen-argv[1]");
        return 1;
    }

    srand (time (NULL));                                /* seed rand generator */

    if (!(words = malloc (nptrs * sizeof *words))) {    /* allocate/validate */
        perror ("malloc-words");
        return 1;
    }

    while (fgets (wordBankArray, ARRSZ, fp) != NULL) {  /* read each line in file */
        size_t len;     /* save length, then memcpy */
        if (wordQuantity == nptrs) {    /* check if all pointers used - realloc */
            /* always realloc using a temporary pointer -- not the pointer itself */
            void *tmp = realloc (words, 2 * nptrs * sizeof *words);
            if (!tmp) {     /* validate realloc succeeds */
                perror ("realloc-words");
                break;      /* don't exit, original words pointer still valid */
            }
            words = tmp;    /* assign reallocated block to original pointer */
            nptrs *= 2;     /* update number of pointers allocated */
        }
        if (!(words[wordQuantity] = malloc ((len = strlen (wordBankArray)) + 1))) {
            perror ("malloc-words[wordQuantity]");
            return 1;
        }
        memcpy (words[wordQuantity], wordBankArray, len + 1);
        wordQuantity++;
    }
    fclose (fp);

    fputs ("Randomly generated string from list : ", stdout);
    index = rand() % wordQuantity;
    printf ("%s\n", words[index]);

    for (int g = 0; g < BXLEN; g++) {
        for (int h = 0; h < BXWDT; h++) {
            printf ("%c", box[g][h]);
        }
        putchar ('\n');
    }

    for (int i = 0; i < wordQuantity; i++)      /* free allocated strings */
        free (words[i]);
    free (words);                               /* free pointers */
}

使用/输出示例

对于数据文件dat/1kfnames.txt,我只是简单地将1000个文件名重定向到文件中作为文字:

$ ./bin/wordinbox dat/1kfnames.txt
Randomly generated string from list : str_printf_null.c

---------------------------------------------------------------------------
|                                                                         |
|                                                                         |
|                                                                         |
|                                                                         |
|                                                                         |
|                                                                         |
|                                                                         |
|                                                                         |
|                                                                         |
|                                                                         |
|                                                                         |
|                                                                         |
|                                                                         |
|                                                                         |
|                                                                         |
|                                                                         |
|                                                                         |
|                                                                         |
---------------------------------------------------------------------------

内存使用/错误检查

在您编写的任何动态分配内存的代码中,对于分配的任何内存块,您都有 2 个职责:(1)始终保留指向起始地址的指针内存块,因此 (2) 当不再需要它时可以释放

您必须使用内存错误检查程序来确保您不会尝试访问内存或写入超出/超出分配块的边界,尝试读取或基于未初始化的值进行条件跳转,最后,以确认您释放了已分配的所有内存。

对于 Linux,valgrind 是正常的选择。每个平台都有类似的内存检查器。它们都易于使用,只需通过它运行您的程序即可。

$ valgrind ./bin/wordinbox dat/1kfnames.txt
==28127== Memcheck, a memory error detector
==28127== Copyright (C) 2002-2017, and GNU GPL'd, by Julian Seward et al.
==28127== Using Valgrind-3.13.0 and LibVEX; rerun with -h for copyright info
==28127== Command: ./bin/wordinbox dat/1kfnames.txt
==28127==
Randomly generated string from list : tor.c

---------------------------------------------------------------------------
|                                                                         |
|                                                                         |
|                                                                         |
|                                                                         |
|                                                                         |
|                                                                         |
|                                                                         |
|                                                                         |
|                                                                         |
|                                                                         |
|                                                                         |
|                                                                         |
|                                                                         |
|                                                                         |
|                                                                         |
|                                                                         |
|                                                                         |
|                                                                         |
---------------------------------------------------------------------------
==28127==
==28127== HEAP SUMMARY:
==28127==     in use at exit: 0 bytes in 0 blocks
==28127==   total heap usage: 1,008 allocs, 1,008 frees, 45,566 bytes allocated
==28127==
==28127== All heap blocks were freed -- no leaks are possible
==28127==
==28127== For counts of detected and suppressed errors, rerun with: -v
==28127== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0)

始终确认您已释放已分配的所有内存并且没有内存错误。

检查一下,如果您还有其他问题,请告诉我。

【讨论】:

猜你喜欢
  • 1970-01-01
  • 2021-07-09
  • 1970-01-01
  • 2015-07-05
  • 1970-01-01
  • 1970-01-01
  • 2012-08-31
  • 1970-01-01
相关资源
最近更新 更多