How to reverse a sentence in C and Perl - c

If the sentence is
"My name is Jack"
then the output should be
"Jack is name My".
I did a program using strtok() to separate the words and then push them onto a stack,
popping them and concatenating.
Is there any other, more efficient way than this?
Is it easier to do in Perl?

Whether it is more efficient or not will be something you can test but in Perl you could do something along the lines of:
my $reversed = join( " ", reverse( split( / /, $string ) ) );

Perl makes this kind of text manipulation very easy, you can even test this easily on the shell:
echo "run as fast as you can" | perl -lne 'print join $",reverse split /\W+/'
or:
echo "all your bases are belong to us" | perl -lne '#a=reverse /\w+/g;print "#a"'

The strategy for C could be this:
1) Reverse the characters of the string. This results in the words being the right general position, albeit backward.
2) Reverse the characters of each word in the string.
We will need one function to reverse characters in a buffer:
/*
* Reverse characters in a buffer.
*
* If provided "My name is Jack", modifies the input to become
* "kcaJ si eman yM".
*/
void reverse_chars(char * buf, int cch_len)
{
char * front = buf, *back = buf + cch_len - 1;
while (front < back)
{
char tmp = *front;
*front = *back;
*back = tmp;
front ++;
back --;
}
}
For the purpose of breaking the input buffer into words, a function which returns the number of non-space characters in the buffer. (strtok() modifies the buffer and is harder to use in-place)
int word_len(char *input)
{
char * p = input;
while (*p && !isspace(*p))
p++;
return p - input;
}
Finally, we will need a function which uses those two helpers to achieve the strategy described in the first paragraph.
/*
* Reverse words in a buffer.
*
* Given the input "My name is Jack", modifies the input to become
* "Jack is name My"
*/
void reverse_words(char *input)
{
int cch_len = strlen(input);
/* Part 1: Reverse the string characters. */
reverse_chars(input, cch_len);
char * p = input;
/* Part 2: Loop over one word at a time. */
while (*p)
{
/* Skip leading spaces */
while (*p && isspace(*p))
p++;
if (*p)
{
/* Advance one complete word. */
int cch_word = word_len(p);
reverse_chars(p, cch_word);
p += cch_word;
}
}
}

You've gotten a couple of versions in C, but they strike me as a bit more verbose than is probably really necessary. Absent a reason to do otherwise, I'd consider something like this:
#define MAX 32
char *words[MAX];
char word[256];
int pos = 0;
for (pos=0; pos<MAX && scanf("%255s", word); pos++)
words[pos] = strdup(word);
while (--pos >= 0)
printf("%s ", words[pos]);
One possible "intermediate" level between C and Perl would be C++:
std::istringstream input("My name is Jack");
std::vector<std::string> words((std::istream_iterator<std::string>(input)),
std::istream_iterator<std::string>());
std::copy(words.rbegin(), words.rend(),
std::ostream_iterator<std::string>(std::cout, " "));

Here is a C idea that uses a little recursion to do the stacking for you:
void rev(char * x){
char * p;
if(p = strchr(x, ' ')){
rev(p+1);
printf("%.*s ", p-x, x);
}
else{
printf("%s ", x);
}
}

Some fun with a little help from regexp and perl special variables :)
$_ = "My name is Jack";
unshift #_, "$1 " while /(\w+)/g;
print #_;
EDIT
And a killer (by now):
$,=' ';print reverse /\w+/g;
Little explanation: $, is special variable for print output separator. Of course you can do it in shorter way without this special var:
print reverse /\w+ ?/g;
but the result might be not as satisfactiry as example above.

