问题描述
我的代码输出不正确。例如,如果我输入“ joy is joyful”,然后要删除单词“ joy”,则输出应为“ is joyful”,但输出与输入相同。
这是完整的代码:
#include<stdio.h>
#include<conio.h>
#include<string.h>
void print(char string[100]);
main()
{
char string[100],remove[100];
int stringLen,removeLen,i,j,k,l,count,location,sLen,ij,counter = 0;
printf("Enter any string: ");
gets(string);
printf("Enter word to remove: ");
gets(remove);
printf("\nString before removing '%s': ",remove);
print(string);
stringLen = strlen(string);
sLen = stringLen;
removeLen = strlen(remove);
for(i=0; i<stringLen; i++)
{
count = 0;
for(j=0; j<removeLen; j++)
{
if(string[i+j] == remove[j])
{
count++;
location = i;
ij = i+j;
}
}
if(count == removeLen)
{
if(string[ij+1] == '\0' && string[ij+1] == ' ' && string[ij+1] == '\n')
{
counter = count;
}
else
{
counter = count - 1;
}
}
if(counter == removeLen)
{
for(l=0; l<count; L++)
{
for(k=location; k<sLen; k++)
{
string[k] = string[k+1];
}
sLen--;
}
}
}
printf("\n\nString after removing '%s':",remove);
print(string);
getch();
return 0;
}
void print(char string[100])
{
printf("\n%s",string);
}
我试图做这部分:
if(count == removeLen)
{
if(string[ij+1] == '\0' && string[ij+1] == ' ' && string[ij+1] == '\n')
{
counter = count;
}
else
{
counter = count - 1;
}
}
为此,它起作用了:
if(count == removeLen)
{
if(string[ij+1] != '\0' && string[ij+1] != ' ' && string[ij+1] != '\n')
{
counter = count - 1;
}
else
{
counter = count;
}
}
原来的似乎是什么问题?
解决方法
对于pf all(永远,永远,永远),请使用gets()
。从C11开始从C库中删除的缓冲区溢出很容易被利用。有关更多讨论,请参见:Why gets() is so dangerous it should never be used!
在单词替换中,您不必担心在删除单词之前或之后删除前导或尾随空格,并且仅当该单词不是较大单词中的子字符串或带有标点符号的单词时才删除该单词。 (这很好-但是在隔离删除词时,通常需要考虑剩下的内容)
您可以简化您要尝试执行的操作,并将整个算法简化为字符串中字符的单个循环。您只需保留三个索引(如果您想以这种方式考虑,则保留计数器)。您需要 read-index ,下一个要读取的字符。您需要一个 write-index ,即字符串中要写入的下一个位置。最后,您需要对要删除的子字符串中的字符进行 remove-index 。
这里,您只需使用读取索引在字符串中的字符之间循环即可。您的读写索引开始相同。如果一个字母与您的remove子字符串中的第一个字母匹配,则可以增加remove-index并再次循环。如果一个字符序列与您的删除子字符串中的所有字符匹配,则在下一次迭代中,您的子字符串索引将以其 nul-终止字符为
。现在,您可以测试字符串中read-index下的下一个字符是否为空格(使用isspace()
宏),或者测试您是否位于原始字符串的末尾。如果这两种情况都成立,则只需从写索引中减去子字符串的长度,然后继续进行-有效地从原始字符串中删除该子字符串。不需要多循环,实际上,您正在通过子字符串索引来遍历原始跟踪的每个字符(状态)。
以这种方式实现的一个简短示例可能类似于以下内容。函数remove_substr()
读取str
中的字符,并删除其中每个孤立的substr
,从而原位更新str
:
int remove_substr (char *str,const char *substr)
{
if (!strstr (str,substr)) /* if substr not found in str */
return 0; /* return 0 - nothing replaced */
size_t sslen = strlen (substr),/* length of substr */
i = 0,j = 0,n = 0; /* read,write,substr indexes */
do { /* loop over str (including '\0') */
if (!substr[n]) { /* substr found (at substr '\0') */
/* if at end of str or whitespace */
if (!str[i] || isspace((unsigned char)str[i]))
j -= sslen; /* subtract sslen from write index */
n = 0; /* reset substr index */
}
str[j++] = str[i]; /* copy from read to write index */
if (str[i] == substr[n]) /* if char matches substr */
n++; /* increment substr counter */
} while (str[i++]); /* exit after '\0' processed */
return 1; /* return replacements made */
}
为返回类型选择了简单类型int
来指示0
没有发生删除,或者1
表示从{{1 }}。
调用该函数的简短示例可以是:
substr
只需运行该程序,系统将提示您输入要删除的字符串和子字符串。当前,所使用的每个字符串都限于str
(#include <stdio.h>
#include <string.h>
#include <ctype.h>
#define MAXC 1024
/* insert function here */
int main (void) {
char str[MAXC] = "",/* storage for string */
substr[MAXC] = ""; /* storage for substring */
fputs ("enter string: ",stdout); /* prompt for string */
if (!fgets (str,sizeof str,stdin)) /* read/validate input */
return 1;
str[strcspn(str,"\n")] = 0; /* overwrite '\n' with '\0' */
fputs ("enter substr: ",stdout); /* ditto for substr */
if (!fgets (substr,sizeof substr,stdin))
return 1;
substr[strcspn(substr,"\n")] = 0;
if (remove_substr (str,substr)) /* remove all substr in str */
printf ("\nresult: '%s'\n",str); /* output updated str if removals */
else
puts ("\nno replacements made"); /* otherwise output no replacements */
}
个字符),可以根据需要进行调整-但不要忽略缓冲区大小。
使用/输出示例
MAXC
一个更复杂的示例:
1024
有很多方法可以编写这样的函数。您可以使用$ ./bin/str_rm_substr
enter string: joy is joyful
enter substr: joy
result: ' is joyful'
的组合来标记原始字符串的副本,以检查每个标记是否与您要删除的$ ./bin/str_rm_substr
enter string: joy is joyful,joy is full of joy
enter substr: joy
result: ' is joyful,is full of '
相匹配。您可以使用多个循环对字符串进行细化搜索,以向前扫描以找到strtok()
中的第一个字母,然后循环查看是否匹配。您还可以使用substr
和substr
的组合来执行相同的蠕虫技术,让这些函数为您处理循环。可能有1/2种左右的有效方法。
仔细研究一下,如果您有任何疑问,请告诉我。
,这里
if(string[ij+1] == '\0' && string[ij+1] == ' '
您测试一个字符是否同时是 和 。
那将永远不是真的。用命令的话来说,整个if语句都是无用的,因为它总是走错误的路径。
,在这种情况下,问题是if(string[ij+1] == '\0' && string[ij+1] == ' ' && string[ij+1] == '\n')
,计数器减少了。因此,减少之后,您将永远不会进入此代码:
if(counter == removeLen)
{
for(l=0; l<count; l++)
{
for(k=location; k<sLen; k++)
{
string[k] = string[k+1];
}
sLen--;
}
}
因此删除此代码:
if(count == removeLen)
{
if(string[ij+1] == '\0' && string[ij+1] == ' ' && string[ij+1] == '\n')
{
counter = count;
}
else
{
counter = count - 1;
}
}
这将是可行的。