我尝试编写一个删除多余空格的脚本,但我没有设法完成它。
基本上我想将abc sssd g g sdg gg gf
转换为abc sssd g g sdg gg gf
。
在PHP或C#等语言中,它很容易,但在C ++中却没有,我知道。这是我的代码:
#include <iostream>
#include <stdio.h>
#include <stdlib.h>
#include <cstring>
#include <unistd.h>
#include <string.h>
char* trim3(char* s) {
int l = strlen(s);
while(isspace(s[l - 1])) --l;
while(* s && isspace(* s)) ++s, --l;
return strndup(s, l);
}
char *str_replace(char * t1, char * t2, char * t6)
{
char*t4;
char*t5=(char *)malloc(10);
memset(t5, 0, 10);
while(strstr(t6,t1))
{
t4=strstr(t6,t1);
strncpy(t5+strlen(t5),t6,t4-t6);
strcat(t5,t2);
t4+=strlen(t1);
t6=t4;
}
return strcat(t5,t4);
}
void remove_extra_whitespaces(char* input,char* output)
{
char* inputPtr = input; // init inputPtr always at the last moment.
int spacecount = 0;
while(*inputPtr != '\0')
{
char* substr;
strncpy(substr, inputPtr+0, 1);
if(substr == " ")
{
spacecount++;
}
else
{
spacecount = 0;
}
printf("[%p] -> %d\n",*substr,spacecount);
// Assume the string last with \0
// some code
inputPtr++; // After "some code" (instead of what you wrote).
}
}
int main(int argc, char **argv)
{
printf("testing 2 ..\n");
char input[0x255] = "asfa sas f f dgdgd dg ggg";
char output[0x255] = "NO_OUTPUT_YET";
remove_extra_whitespaces(input,output);
return 1;
}
它不起作用。我尝试了几种方法。我想要做的是逐个字符地迭代字符串并将其转储到另一个字符串中,只要一行中只有一个空格;如果有两个空格,请不要将第二个字符写入新字符串。
我该如何解决这个问题?
答案 0 :(得分:24)
已经有很多很好的解决方案。我建议您选择基于专用<algorithm>
的替代方案,以避免连续重复: unique_copy()
:
void remove_extra_whitespaces(const string &input, string &output)
{
output.clear(); // unless you want to add at the end of existing sring...
unique_copy (input.begin(), input.end(), back_insert_iterator<string>(output),
[](char a,char b){ return isspace(a) && isspace(b);});
cout << output<<endl;
}
这是 live demo 。 请注意,我从c样式字符串更改为更安全,更强大的C ++字符串。
编辑: 如果代码中需要保留c风格的字符串,则可以使用几乎相同的代码但使用指针代替迭代器。这就是C ++的神奇之处。这是another live demo。
答案 1 :(得分:8)
这是一个简单的非C ++ 11解决方案,使用与问题相同的remove_extra_whitespace()
签名:
#include <cstdio>
void remove_extra_whitespaces(char* input, char* output)
{
int inputIndex = 0;
int outputIndex = 0;
while(input[inputIndex] != '\0')
{
output[outputIndex] = input[inputIndex];
if(input[inputIndex] == ' ')
{
while(input[inputIndex + 1] == ' ')
{
// skip over any extra spaces
inputIndex++;
}
}
outputIndex++;
inputIndex++;
}
// null-terminate output
output[outputIndex] = '\0';
}
int main(int argc, char **argv)
{
char input[0x255] = "asfa sas f f dgdgd dg ggg";
char output[0x255] = "NO_OUTPUT_YET";
remove_extra_whitespaces(input,output);
printf("input: %s\noutput: %s\n", input, output);
return 1;
}
输出:
input: asfa sas f f dgdgd dg ggg
output: asfa sas f f dgdgd dg ggg
答案 2 :(得分:6)
由于您使用C ++,因此您可以利用为此类工作设计的标准库功能。您可以使用std::string
(而不是char[0x255]
)和std::istringstream
来代替大部分指针算法。
首先,创建一个字符串流:
std::istringstream stream(input);
然后,从中读取字符串。它将自动删除空白分隔符:
std::string word;
while (stream >> word)
{
...
}
在循环内部,构建输出字符串:
if (!output.empty()) // special case: no space before first word
output += ' ';
output += word;
这种方法的一个缺点是它动态分配内存(包括几个重新分配,在输出字符串增长时执行)。
答案 3 :(得分:3)
有很多方法可以做到这一点(例如,使用正则表达式),但是你可以这样做的一种方法是使用std::copy_if
和状态函子来记住最后一个字符是否是空格:
#include <algorithm>
#include <string>
#include <iostream>
struct if_not_prev_space
{
// Is last encountered character space.
bool m_is = false;
bool operator()(const char c)
{
// Copy if last was not space, or current is not space.
const bool ret = !m_is || c != ' ';
m_is = c == ' ';
return ret;
}
};
int main()
{
const std::string s("abc sssd g g sdg gg gf into abc sssd g g sdg gg gf");
std::string o;
std::copy_if(std::begin(s), std::end(s), std::back_inserter(o), if_not_prev_space());
std::cout << o << std::endl;
}
答案 4 :(得分:2)
对于就地修改,您可以应用erase-remove技术:
#include <string>
#include <iostream>
#include <algorithm>
#include <cctype>
int main()
{
std::string input {"asfa sas f f dgdgd dg ggg"};
bool prev_is_space = true;
input.erase(std::remove_if(input.begin(), input.end(), [&prev_is_space](char curr) {
bool r = std::isspace(curr) && prev_is_space;
prev_is_space = std::isspace(curr);
return r;
}), input.end());
std::cout << input << "\n";
}
因此,您首先将所有额外空格移动到字符串的末尾,然后截断它。
C ++的巨大优势在于它足够通用,可以将代码移植到只有少量修改的普通c静态字符串:
void erase(char * p) {
// note that this ony works good when initial array is allocated in the static array
// so we do not need to rearrange memory
*p = 0;
}
int main()
{
char input [] {"asfa sas f f dgdgd dg ggg"};
bool prev_is_space = true;
erase(std::remove_if(std::begin(input), std::end(input), [&prev_is_space](char curr) {
bool r = std::isspace(curr) && prev_is_space;
prev_is_space = std::isspace(curr);
return r;
}));
std::cout << input << "\n";
}
这里有趣的remove
步骤是字符串表示独立的。它可以与std::string
一起使用而不需要修改。
答案 5 :(得分:1)
由于你正在编写c风格,这是一种做你想做的事情的方法。
请注意,您可以删除换行符'\r'
和'\n'
(当然,如果您考虑这些空格,那么这取决于您)。
这个函数应该比任何其他替代方法更快或更快,即使用std :: strings调用它也没有内存分配(我已经超载了它)。
char temp[] = " alsdasdl gasdasd ee";
remove_whitesaces(temp);
printf("%s\n", temp);
int remove_whitesaces(char *p)
{
int len = strlen(p);
int new_len = 0;
bool space = false;
for (int i = 0; i < len; i++)
{
switch (p[i])
{
case ' ': space = true; break;
case '\t': space = true; break;
case '\n': break; // you could set space true for \r and \n
case '\r': break; // if you consider them spaces, I just ignore them.
default:
if (space && new_len > 0)
p[new_len++] = ' ';
p[new_len++] = p[i];
space = false;
}
}
p[new_len] = '\0';
return new_len;
}
// and you can use it with strings too,
inline int remove_whitesaces(std::string &str)
{
int len = remove_whitesaces(&str[0]);
str.resize(len);
return len; // returning len for consistency with the primary function
// but u can return std::string instead.
}
// again no memory allocation is gonna take place,
// since resize does not not free memory because the length is either equal or lower
如果你简单地看一下C ++标准库,你会注意到很多返回std :: string的C ++函数,或者其他std :: objects基本上都是一个写得很好的extern的包装器&#34; C& #34;功能。所以不要害怕在C ++应用程序中使用C函数,如果编写得好,你可以重载它们以支持std :: strings等。
例如,在Visual Studio 2015中,std::to_string
的编写完全如下:
inline string to_string(int _Val)
{ // convert int to string
return (_Integral_to_string("%d", _Val));
}
inline string to_string(unsigned int _Val)
{ // convert unsigned int to string
return (_Integral_to_string("%u", _Val));
}
和_Integral_to_string是C函数sprintf_s的包装器
template<class _Ty> inline
string _Integral_to_string(const char *_Fmt, _Ty _Val)
{ // convert _Ty to string
static_assert(is_integral<_Ty>::value,
"_Ty must be integral");
char _Buf[_TO_STRING_BUF_SIZE];
int _Len = _CSTD sprintf_s(_Buf, _TO_STRING_BUF_SIZE, _Fmt, _Val);
return (string(_Buf, _Len));
}
答案 6 :(得分:1)
我有一种沉闷的感觉,那就是'scanf'会做得很好(事实上,这是C学校相当于Anatoly的C ++解决方案):
void remove_extra_whitespaces(char* input, char* output)
{
int srcOffs = 0, destOffs = 0, numRead = 0;
while(sscanf(input + srcOffs, "%s%n", output + destOffs, &numRead) > 0)
{
srcOffs += numRead;
destOffs += strlen(output + destOffs);
output[destOffs++] = ' '; // overwrite 0, advance past that
}
output[destOffs > 0 ? destOffs-1 : 0] = '\0';
}
我们利用scanf
具有神奇的内置空间跳过功能的事实。然后,我们使用可能不太知名的%n
“转换”规范,该规范为我们提供了scanf
消耗的字符数量。这个功能在读取字符串时经常会派上用场,就像这里一样。使这个解决方案不完美的苦涩是对输出的strlen
调用(不幸的是,没有“我实际上只有多少字节写”转换说明符)。
最近使用scanf很简单,因为output
保证存在足够的内存;如果不是这种情况,由于缓冲和溢出处理,代码会变得更加复杂。
答案 7 :(得分:0)
这里有一个很长(但很简单)的解决方案,它不使用指针。 它可以进一步优化,但它可以工作。
#include <iostream>
#include <string>
using namespace std;
void removeExtraSpace(string str);
int main(){
string s;
cout << "Enter a string with extra spaces: ";
getline(cin, s);
removeExtraSpace(s);
return 0;
}
void removeExtraSpace(string str){
int len = str.size();
if(len==0){
cout << "Simplified String: " << endl;
cout << "I would appreciate it if you could enter more than 0 characters. " << endl;
return;
}
char ch1[len];
char ch2[len];
//Placing characters of str in ch1[]
for(int i=0; i<len; i++){
ch1[i]=str[i];
}
//Computing index of 1st non-space character
int pos=0;
for(int i=0; i<len; i++){
if(ch1[i] != ' '){
pos = i;
break;
}
}
int cons_arr = 1;
ch2[0] = ch1[pos];
for(int i=(pos+1); i<len; i++){
char x = ch1[i];
if(x==char(32)){
//Checking whether character at ch2[i]==' '
if(ch2[cons_arr-1] == ' '){
continue;
}
else{
ch2[cons_arr] = ' ';
cons_arr++;
continue;
}
}
ch2[cons_arr] = x;
cons_arr++;
}
//Printing the char array
cout << "Simplified string: " << endl;
for(int i=0; i<cons_arr; i++){
cout << ch2[i];
}
cout << endl;
}
答案 8 :(得分:0)
我最终在这里遇到了一个非常不同的问题。由于我不知道还有什么地方可以把它放在哪里,而且我发现了什么是错的,我在这里分享。请不要和我交叉。 我有一些字符串可以在它们的末端打印额外的空格,同时在调试中显示没有空格。在窗口中形成的字符串调用类似于VerQueryValue(),除了其他东西之外还输出字符串长度,例如,以下行中的iProductNameLen将结果转换为名为strProductName的字符串:
strProductName = string((LPCSTR)pvProductName, iProductNameLen)
然后生成了一个末尾带有\ 0字节的字符串,它在调试器中不易显示,但在屏幕上显示为空格。我将这个解决方案作为一个练习,因为一旦你意识到这一点,它就一点也不难。
答案 9 :(得分:0)
您可以使用std::unique根据定义如何使两个元素相等的方式将相邻重复项简化为单个实例。
如果两个元素都是空白字符,则在此处将它们定义为相等:
inline std::string& remove_extra_ws_mute(std::string& s)
{
s.erase(std::unique(std::begin(s), std::end(s), [](char a, char b){
return std::isspace(a) && std::isspace(b);
}), std::end(s));
return s;
}
inline std::string remove_extra_ws_copy(std::string s)
{
return remove_extra_ws_mute(s);
}
std::unique将重复项移到字符串的末尾,并将迭代器返回到它们的开头,以便将其删除。
此外,如果您必须使用低级字符串,则仍可以在指针上使用std::unique:
char* remove_extra_ws(char const* s)
{
std::size_t len = std::strlen(s);
char* buf = new char[len + 1];
std::strcpy(buf, s);
// Note that std::unique will also retain the null terminator
// in its correct position at the end of the valid portion
// of the string
std::unique(buf, buf + len + 1, [](char a, char b){
return (a && std::isspace(a)) && (b && std::isspace(b));
});
return buf;
}
答案 10 :(得分:0)
我不知道这是否有帮助,但这是我在家庭作业中所做的事情。唯一可能中断的情况是在字符串EX“ wor ds”的开头有空格时,在这种情况下,它将更改为“ wor ds”
void ShortenSpace(string &usrStr){
char cha1;
char cha2;
for (int i = 0; i < usrStr.size() - 1; ++i) {
cha1 = usrStr.at(i);
cha2 = usrStr.at(i + 1);
if ((cha1 == ' ') && (cha2 == ' ')) {
usrStr.erase(usrStr.begin() + 1 + i);
--i;//edit: was ++i instead of --i, made code not work properly
}
}
}
答案 11 :(得分:-1)
在不使用任何内置函数的情况下删除多余空格的简单程序。
switch_to.frame()