Using reverse:
my #words = split / /, $sentence;
my $newSentence = join(' ', reverse #words);

It's probably a lot easier to do in Perl, but...
char *strrtok(char *str, const char *delim)
{
int i, j;
for (i = strlen(str) - 1; i > 0; i--)
{
// Sets the furthest set of contiguous delimiters to null characters
if (strchr(delim, str[i]))
{
j = i + 1;
while (strchr(delim, str[i]) && i >= 0)
{
str[i] = '\0';
i--;
}
return &(str[j]);
}
}
return str;
}
This should work similarly to strtok() in reverse, but you continue to pass the pointer to the original string location rather than passing NULL after the first call. Also, you should get empty strings for start and end cases.

C version:
#include <string.h>
int main()
{
char s[] = "My name is Jack";
char t[100];
int i = 0, j = 0, k = 0;
for(i = strlen(s) - 1 ; i >= 0 ;i--)
{
if(s[i] == ' ' || i == 0)
{
j = i == 0 ? i : i + 1;
for(j = j; s[j] != '\0'; j++) t[k++] = s[j];
t[k++] = ' ';
s[i] = '\0';
}
}
t[k] = '\0';
printf("%s\n", t);
return 0;
}

C example
char * srtrev (char * str) {
int l = strlen(str);
char * rev;
while(l != 0)
{
rev += str[ --l];
}
return rev;
}

Related

Eject excess space from string in C

I need to write a function which will eject excess space from string in C.
Example:
char s[]=" abcde abcde ";
OUTPUT:
"abcde abcde"
Code:
#include <stdio.h>
#include <ctype.h>
char *eject(char *str) {
int i, x;
for (i = x = 0; str[i]; ++i)
if (!isspace(str[i]) || (i > 0 && !isspace(str[i - 1])))
str[x++] = str[i];
if(x > 0 && str[x-1] == ' ') str[x-1] = '\0';
return str;
}
int main() {
char s[] = " abcde abcde ";
printf("\"%s\"", eject(s));
return 0;
}
This code doesn't work for string " "
If this string is found program should print:
""
How to fix this?
Basically, you need to remove the consecutive space characters between the words in the input string and all leading and trailing space characters of the input string. That means, write code to remove the consecutive space characters in the input string and while removing the consecutive space characters, remove the leading and trailing space characters completely.
You can do it in just one iteration. No need to write the different functions for removing the leading and trailing spaces of input string, as shown in the other post.
You can do:
#include <stdio.h>
#include <ctype.h>
char * eject (char *str) {
if (str == NULL) {
printf ("Invalid input..\n");
return NULL;
}
/* Pointer to keep track of position where next character to be write
*/
char * p = str;
for (unsigned int i = 0; str[i] ; ++i) {
if ((isspace (str[i])) && ((p == str) || (str[i + 1] == '\0') || (str[i] == (str[i + 1])))) {
continue;
}
*p++ = str[i];
}
/* Add the null terminating character.
*/
*p = '\0';
return str;
}
int main (void) {
char s[] = " abcde abcde ";
printf("\"%s\"\n", eject(s));
char s1[] = " ";
printf("\"%s\"\n", eject(s1));
char s2[] = "ab yz ";
printf("\"%s\"\n", eject(s2));
char s3[] = " ddd xx jj m";
printf("\"%s\"\n", eject(s3));
char s4[] = "";
printf("\"%s\"\n", eject(s4));
return 0;
}
Output:
# ./a.out
"abcde abcde"
""
"ab yz"
"ddd xx jj m"
""
You could write two functions which trim leading and trailing whitespace characters.
void trim_front(char *src) {
size_t i = 0, j = 0;
while (isspace(src[i])) i++;
while (i < strlen(src)) src[j++] = src[i++];
src[j] = '\0';
}
void trim_back(char *src) {
char *ch = src + strlen(src) - 1;
while (isspace(*ch)) *ch-- = '\0';
}
If you know you don't have to deal with trailing or leading spaces, your task becomes much simpler.
void reduce_spaces(char *src) {
size_t i = 0, j = 0;
for (; i < strlen(src); ++i) {
if (i == strlen(src) - 1 ||
(isspace(src[i]) && !isspace(src[i + 1])) ||
!isspace(src[i])) {
src[j++] = src[i];
}
}
src[j] = '\0';
}
And testing this:
int main(void) {
char s[] = " hello world ";
trim_front(s);
trim_back(s);
reduce_spaces(s);
printf(">%s<\n", s);
return 0;
}
% gcc test.c
% ./a.out
>hello world<
%
Of course, if you really want to, you can transplant the code from those functions into reduce_spaces, but decomposing a problem into multiple smaller problems can make things much easier.
A slightly more advanced answer just for reference - suppose you were tasked in writing a professional library for the use in real world programs. Then one would first list all requirements that make sense:
It's good practice to treat strings as "immutable" - that is, build up a new string based on the old one rather than in-place replacement.
Take the destination string as parameter but also return a pointer to it (similar to strcpy etc functions).
In case of empty strings, set the destination string empty too.
Remove all "white space" not just the ' ' character.
Instead of always inserting a space character after each word, why not insert a variable delimiter? Might as well be something like , or ;.
No delimiter should be inserted after the last word.
The algorithm should only traverse the data once for performance reasons. That is, internal calls like strlen etc are unacceptable.
Byte by byte iteration is fine - we need not care about alignment.
Then we might come up with something like this:
#include <ctype.h>
#include <stdbool.h>
#include <stdio.h>
char* trim_delimit (char* restrict dst, const char* restrict src, char delim)
{
char* start = dst;
*dst = '\0';
bool remove_spaces = true;
char* insert_delim_pos = NULL;
for(; *src != '\0'; src++)
{
if(remove_spaces)
{
if(!isspace(*src))
{
remove_spaces = false;
if(insert_delim_pos != NULL)
{
// we only get here if more words were found, not yet at the end of the string
*insert_delim_pos = delim;
insert_delim_pos = NULL;
}
}
}
if(!remove_spaces)
{
if(isspace(*src))
{
remove_spaces = true;
insert_delim_pos = dst; // remember where to insert delimiter for later
}
else
{
*dst = *src;
}
dst++;
}
}
return start;
}
Test cases:
int main (void)
{
char s[]=" abcde abcde ";
char trimmed[100];
puts(trim_delimit(trimmed, s, ' '));
puts(trim_delimit(trimmed, "", ' '));
puts(trim_delimit(trimmed, s, ';'));
}
Output:
abcde abcde
abcde;abcde

How to Replace Leading or Trailing Blank Characters with "X"

Looking for a more efficient way to replace leading and trailing empty spaces (' ') and appending an 'X' to the front for each empty space.. It seems to work ok for trailing spaces but I'd like to know if there's a better / simpler way of going about this that I am missing.
Example:
Passed in string: '12345 '
Desired result 'XXXXX12345'
Removed 5 empty spaces and append 5 'X's to front.
Example:
Passed in string: ' 12345'
Desired result 'XX12345'
Remove 2 empty spaces and append 2 'X's to front.
void fixStr(char* str)
{
int i = 0;
int length = strlen(str);
char strCopy[10];
strcpy(strCpy, str);
for(i = 0; i < length; i++)
{
if(strCopy[i] == ' ')
{
strCopy[i] = '\0';
str[i] = '\0';
break;
}
}
for(i = 0; i < length - i + 2; i++)
{
str[i] = 'X';
str[i + 1] = '\0';
}
strcat(str, strCopy);
}
One way to achieve this is to find out the leading non-space position & trailing non-space position of the string, and then move the content in-between (leading nonspace, trailing nonspace) this to end of the string, then set all the empty space at the beginning to 'x'
This way you can get the expected output (function below)
void fixStr(char* str)
{
int i = 0;
int length = strlen(str);
int leadindex = length;
int tailindex = 0;
// First find the leading nonspace position
for(i = 0; i < length; i++)
{
if(str[i] != ' ')
{
leadindex = i;
break;
}
}
// if not found nonspace then no change
if( leadindex == length )
{
// all spaces, so no change required;
return;
}
// Find the trailing nonspace position
for(i = length - 1; i >= 0 ; i--)
{
if(str[i] != ' ')
{
tailindex = i;
break;
}
}
// move the buffer (in place) to exclude trailing spaces
memmove(str + (length - tailindex -1),str,(tailindex +1) );
// set the 'x' to all empty spaces at leading ( you may use for loop to set this)
memset(str, 'X', length - (tailindex - leadindex + 1) );
}
To solve a problem the engineer's way:
Define the needs.
Know your tools.
Use the tools as simple as possible, as accurate as necessary to make up a solution.
In your case:
Needs:
find the number of trailing spaces
move content of string to the end
set beginning to 'X's
Tools:
to measure, iterate, compare and count
to move a block of memory
to initialise a block of memory
Example for a solution:
#include <string.h> /* for strlen(), memmove () and memset() */
void fix_str(char * s)
{
if ((NULL != s) && ('\0' != *s)) /* Ignore NULL and empty string! */
{
/* Store length and initialise counter: */
size_t l = strlen(s), i = l;
/* Count space(s): */
for (; (0 != i) && (' ' == s[i-1]); --i); /* This for loop does not need a "body". */
/* Calculate the complement: */
size_t c = l - i;
/* Move content to the end overwriting any trailing space(s) counted before hand: */
memmove(s+c, s, i); /* Note that using memmove() instead of memmcpy() is essential
here as the source and destination memory overlap! */
/* Initialise the new "free" characters at the beginning to 'X's:*/
memset(s, 'X', c);
}
}
I didn't fix your code but you could use sprintf in combination with isspace, something along the lines of this. Also, remember to make a space for the '\0 at the end of your string. Use this idea and it should help you:
#include <ctype.h>
#include <stdio.h>
int main()
{
char buf[11];
char *s = "Hello";
int i;
sprintf(buf, "%10s", s); /* right justifies in a column of 10 in buf */
for(i = 0; i < 10; i++) {
if(isspace(buf[i])) /* replace the spaces with an x (or whatever) */
buf[i] = 'x';
}
printf("%s\n", buf);
return 0;
}

add additional letters in a string if there are two same letters beside each other

I'm trying to add an additional letter if there are two equal letters beside each other.
That's what I was thinking, but it doesn't put in an x between the two letters; instead of that, it copies one of the double letters, and now I have, for example, MMM instead of MXM.
for (index_X = 0; new_text[index_X] != '\0'; index_X++)
{
if (new_text[index_X] == new_text[index_X - 1])
{
double_falg = 1;
}
text[index_X] = new_text[index_X];
}
if (double_falg == 1)
{
for (counter_X = 0; text[counter_X] != '\0'; counter_X++)
{
transfer_X = counter_X;
if (text[transfer_X - 1] == text[transfer_X])
{
text_X[transfer_X] = 'X';
cnt_double++;
printf("%c\n", text[transfer_X]);
}
text_X[transfer_X] = text[transfer_X - cnt_double];
}
printf("%s\n", text_X);
}
If you're trying to create the modified array in text_X, copying data from new_text and putting an X between adjacent repeated letters (ignoring the possibility that the input contains XX), then you only need:
char new_text[] = "data with appalling repeats";
char text_X[SOME_SIZE];
int out_pos = 0;
for (int i = 0; new_text[i] != '\0'; i++)
{
text_X[out_pos++] = new_text[i];
if (new_text[i] == new_text[i+1])
text_X[out_pos++] = 'X';
}
text_X[out_pos] = '\0';
printf("Input: [%s]\n", new_text);
printf("Output: [%s]\n", text_X);
When wrapped in a basic main() function (and enum { SOME_SIZE = 64 };), that produces:
Input: [data with appalling repeats]
Output: [data with apXpalXling repeats]
To deal with repeated X's in the input, you could use:
text_X[out_pos++] = (new_text[i] == 'X') ? 'Q' : 'X';
It seems that your approach is more complicated than needed - too many loops and too many arrays involved. A single loop and two arrays should do.
The code below iterates the original string with idx to track position and uses the variable char_added to count how many extra chars that has been added to the new array.
#include <stdio.h>
#define MAX_LEN 20
int main(void) {
char org_arr[MAX_LEN] = "aabbcc";
char new_arr[MAX_LEN] = {0};
int char_added = 0;
int idx = 1;
new_arr[0] = org_arr[0];
if (new_arr[0])
{
while(org_arr[idx])
{
if (org_arr[idx] == org_arr[idx-1])
{
new_arr[idx + char_added] = '*';
++char_added;
}
new_arr[idx + char_added] = org_arr[idx];
++idx;
}
}
puts(new_arr);
return 0;
}
Output:
a*ab*bc*c
Note: The code isn't fully tested. Also it lacks out-of-bounds checking.
There is a lot left to be desired in your Minimal, Complete, and Verifiable Example (MCVE) (MCVE). However, that said, what you will need to do is fairly straight-forward. Take a simple example:
"ssi"
According to your statement, you need to add a character between the adjacent 's' characters. (you can use whatever you like for the separator, but if your input are normal ASCII character, then you can set the current char to the next ASCII character (or subtract one if current is the last ASCII char '~')) See ASCII Table and Description.
For example, you could use memmove() to shift all characters beginning with the current character up by one and then set the current character to the replacement. You also need to track the current length so you don't write beyond your array bounds.
A simple function could be:
#include <stdio.h>
#include <string.h>
#define MAXC 1024
char *betweenduplicates (char *s)
{
size_t len = strlen(s); /* get length to validate room */
if (!len) /* if empty string, nothing to do */
return s;
for (int i = 1; s[i] && len + 1 < MAXC; i++) /* loop until end, or out of room */
if (s[i-1] == s[i]) { /* adjacent chars equal? */
memmove (s + i + 1, s + i, len - i + 1); /* move current+ up by one */
if (s[i-1] != '~') /* not last ASCII char */
s[i] = s[i-1] + 1; /* set to next ASCII char */
else
s[i] = s[i-1] - 1; /* set to previous ASCII char */
len += 1; /* add one to len */
}
return s; /* convenience return so it can be used immediately if needed */
}
A short example program taking the string to check as the first argument could be:
int main (int argc, char **argv) {
char str[MAXC];
if (argc > 1) /* if argument given */
strcpy (str, argv[1]); /* copy to str */
else
strcpy (str, "mississippi"); /* otherwise use default */
puts (str); /* output original */
puts (betweenduplicates (str)); /* output result */
}
Example Use/Output
$ ./bin/betweenduplicated
mississippi
mistsistsipqpi
or when there is nothing to replace:
$ ./bin/betweenduplicated dog
dog
dog
Or checking the extremes:
$ ./bin/betweenduplicated "two spaces and alligators ~~"
two spaces and alligators ~~
two ! spaces ! and ! almligators ! ~}~
There are a number of ways to approach it. Let me know if you have further questions.

