C - 从字符串中提取大写字母

时间:2015-11-03 22:48:37

标签: c string

我正在开发一个程序,其目标是从文件中读取字符串并仅打印大写字母。我的代码如下:

#include <stdio.h>
#include <string.h> //enables the use of string functions

#define BUFFER_SIZE 25 //limits string to 25 characters
#define INPUT_FILE "l6p2.txt"

//Function prototypes
char extractCaps(char *str);

int main()
{    
    //Create an array that can hold 25 characters
    char string[BUFFER_SIZE] = { 0 }; 

    //Open the text file
    FILE *fp = NULL;
    fp = fopen(INPUT_FILE, "r"); //read only

    //Scan string from the file
    fscanf(fp, "%s", string);

    //Call the function and pass the string from the file as the parameter
    extractCaps(string); 
    printf("%s", string);

    return 0;
}

//Recursive function that will extract capital letters from a string
char extractCaps(char *str)
{
    static int i=0;

    if(i < strlen(str))
    {
        //Base case (if the string is empty)
        if (str[i] == '\0') // \0 is a character that is automatically added at the end of a string
        {
            return;
        }

        else if((str[i] >= 65 && str[i] <= 90)) //if the letter at this index is capital (ASCII codes for A and Z)
        {
            return str[i];
            i++;
        }
        else //if not a capital letter
        {
        //Recursive call... move to the next letter and repeat the above steps
        i++;
        return extractCaps(str);
        }
    }
    printf("\n");
}

l6p2.txt文件包含字符串"hHeElLlLoO"。因此,我希望输出为"HELLO",但我的代码仍在输出整个字符串"hHeElLlLoO"。当我在printf函数中有extractCaps语句时,我原来的代码工作正常,但我被告知要更改它以便在main中进行打印。对于我的生活,我无法理解为什么这段代码表现不正确。这个逻辑对我来说很有意义,但是我再也不知道对编程的蹲坐了。

作为旁注,我听说将静态变量与递归相结合是一个坏主意,所以我尝试使用i作为extractCaps的参数,但仍然得到相同(不正确)的输出。

我哪里错了?

编辑:要求extractCaps是递归函数。

3 个答案:

答案 0 :(得分:2)

虽然你已经有了答案,但是有一些事情要考虑未来。首先,当您打开一个文件进行阅读始终验证开放实际上是否成功:

/* Open the text file AND VALIDATE */
FILE *fp = NULL;
if (!(fp = fopen ("filename.txt", "r"))) {
    fprintf (stderr, "error: file open failed '%s'.\n", "filename.txt");
    return 1;
}

如果只是将文件名作为参数传递给程序,则可以使代码更加灵活,而不是在代码中对"filename.txt"进行硬编码。这是int main (int argc, char **argv) main参数的目的。它只需要额外的努力:

