【问题标题】:Removing special characters from fscanf string in C从 C 中的 fscanf 字符串中删除特殊字符
【发布时间】:2013-04-07 16:50:38
【问题描述】:

我目前正在使用以下代码扫描文本文件中的每个单词,将其放入变量中,然后对其进行一些操作,然后再转到下一个单词。这很好用,但我正在尝试删除所有不属于A-Z / a-z. 的字符,例如,如果输入了"he5llo",我希望输出为"hello"。如果我不能修改fscanf 来做,有没有办法在扫描后对变量进行处理?谢谢。

while (fscanf(inputFile, "%s", x) == 1)

【问题讨论】:

  • fscanf 有一个大问题:它是潜在的缓冲区溢出。当您拥有char x[100] 时,您应该始终使用例如fscanf(inputFile, "%99s", x)

标签: c scanf


【解决方案1】:

您可以将x 赋予这样的函数。为了便于理解,第一个简单版本:

// header needed for isalpha()
#include <ctype.h>

void condense_alpha_str(char *str) {
  int source = 0; // index of copy source
  int dest = 0; // index of copy destination

  // loop until original end of str reached
  while (str[source] != '\0') {
    if (isalpha(str[source])) {
      // keep only chars matching isalpha()
      str[dest] = str[source];
      ++dest;
    }
    ++source; // advance source always, wether char was copied or not
  }
  str[dest] = '\0'; // add new terminating 0 byte, in case string got shorter
}

它将就地遍历字符串,复制匹配isalpha() 测试的字符,跳过并删除不匹配的字符。要理解代码,重要的是要意识到 C 字符串只是 char 数组,字节值 0 标记字符串的结尾。另一个重要的细节是,在 C 中,数组和指针在许多(不是全部!)方面都是相同的,所以指针可以像数组一样被索引。此外,这个简单的版本将重写字符串中的每个字节,即使字符串实际上没有改变。


然后是一个更全功能的版本,它使用作为参数传递的过滤函数,并且只会在 str 发生变化时进行内存写入,并像大多数库字符串函数一样返回指向 str 的指针:

char *condense_str(char *str, int (*filter)(int)) {

  int source = 0; // index of character to copy

  // optimization: skip initial matching chars
  while (filter(str[source])) {
    ++source; 
  }
  // source is now index if first non-matching char or end-of-string

  // optimization: only do condense loop if not at end of str yet
  if (str[source]) { // '\0' is same as false in C

    // start condensing the string from first non-matching char
    int dest = source; // index of copy destination
    do {
      if (filter(str[source])) {
        // keep only chars matching given filter function
        str[dest] = str[source];
        ++dest;
      }
      ++source; // advance source always, wether char was copied or not
    } while (str[source]);
    str[dest] = '\0'; // add terminating 0 byte to match condenced string

  }

  // follow convention of strcpy, strcat etc, and return the string
  return str;
}

过滤函数示例:

int isNotAlpha(char ch) {
    return !isalpha(ch);
}

示例调用:

char sample[] = "1234abc";
condense_str(sample, isalpha); // use a library function from ctype.h
// note: return value ignored, it's just convenience not needed here
// sample is now "abc"
condense_str(sample, isNotAlpha); // use custom function
// sample is now "", empty

// fscanf code from question, with buffer overrun prevention
char x[100];
while (fscanf(inputFile, "%99s", x) == 1) {
  condense_str(x, isalpha); // x modified in-place
  ...
}

参考:

阅读int isalpha ( int c );手册:

检查 c 是否为字母。
返回值
如果 c 确实是一个字母,则该值不同于零(即 true)。否则为零(即假)

【讨论】:

  • @RandyHoward 如果您认为它是错误的,建议您应该如何回应相反.. hyde 不知道 OP 是要求做作业还是出于自学目的。海德只是帮忙。
  • @hyde 我想建议始终解释您的代码,以便更好地帮助 OP ..
  • 为回答干杯,虽然我不完全理解你给出的例子,所以我很难将它用于我的方法。
  • @user2254988 我修改了代码以使用索引而不是指针算法。现在清楚了吗?
  • +1 - 一小部分更改使此功能更加通用。与其硬编码它以使用isalpha(),不如将​​它传递给一个函数的指针(与isalpha() 和其他ctype.h 字符分类函数具有相同的原型),您可以轻松地使用它来过滤任何类别的字符,甚至是自定义字符类:compress_str( char* str, int (*filter)(int))
【解决方案2】:

luser droog 回答会起作用,但在我看来它比必要的复杂。

你可以试试这个简单的例子:

while (fscanf(inputFile, "%[A-Za-z]", x) == 1) {   // read until find a non alpha character
   fscanf(inputFile, "%*[^A-Za-z]"))  // discard non alpha character and continue
}

【讨论】:

    【解决方案3】:

    您可以使用isalpha() 函数检查字符串中包含的所有字符

    【讨论】:

      【解决方案4】:

      我正在做一个类似的项目,所以你会得到很好的照顾!将单词分解成单独的部分。

      空格不是 cin 每个单词的问题 你可以使用一个

       if( !isPunct(x) )
      

      将索引增加 1,并将新字符串添加到临时字符串持有者。 您可以像数组一样选择字符串中的字符,因此查找那些非字母字符并存储新字符串很容易。

       string x = "hell5o"     // loop through until you find a non-alpha & mark that pos
       for( i = 0; i <= pos-1; i++ )
                                          // store the different parts of the string
       string tempLeft = ...    // make loops up to and after the position of non-alpha character
       string tempRight = ... 
      

      【讨论】:

        【解决方案5】:

        scanf 系列函数不会这样做。您必须遍历字符串并使用isalpha 检查每个字符。并通过向前复制字符串的结尾来“删除”带有memmove 的字符。

        也许scanf毕竟可以做到。在大多数情况下,scanf 和朋友会在匹配失败时将任何非空白字符推回输入流中。

        此示例使用scanf 作为流上的正则表达式过滤器。使用* 转换修饰符意味着否定模式没有存储目标;它只是被吃掉了。

        #include <stdio.h>
        #include <string.h>
        
        int main(){
            enum { BUF_SZ = 80 };   // buffer size in one place
            char buf[BUF_SZ] = "";
            char fmtfmt[] = "%%%d[A-Za-z]";  // format string for the format string
            char fmt[sizeof(fmtfmt + 3)];    // storage for the real format string
            char nfmt[] = "%*[^A-Za-z]";     // negated pattern
        
            char *p = buf;                               // initialize the pointer
            sprintf(fmt, fmtfmt, BUF_SZ - strlen(buf));  // initialize the format string
            //printf("%s",fmt);
            while( scanf(fmt,p) != EOF                   // scan for format into buffer via pointer
                && scanf(nfmt) != EOF){                  // scan for negated format
                p += strlen(p);                          // adjust pointer
                sprintf(fmt, fmtfmt, BUF_SZ - strlen(buf));   // adjust format string (re-init)
            }
            printf("%s\n",buf);
            return 0;
        }
        

        【讨论】:

          猜你喜欢
          • 2019-11-09
          • 2011-04-11
          • 2016-01-23
          • 2014-05-20
          • 1970-01-01
          相关资源
          最近更新 更多