Reading a string character by character in C

I am trying to read a string character by character in C. Since there is no string class, there are no functions to help this. Here is what i want to do: I have,
char m[80]; //I do some concenation, finally m is:
m= 12;256;2;
Now, i want to count how many characters are there between the semicolumns. In this example, there are 2,4 and 1 characters respectively. How can do this?
Thank you
What do you mean "there are no functions to help this"? There are. If you want to read a string, check out the function fgets.
On to the problem at hand, let's say you have this:
char m[80] = "12;256;2";
And you want to count the characters between the semi-colons. The easiest way is to use strchr.
char *p = m;
char *pend = m + strlen(m);
char *plast;
int count;
while( p != NULL ) {
plast = p;
p = strchr(p, ';');
if( p != NULL ) {
// Found a semi-colon. Count characters and advance to next char.
count = p - plast;
p++;
} else {
// Found no semi-colon. Count characters to the end of the string.
count = pend - p;
}
printf( "Number of characters: %d\n", count );
}
Well I'm not sure were supposed to write the code for you here, just correct it. But...
int strcount, charcount = 0, numcharcount = 0, num_char[10] = 0;
//10 or how many segments you expect
for (strcount = 0; m[strcount] != '\0'; strcount++) {
if (m[strcount] == ';') {
num_char[numcharcount++] = charcount;
charcount = 0;
} else {
charcount++;
}
}
This will store the amount of each character between the ; in an array.
It is kind of sloppy I'll admit but it will work for what you asked.
If you don't mind modifying your string then the easiest way is to use strtok.
#include <string.h>
#include <stdio.h>
int main(void) {
char m[80] = "12;256;2;";
char *p;
for (p = strtok(m, ";"); p; p = strtok(NULL, ";"))
printf("%s = %u\n", p, strlen(p));
}

