J'ai essayé d'écrire un script qui supprime les espaces blancs mais je n'ai pas réussi à le finir.
En gros, je veux transformer abc sssd g g sdg gg gf
en abc sssd g g sdg gg gf
.
Dans des langages comme PHP ou C #, ce serait très facile, mais pas en C++, à ce que je vois. Ceci est mon code:
#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;
}
Ça ne marche pas J'ai essayé plusieurs méthodes. Ce que je cherche à faire est d’itérer la chaîne lettre par lettre et de la transférer dans une autre chaîne tant qu’il n’ya qu’un espace dans une ligne; s'il y a deux espaces, n'écrivez pas le deuxième caractère dans la nouvelle chaîne.
Comment puis-je résoudre ça?
Voici une solution simple, non-C++ 11, utilisant la même signature remove_extra_whitespace()
que dans la question:
#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;
}
Sortie:
input: asfa sas f f dgdgd dg ggg
output: asfa sas f f dgdgd dg ggg
Il y a déjà beaucoup de solutions de Nice. Je vous propose une alternative basée sur un <algorithm>
dédié destiné à éviter les doublons consécutifs: 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;
}
Voici un D&EACUTE;MO EN DIRECT. Notez que je suis passé des chaînes de style c aux chaînes C++ plus sûres et plus puissantes.
Edit: si votre code nécessite de conserver des chaînes de style c, vous pouvez utiliser presque le même code mais avec des pointeurs au lieu d'itérateurs. C'est la magie du C++. Voici une autre démo live .
Puisque vous utilisez C++, vous pouvez tirer parti des fonctionnalités de bibliothèque standard conçues pour ce type de travail. Vous pouvez utiliser std::string
(au lieu de char[0x255]
) et std::istringstream
, qui remplacera l'essentiel de l'arithmétique du pointeur.
Commencez par créer un flux de chaîne:
std::istringstream stream(input);
Ensuite, lisez les chaînes de celui-ci. Cela supprimera automatiquement les délimiteurs d'espaces:
std::string Word;
while (stream >> Word)
{
...
}
Dans la boucle, construisez votre chaîne de sortie:
if (!output.empty()) // special case: no space before first Word
output += ' ';
output += Word;
L'inconvénient de cette méthode est qu'elle alloue de la mémoire de manière dynamique (y compris plusieurs réaffectations, effectuées lorsque la chaîne de sortie augmente).
Il existe de nombreuses façons de le faire (par exemple, en utilisant des expressions régulières), mais vous pouvez le faire en utilisant std::copy_if
avec un foncteur avec état en rappelant si le dernier caractère était un espace:
#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;
}
pour une modification sur place, vous pouvez appliquer la technique d'effacement et de suppression:
#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";
}
Ainsi, vous déplacez d’abord tous les espaces supplémentaires à la fin de la chaîne, puis vous le tronquez.
Le grand avantage du C++ est qu’il est suffisamment universel pour porter votre code dans des chaînes plain-c-static avec seulement peu modifications:
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";
}
L’étape remove
assez intéressante ici est indépendante de la représentation sous forme de chaîne. Cela fonctionnera avec std::string
sans aucune modification.
J'ai l'impression que le bon vieux scanf fera l'affaire (en fait, c'est l'équivalent C de la solution d'Anatoly en 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';
}
Nous exploitons le fait que scanf
possède des fonctionnalités magiques de saut d’espace intégrées. Nous utilisons ensuite la spécification %n
"conversion", peut-être moins connue, qui indique la quantité de caractères consommés par scanf
. Cette fonctionnalité est souvent utile lors de la lecture de chaînes, comme ici. La goutte amère qui rend cette solution moins que parfaite est l’appel strlen
sur la sortie (il n’existe malheureusement pas d'indicateur de conversion "Combien d'octets ai-je en fait juste écrit").
Enfin, l'utilisation de scanf est facile ici car il est garanti que suffisamment de mémoire existe à output
; si ce n'était pas le cas, le code deviendrait plus complexe en raison de la mise en mémoire tampon et de la gestion des débordements.
Puisque vous écrivez en style C, voici une façon de faire ce que vous voulez ... Notez que vous pouvez supprimer les '\r'
et '\n'
qui sont des sauts de ligne (mais bien sûr, cela vous appartient si vous considérez ces espaces ou non).
Cette fonction devrait être aussi rapide ou rapide que toute autre alternative et aucune allocation de mémoire n’a lieu même quand elle est appelée avec std :: strings (je l’ai surchargée).
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
Si vous examinez brièvement la bibliothèque C++ Standard, vous remarquerez que beaucoup de fonctions C++ qui renvoient std :: string, ou d’autres objets std :: sont en fait des enveloppes pour une fonction "C" externe bien écrite. Donc n'ayez pas peur d'utiliser les fonctions C dans les applications C++, si elles sont bien écrites et que vous pouvez les surcharger pour supporter std :: strings et autres.
Par exemple, dans Visual Studio 2015, std::to_string
est écrit exactement comme ceci:
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));
}
et _Integral_to_string est une enveloppe pour une fonction 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));
}
Programme simple pour supprimer les espaces blancs supplémentaires sans utiliser aucune fonction intégrée.
#include<iostream>
#include<string.h>
#include<stdio.h>
using namespace std;
int main()
{
char str[1200];
int i,n,j,k, pos = 0 ;
cout<<"Enter string:\n";
gets(str);
n = strlen(str);
for(i =0;i<=n;i++)
{
if(str[i] == ' ')
{
for(j= i+1;j<=n;j++)
{
if(str[j] != ' ')
{
pos = j;
break;
}
}
if(pos != 0 && str[pos] != ' ')
{
for(k =i+1;k< pos;k++)
{ if(str[pos] == ' ')
break;
else{
str[k] = str[pos];
str[pos] = ' ';
pos++;
}
}
}
}
}
puts(str);
}
Je me suis retrouvé ici pour un problème légèrement différent. Puisque je ne sais pas où le mettre ailleurs et que j'ai découvert ce qui n'allait pas, je le partage ici. Ne vous fâchez pas avec moi, s'il vous plaît ... J'ai quelques chaînes qui imprimaient des espaces supplémentaires à leurs extrémités, tout en apparaissant sans espaces lors du débogage. Les chaînes formées dans les appels Windows, telles que VerQueryValue (), qui, à côté d’autres éléments, génère une longueur de chaîne, comme par exemple. iProductNameLen dans la ligne suivante convertissant le résultat en une chaîne nommée strProductName:
strProductName = string((LPCSTR)pvProductName, iProductNameLen)
puis produit une chaîne avec un octet\0 à la fin, qui ne s’affiche pas facilement dans le débogueur, mais est affichée à l’écran sous forme d’espace. Je laisserai la solution de ceci comme un exercice, car ce n'est pas difficile du tout, une fois que vous en êtes conscient.
Voici une solution longue (mais facile) qui n’utilise pas de pointeur . Elle peut être optimisée mais bon, ça marche.
#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;
}