在C语言中从fscanf字符串中删除特殊字符

3
我目前正在使用以下代码扫描文本文件中的每个单词,将其放入变量中,然后对其进行一些操作,然后再移动到下一个单词。这个过程很好,但我想删除所有不属于A-Z / a-z范围内的字符。例如,如果输入"he5llo",我希望输出为"hello"。如果我不能修改fscanf以执行此操作,那么一旦扫描变量是否有方法可以做到?谢谢。
while (fscanf(inputFile, "%s", x) == 1)

那个 fscanf 存在一个大问题:它可能会导致缓冲区溢出。当你有 char x[100] 时,你应该总是使用例如 fscanf(inputFile, "%99s", x) - hyde
5个回答

3
您可以像这样将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字符串只是具有字节值0表示字符串结尾的char数组。另一个重要细节是,在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)。否则返回零(即false)。


1
@RandyHoward 如果你认为这是错误的,请建议应该如何回应。然而,Hyde不知道OP是在寻求作业帮助还是自学目的,只是在提供帮助。 - Grijesh Chauhan
谢谢你的回答,虽然我不完全理解你给出的例子,所以我会在我的方法中遇到困难。 - user2254988
@user2254988 现在有帮助了吗?如果您有疑问,请提出并确保您完全理解... - Grijesh Chauhan
1
+1 - 只需进行少量更改,就可以使此函数更加通用。不要将其硬编码为使用 isalpha(),而是传递一个指向函数的指针(具有与 isalpha() 和其他 ctype.h 字符分类函数相同的原型),您可以轻松地使用它来过滤任何类别的字符,甚至是自定义字符类:compress_str( char* str, int (*filter)(int)) - Michael Burr
刚刚意识到它不需要返回,因为它是一个指针,仍在努力掌握它们!不,这正是我所需要的,感谢您,因为我正在尝试使其尽可能紧凑和简单。 - user2254988
显示剩余8条评论

1

我认为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
}

0

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;
}

0

我正在处理一个类似的项目,所以你可以放心!将这个单词分解成不同的部分。

使用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 = ... 

0

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


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接