int main (int argc, char **argv)
{    
    ...
    /* validate sufficient input on command line (filename) given */
    if (argc < 2) {
        fprintf (stderr, "error: insufficient input. usage: %s filename",
                argv[0]);
        return 1;
    }

    /* Open the text file AND VALIDATE */
    FILE *fp = NULL;
    if (!(fp = fopen (argv[1], "r"))) {
        fprintf (stderr, "error: file open failed '%s'.\n", argv[1]);
        return 1;
    }
...

虽然您可以使用fscanf读取字符串,但尝试使用Xscanf函数族进行输入时会涉及许多陷阱。 (他们有自己的位置,但对于一般阅读,有更好的选择)

在阅读文件时,您通常要考虑使用C标准库提供的面向行的输入函数之一,如fgetsgetline。一般方法是一次读取一行到缓冲区,然后解析缓冲区的内容以获取所需的信息。一次读取一行并不比一次读取一个字符串困难,但它更不容易出错,例如:

/* read each line in file into buf
 * BUFFER_SIZE set to 256 (or as needed)
 */
while (fgets (buf, BUFFER_SIZE, fp))
    extractCaps (string, buf, &stridx);

在这种情况下,您需要以某种方式将所有大写字母收集到第二个缓冲区中,以便在文件读取完成后保存它们以进行打印。当然,使用单行文件几乎没有必要,但由于这些只是提示,让我们考虑一下你的文件是否超过一行,例如:

hHeElL
lLoO

然后呢?在这种情况下,您将读取一行,然后将该行传递给extractCaps。然后,extractCaps将以递归方式扫描该行,或者只是使用指针逐步遍历每个字符,测试每个字符以确定它是否为大写,然后将字符保存在字符串中,并将字符串索引递增1(所以它会知道在哪里保存下一个)。通过使用单独的缓冲区来保存大写字母,您可以处理具有任意行数的输入文件。

每当您填充固定大小的任何类型缓冲区时,您需要检查缓冲区是否已满,并且如果是,则采取适当的操作。 (写入超出缓冲区的末尾是遇到麻烦的可靠方法。)

最后,您需要一种方法来跟踪在调用extractCaps之间在缓冲区中写下一个Capital的位置的索引。有几种方法。您可以让函数返回索引号并以这种方式跟踪它 - 或者 - 您可以将指针作为参数传递给extractCaps,以便在main中的extractCaps中更新值。将所有这些部分放在备用extractCaps函数中,它可能看起来像:

/* simple function to extract capital letters from 'buf' and save in 'str'
   updating the index 'idx' to reflect the number of chars in string */
void extractCaps (char *str, char *buf, size_t *idx)
{
    if (!buf || !str || !idx) { /* validate parameters */
        fprintf (stderr, "extractCaps() error: invalid parameter.\n");
        return;
    }

    char *p = buf;      /* pointer to buf */

    while (*p) {    /* for each char in buf */
        if ('A' <= *p && *p <= 'Z') {

            if (*idx == BUFFER_SIZE - 1) { /* check idx against BUFFER_SIZE */
                fprintf (stderr, "extractCaps() error: BUFFER_SIZE reached.\n");
                return;
            }

            str[(*idx)++] = *p; /* copy CAP to str, increment idx */
        }
        p++;
    }
}

采取下一步并将所有部分放在一起,这是解决问题的另一种方法。这只是未来需要考虑的事项,用于比较目的,以及以面向行的方式读取文件的示例:

#include <stdio.h>
#include <string.h>

#define BUFFER_SIZE 256 /* limits line to 256 characters */

/* Function prototypes */
void extractCaps (char *str, char *buf, size_t *idx);

int main (int argc, char **argv)
{    
    /* Create an array that can hold 25 characters */
    char buf[BUFFER_SIZE]    = { 0 }; 
    char string[BUFFER_SIZE] = { 0 }; 
    size_t stridx = 0;

    /* validate sufficient input on command line (filename) given */
    if (argc < 2) {
        fprintf (stderr, "error: insufficient input. usage: %s filename",
                argv[0]);
        return 1;
    }

    /* Open the text file AND VALIDATE */
    FILE *fp = NULL;
    if (!(fp = fopen (argv[1], "r"))) {
        fprintf (stderr, "error: file open failed '%s'.\n", argv[1]);
        return 1;
    }

    /* read each line in file into buf */
    while (fgets (buf, BUFFER_SIZE, fp))
        extractCaps (string, buf, &stridx);

    /* print the results in string */
    printf("\nAll caps in file : %s\n\n", string);

    return 0;
}

/* simple function to extract capital letters from 'buf' and save in 'str'
   updating the index 'idx' to reflect the number of chars in string */
void extractCaps (char *str, char *buf, size_t *idx)
{
    if (!buf || !str || !idx) { /* validate parameters */
        fprintf (stderr, "extractCaps() error: invalid parameter.\n");
        return;
    }

    char *p = buf;      /* pointer to buf */

    while (*p) {    /* for each char in buf */
        if ('A' <= *p && *p <= 'Z') {

            if (*idx == BUFFER_SIZE - 1) { /* check idx against BUFFER_SIZE */
                fprintf (stderr, "extractCaps() error: BUFFER_SIZE reached.\n");
                return;
            }

            str[(*idx)++] = *p; /* copy CAP to str, increment idx */
        }
        p++;
    }
}

输入文件

$ cat dat/hellocaps.txt
hHeElLlLoO

使用/输出

$ ./bin/extract_caps dat/hellocaps.txt

All caps in file : HELLO

答案 1 :(得分:1)

以下是解决方案,基本上您只需要检查fgetc中的字符是否在大写字母A到Z字母的ASCII值范围65到90范围内。

#include<stdio.h>
#pragma warning(disable : 4996)


int main()
{
   FILE *pfile;
   int data;
   pfile = fopen("test.txt", "r");

   printf("Opening file...\n");
   if (pfile == NULL)
   {
     printf("Error!\n");
   }

   while ((data = fgetc(pfile)) != EOF)
   {
        if (data>=65 && data<=90)//ascii values 65 to 90 represent 
        {                        //Upper Case Letters A to Z.
            printf("%c\n", data);
        }       
   }
   fclose(pfile);
   return 0;
}

以下是Ascii Table ASCII Table的链接。

答案 2 :(得分:1)

您的固定代码

#include <stdio.h>
#include <string.h> //enables the use of string functions

#define BUFFER_SIZE 25 //limits string to 25 characters
#define INPUT_FILE "l6p2.txt"

//Function prototypes
void extractCaps(char *str, char* ostr, int iPos, int iCurrPos);

int main()
{    
    //Create an array that can hold 25 characters
    char string[BUFFER_SIZE] = { 0 };
    char ostring[BUFFER_SIZE] = { 0 };

    //Open the text file
    FILE *fp = NULL;
    fp = fopen(INPUT_FILE, "r"); //read only

    //Scan string from the file
    fscanf(fp, "%s", string);

    //Call the function and pass the string from the file as the parameter
    extractCaps(string, ostring, 0, 0); 
    printf("%s",ostring);

    return 0;
}

//Recursive function that will extract capital letters from a string
void extractCaps(char *str, char* ostr, int iPos, int iCurrPos)
{
    if (iPos < strlen(str))
    {
        if((str[iPos] >= 65 && str[iPos] <= 90)) //if the letter at this index is capital (ASCII codes for A and Z)
        {
            ostr[iCurrPos] = str[iPos];
            extractCaps(str, ostr, iPos + 1, iCurrPos + 1);
        }
        else
            extractCaps(str, ostr, iPos + 1, iCurrPos);

    }
}

我强烈建议你避免在这样的函数中使用静态变量,因为如果第二次使用这个函数,你将收到超出边界错误的索引,这在C中很难找到。