C - Largest String From a Big One

So pray tell, how would I go about getting the largest contiguous string of letters out of a string of garbage in C? Here's an example:
char *s = "(2034HEY!!11 th[]thisiswhatwewant44";
Would return...
thisiswhatwewant
I had this on a quiz the other day...and it drove me nuts (still is) trying to figure it out!
UPDATE:
My fault guys, I forgot to include the fact that the only function you are allowed to use is the strlen function. Thus making it harder...
Uae strtok() to split your string into tokens, using all non-letter characters as delimiters, and find the longest token.
To find the longest token you will need to organise some storage for tokens - I'd use linked list.
As simple as this.
EDIT
Ok, if strlen() is the only function allowed, you can first find the length of your source string, then loop through it and replace all non-letter characters with NULL - basically that's what strtok() does.
Then you need to go through your modified source string second time, advancing one token at a time, and find the longest one, using strlen().
This sounds similar to the standard UNIX 'strings' utility.
Keep track of the longest run of printable characters terminated by a NULL.
Walk through the bytes until you hit a printable character. Start counting. If you hit a non-printable character stop counting and throw away the starting point. If you hit a NULL, check to see if the length of the current run is greater then the previous record holder. If so record it, and start looking for the next string.
What defines the "good" substrings compared to the many others -- being lowercase alphas only? (i.e., no spaces, digits, punctuation, uppercase, &c)?
Whatever the predicate P that checks for a character being "good", a single pass over s applying P to each character lets you easily identify the start and end of each "run of good characters", and remember and pick the longest. In pseudocode:
longest_run_length = 0
longest_run_start = longest_run_end = null
status = bad
for i in (all indices over s):
if P(s[i]): # current char is good
if status == bad: # previous one was bad
current_run_start = current_run_end = i
status = good
else: # previous one was also good
current_run_end = i
else: # current char is bad
if status == good: # previous one was good -> end of run
current_run_length = current_run_end - current_run_start + 1
if current_run_length > longest_run_length:
longest_run_start = current_run_start
longest_run_end = current_run_end
longest_run_length = current_run_length
status = bad
# if a good run ends with end-of-string:
if status == good: # previous one was good -> end of run
current_run_length = current_run_end - current_run_start + 1
if current_run_length > longest_run_length:
longest_run_start = current_run_start
longest_run_end = current_run_end
longest_run_length = current_run_length
Why use strlen() at all?
Here's my version which uses no function whatsoever.
#ifdef UNIT_TEST
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#endif
/*
// largest_letter_sequence()
// Returns a pointer to the beginning of the largest letter
// sequence (including trailing characters which are not letters)
// or NULL if no letters are found in s
// Passing NULL in `s` causes undefined behaviour
// If the string has two or more sequences with the same number of letters
// the return value is a pointer to the first sequence.
// The parameter `len`, if not NULL, will have the size of the letter sequence
//
// This function assumes an ASCII-like character set
// ('z' > 'a'; 'z' - 'a' == 25; ('a' <= each of {abc...xyz} <= 'z'))
// and the same for uppercase letters
// Of course, ASCII works for the assumptions :)
*/
const char *largest_letter_sequence(const char *s, size_t *len) {
const char *p = NULL;
const char *pp = NULL;
size_t curlen = 0;
size_t maxlen = 0;
while (*s) {
if ((('a' <= *s) && (*s <= 'z')) || (('A' <= *s) && (*s <= 'Z'))) {
if (p == NULL) p = s;
curlen++;
if (curlen > maxlen) {
maxlen = curlen;
pp = p;
}
} else {
curlen = 0;
p = NULL;
}
s++;
}
if (len != NULL) *len = maxlen;
return pp;
}
#ifdef UNIT_TEST
void fxtest(const char *s) {
char *test;
const char *p;
size_t len;
p = largest_letter_sequence(s, &len);
if (len && (len < 999)) {
test = malloc(len + 1);
if (!test) {
fprintf(stderr, "No memory.\n");
return;
}
strncpy(test, p, len);
test[len] = 0;
printf("%s ==> %s\n", s, test);
free(test);
} else {
if (len == 0) {
printf("no letters found in \"%s\"\n", s);
} else {
fprintf(stderr, "ERROR: string too large\n");
}
}
}
int main(void) {
fxtest("(2034HEY!!11 th[]thisiswhatwewant44");
fxtest("123456789");
fxtest("");
fxtest("aaa%ggg");
return 0;
}
#endif
While I waited for you to post this as a question I coded something up.
This code iterates through a string passed to a "longest" function, and when it finds the first of a sequence of letters it sets a pointer to it and starts counting the length of it. If it is the longest sequence of letters yet seen, it sets another pointer (the 'maxStringStart' pointer) to the beginning of that sequence until it finds a longer one.
At the end, it allocates enough room for the new string and returns a pointer to it.
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
int isLetter(char c){
return ( (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') );
}
char *longest(char *s) {
char *newString = 0;
int maxLength = 0;
char *maxStringStart = 0;
int curLength = 0;
char *curStringStart = 0;
do {
//reset the current string length and skip this
//iteration if it's not a letter
if( ! isLetter(*s)) {
curLength = 0;
continue;
}
//increase the current sequence length. If the length before
//incrementing is zero, then it's the first letter of the sequence:
//set the pointer to the beginning of the sequence of letters
if(curLength++ == 0) curStringStart = s;
//if this is the longest sequence so far, set the
//maxStringStart pointer to the beginning of it
//and start increasing the max length.
if(curLength > maxLength) {
maxStringStart = curStringStart;
maxLength++;
}
} while(*s++);
//return null pointer if there were no letters in the string,
//or if we can't allocate any memory.
if(maxLength == 0) return NULL;
if( ! (newString = malloc(maxLength + 1)) ) return NULL;
//copy the longest string into our newly allocated block of
//memory (see my update for the strlen() only requirement)
//and null-terminate the string by putting 0 at the end of it.
memcpy(newString, maxStringStart, maxLength);
newString[maxLength + 1] = 0;
return newString;
}
int main(int argc, char *argv[]) {
int i;
for(i = 1; i < argc; i++) {
printf("longest all-letter string in argument %d:\n", i);
printf(" argument: \"%s\"\n", argv[i]);
printf(" longest: \"%s\"\n\n", longest(argv[i]));
}
return 0;
}
This is my solution in simple C, without any data structures.
I can run it in my terminal like this:
~/c/t $ ./longest "hello there, My name is Carson Myers." "abc123defg4567hijklmnop890"
longest all-letter string in argument 1:
argument: "hello there, My name is Carson Myers."
longest: "Carson"
longest all-letter string in argument 2:
argument: "abc123defg4567hijklmnop890"
longest: "hijklmnop"
~/c/t $
the criteria for what constitutes a letter could be changed in the isLetter() function easily. For example:
return (
(c >= 'a' && c <= 'z') ||
(c >= 'A' && c <= 'Z') ||
(c == '.') ||
(c == ' ') ||
(c == ',') );
would count periods, commas and spaces as 'letters' also.
as per your update:
replace memcpy(newString, maxStringStart, maxLength); with:
int i;
for(i = 0; i < maxLength; i++)
newString[i] = maxStringStart[i];
however, this problem would be much more easily solved with the use of the C standard library:
char *longest(char *s) {
int longest = 0;
int curLength = 0;
char *curString = 0;
char *longestString = 0;
char *tokens = " ,.!?'\"()#$%\r\n;:+-*/\\";
curString = strtok(s, tokens);
do {
curLength = strlen(curString);
if( curLength > longest ) {
longest = curLength;
longestString = curString;
}
} while( curString = strtok(NULL, tokens) );
char *newString = 0;
if( longest == 0 ) return NULL;
if( ! (newString = malloc(longest + 1)) ) return NULL;
strcpy(newString, longestString);
return newString;
}
First, define "string" and define "garbage". What do you consider a valid, non-garbage string? Write down a concrete definition you can program - this is how programming specs get written. Is it a sequence of alphanumeric characters? Should it start with a letter and not a digit?
Once you get that figured out, it's very simple to program. Start with a naive method of looping over the "garbage" looking for what you need. Once you have that, look up useful C library functions (like strtok) to make the code leaner.
Another variant.
#include <stdio.h>
#include <string.h>
int main(void)
{
char s[] = "(2034HEY!!11 th[]thisiswhatwewant44";
int len = strlen(s);
int i = 0;
int biggest = 0;
char* p = s;
while (p[0])
{
if (!((p[0] >= 'A' && p[0] <= 'Z') || (p[0] >= 'a' && p[0] <= 'z')))
{
p[0] = '\0';
}
p++;
}
for (; i < len; i++)
{
if (s[i] && strlen(&s[i]) > biggest)
{
biggest = strlen(&s[i]);
p = &s[i];
}
}
printf("%s\n", p);
return 0;
}

Resources