Not printing new lines with printf - c

I am currently printing a string using printf("'%.*s'\n", length, start);, where start is a const char* and length is an int.
The string being printed sometimes contains newline characters \n which mess up the formatting, is it possible for them to be replaced with the characters \ and n in the printed output, instead of the \n character.
If not, could you help with a function that would replace the characters?
The string is a malloc'd string that is has other references from different pointers so it cannot be changed.
EDIT: I have written the following code which I think does what I need it to
static void printStr(const char* start, int length) {
char* buffer = malloc(length * sizeof(char));
int processedCount = 0;
for(int i = 0; i < length; i++) {
char c = start[i];
if(c == '\n') {
printf("%.*s\\n", processedCount, buffer);
processedCount = 0;
} else {
buffer[processedCount] = c;
processedCount++;
}
}
printf("%.*s", processedCount, buffer);
free(buffer);
}

There is no need to allocate memory to process the string. Simply, iterate through the original one and print the characters as required. For instance:
#include <stdio.h>
void print(const char * str, int length)
{
for (int i = 0; i < length; ++i) {
if (str[i] == '\n') {
putchar('\\');
putchar('n');
} else
putchar(str[i]);
}
}
int main()
{
print("hello\nworld!", 12);
return 0;
}

I would implement your custom print function slightly differently:
#include <stdlib.h>
#include <ctype.h>
#include <stdio.h>
#include <errno.h>
static int output_escaped(FILE *out, const char *str, const char *end)
{
int count = 0;
while (str < end)
switch (*str) {
case '\\': fputs("\\\\", out); count++; break;
case '\a': fputs("\\a", out); count++; break;
case '\b': fputs("\\b", out); count++; break;
case '\t': fputs("\\t", out); count++; break;
case '\n': fputs("\\n", out); count++; break;
case '\v': fputs("\\v", out); count++; break;
case '\f': fputs("\\f", out); count++; break;
case '\r': fputs("\\r", out); count++; break;
default:
if (isprint((unsigned char)(*str)))
fputc(*str, out);
else {
fprintf(out, "\\x%02x", (unsigned char)(*str));
count += 3; /* Note: incremented by one later */
}
}
str++;
count++;
}
return count;
}
with wrapper functions
int escape(const char *str)
{
if (!stdout || !str) {
errno = EINVAL;
return -1;
} else
return output_escaped(stdout, str, str + strlen(str));
}
int escapen(const char *str, const size_t len)
{
if (!stdout || !str) {
errno = EINVAL;
return -1;
} else
return output_escaped(stdout, str, str + len);
}
int fescape(FILE *out, const char *str)
{
if (!out || !str) {
errno = EINVAL;
return -1;
} else
return output_escaped(out, str, str + strlen(str));
}
int fescapen(FILE *out, const char *str, const size_t len)
{
if (!out || !str) {
errno = EINVAL;
return -1;
} else
return output_escaped(out, str, str + len);
}
The four wrappers cater for the cases when you want to print the entire thing, or just some len first characters, to stdout or a specific stream. All return the number of characters output, or -1 if an error occurs (and errno set).
Note that this prints \ as \\, and other non-printable characters using a hexadecimal escape sequence \x00 to \xFF. If you want octal (\001 through \377) rather than hexadecimal escapes, use
else {
fprintf(out, "\\%03o", (unsigned char)(*str));
count += 3; /* Note: incremented by one later */
}
instead.
The (unsigned char) casts ensure that the character value is never negative.

This is essentially the same thing, but it uses strchr in memory to see exactly how many chars we can write before an escape sequence, and fwrites them all in one. This may be faster then character-at-a-time output, (perhaps related https://stackoverflow.com/a/37991361/2472827.) (Not thoroughly tested.)
#include <stdlib.h> /* EXIT_ */
#include <stdio.h> /* perror fwrite fputs */
#include <string.h> /* strchr */
#include <assert.h>
/** Prints the string, escaping newlines to "\n"; a newline will be added at
the end.
#return A non-negative number, otherwise EOF; in the latter case, it shall
set an error indicator for the stream and (may) set {errno} (IEEE Std
1003.1-2001-conforming systems?) */
static int printStr(const char* start) {
const char *so_far = start, *nl;
size_t nchars;
assert(start);
while((nl = strchr(so_far, '\n'))) {
nchars = nl - so_far;
if(fwrite(so_far, 1, nchars, stdout) != nchars
|| fwrite("\\n", 1, 2, stdout) != 2) return EOF;
so_far = nl + 1;
}
/* The rest. */
return fputs(so_far, stdout) == EOF || fputc('\n', stdout) == EOF ? EOF : 1;
}
int main(void) {
return printStr("Lalala\n\nlalala\n\n") == EOF
? perror("string"), EXIT_FAILURE : EXIT_SUCCESS;
}
It doesn't require length, but you could put in by checking nchars, (if your string was not null-terminated?)

The code you posted in your question seems to work, except for the potential undefined behavior if memory allocation fails. It can be simplified: there is no need for a temporary buffer, you can print from the supplied array directly, avoiding allocation:
static void printStr(const char *s, int length) {
int i, j;
for (i = j = 0; i < length; i++) {
if (s[i] == '\n') {
printf("%.*s\\n", i - j, s + j);
}
}
if (i > j) {
printf("%.*s", i - j, s + j);
}
}

Related

Segmentation Fault when finding longest word in input

I have written a program to find the longest word in the input. I get no errors when using valgrind or running tests locally, but the grading program I email the code to reports a segmentation fault.
int main(void)
{
char *longest = malloc(1);
size_t size = 1;
do {
char word[20];
if (scanf("%s", word) > 0) {
if (strlen(word) > size) {
longest = realloc(longest,strlen(word)+1);
strcpy(longest,word);
size = strlen(word);
}
}
} while (getchar() != EOF);
printf("%zu characters in longest word: %s\n", strlen(longest),longest);
free(longest);
return 0;
}
Your problem is in the line char word[20]; and the way scanf reads words. From scanf's point of view, a word is any sequence of non-spaces. For example, realloc(longest,strlen(word)+1); is treated as one word, and that alone is longer than 20 characters.
You should use a more robust function to read words and allocate space for them. The most cost-efficient solution is getline() for reading the line followed by strsep() for extracting words.
Without relying on the luxurities of POSIX-functions, only standard-C for variable word-length:
#include <assert.h> // assert()
#include <stddef.h> // size_t
#include <stdbool.h> // bool, true, false
#include <stdlib.h> // EXIT_FAILURE, realloc(), free()
#include <stdio.h> // fscanf(), fgetc(), ungetc(), printf(), fputs()
#include <ctype.h> // isspace()
#include <string.h> // strlen(), strcat(), strcpy()
#define WORD_BUFFER_SIZE 20
#define STRING(value) #value
#define STRINGIFY(value) STRING(value)
// reads and discards whitespace, returns false on EOF
bool skip_ws(FILE *stream)
{
int ch;
while ((ch = fgetc(stream)) != EOF && isspace(ch));
if(!isspace(ch) && ch != EOF) // if it was not whitespace and not EOF
ungetc(ch, stream); // pretend we were never here.
return ch != EOF;
}
bool read_word(char **dst, FILE *stream)
{
assert(dst);
char chunk_buffer[WORD_BUFFER_SIZE + 1];
if (!skip_ws(stream)) // if we encounter EOF before any other non-whitespace
return false;
// read chunk by chunk
for (size_t i = 0; fscanf(stream, "%" STRINGIFY(WORD_BUFFER_SIZE) "s", chunk_buffer) == 1; ++i)
{
size_t chunk_length = strlen(chunk_buffer);
// adjust *dst's size
char *tmp = realloc(*dst, (i * WORD_BUFFER_SIZE + chunk_length + 1) * sizeof(*tmp));
if (!tmp) {
free(*dst);
*dst = NULL;
return false;
}
*dst = tmp;
if (i == 0) // zero terminate it if it is the first junk
**dst = '\0'; // for strcat() to behave well.
strcat(*dst, chunk_buffer); // add the current chunk to *dst.
int next_char = fgetc(stream);
ungetc(next_char, stream);
if (chunk_length < WORD_BUFFER_SIZE || isspace(next_char) || next_char == EOF)
return true;
}
return true;
}
int main(void)
{
char *word = NULL;
char *longest_word = NULL;
size_t longest_length = 0;
while (read_word(&word, stdin)) {
size_t length = strlen(word);
if (length > longest_length) {
char *tmp = realloc(longest_word, (length + 1) * sizeof *tmp);
if (!tmp) {
fputs("Not enough memory. :(\n\n", stderr);
free(longest_word);
free(word);
return EXIT_FAILURE;
}
longest_length = length;
longest_word = tmp;
strcpy(longest_word, word);
}
}
free(word);
printf("%zu characters in the longest word: \"%s\"\n\n", longest_length, longest_word);
free(longest_word);
}

Remove a word from a sentence (string)

I am in the stage of preparing myself for exams, and the thing that I m least proud of are my skills with strings. What I need to do is remove a word from a sentence, without using <string.h> library at all.
This is what I've got so far. It keeps showing me that certain variables are not declared, such as start and end.
#include <stdio.h>
/* Side function to count the number of letters of the word we wish to remove */
int count(char *s) {
int counter = 0;
while (*s++) {
counter++;
s--;
return counter;
}
/* Function to remove a word from a sentence */
char *remove_word(const char *s1, const char *s2) {
int counter2 = 0;
/* We must remember where the string started */
const char *toReturn = s1;
/* Trigger for removing the word */
int found = 1;
/* First we need to find the word we wish to remove [Don't want to
use string.h library for anything associated with the task */
while (*s1 != '\0') {
const char *p = s1;
const char *q = s2;
if (*p == *q)
const char *start = p;
while (*p++ == *q++) {
counter2++;
if (*q != '\0' && counter2 < count(s2))
found = 0;
else {
const char *end = q;
}
}
/* Rewriting the end of a sentence to the beginning of the found word */
if (found) {
while (*start++ = *end++)
;
}
s1++;
}
return toReturn;
}
void insert(char niz[], int size) {
char character = getchar();
if (character == '\n')
character = getchar();
int i = 0;
while (i < size - 1 && character != '\n') {
array[i] = character;
i++;
character = getchar();
}
array[i] = '\0';
}
int main() {
char stringFirst[100];
char stringSecond[20];
printf("Type your text here: [NOT MORE THAN 100 CHARACTERS]\n");
insert(stringFirst, 100);
printf("\nInsert the word you wish to remove from your text.");
insert(stringSecond, 20);
printf("\nAfter removing the word, the text looks like this now: %s", stringFirst);
return 0;
}
your code is badly formed, i strongly suggest compiling with:
gcc -ansi -Wall -pedantic -Werror -D_DEBUG -g (or similar)
start with declaring your variables at the beginning of the function block, they are known only inside the block they are declared in.
your count function is buggy, missing a closing '}' (it doesn't compile)
should be something like
size_t Strlen(const char *s)
{
size_t size = 0;
for (; *s != '\n'; ++s, ++size)
{}
return size;
}
implementing memmove is much more efficient then copy char by char
I reformatted you code for small indentation problems and indeed indentation problems indicate real issues:
There is a missing } in count. It should read:
/* Side function to count the number of letters of the word we wish to remove */
int count(char *s) {
int counter = 0;
while (*s++) {
counter++;
}
return counter;
}
or better:
/* Side function to count the number of letters of the word we wish to remove */
int count(const char *s) {
const char *s0 = s;
while (*s++) {
continue;
}
return s - s0;
}
This function counts the number of bytes in the string, an almost exact clone of strlen except for the return type int instead of size_t. Note also that you do not actually use nor need this function.
Your function insert does not handle EOF gracefully and refuses an empty line. Why not read a line with fgets() and strip the newline manually:
char *input(char buf[], size_t size) {
size_t i;
if (!fgets(buf, size, stdin))
return NULL;
for (i = 0; buf[i]; i++) {
if (buf[i] == '\n') {
buf[i] = '\0';
break;
}
}
return buf;
}
In function remove_word, you should define start and end with a larger scope, typically the outer while loop's body. Furthermore s1 should have type char *, not const char *, as the phrase will be modified in place.
You should only increment p and q if the test succeeds and you should check that p and q are not both at the end of their strings.
last but not least: you do not call remove_word in the main function.
The complete code can be simplified into this:
#include <stdio.h>
/* Function to remove a word from a sentence */
char *remove_word(char *s1, const char *s2) {
if (*s2 != '\0') {
char *dst, *src, *p;
const char *q;
dst = src = s1;
while (*src != '\0') {
for (p = src, q = s2; *q != '\0' && *p == *q; p++, q++)
continue;
if (*q == '\0') {
src = p; /* the word was found, skip it */
} else {
*dst++ = *src++; /* otherwise, copy this character */
}
}
*dst = '\0'; /* put the null terminator if the string was shortened */
}
return s1;
}
char *input(char buf[], size_t size) {
size_t i;
if (!fgets(buf, size, stdin))
return NULL;
for (i = 0; buf[i]; i++) {
if (buf[i] == '\n') {
buf[i] = '\0';
break;
}
}
return buf;
}
int main() {
char stringFirst[102];
char stringSecond[22];
printf("Type your text here, up to 100 characters:\n");
if (!input(stringFirst, sizeof stringFirst))
return 1;
printf("\nInsert the word you wish to remove from your text: ");
if (!input(stringSecond, sizeof stringSecond))
return 1;
printf("\nAfter removing the word, the text looks like this now: %s\n",
remove_word(stringFirst, stringSecond));
return 0;
}
Your start and end pointers are defined within a block which makes their scope limited within that block. So, they are not visible to other parts of your code, and if you attempt to reference them outside their scope, the compiler will complain and throw an error. You should declare them at the beginning of the function block.
That said, consider the following approach to delete a word from a string:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int delete_word(char *buf,
const char *word);
int main(void)
{
const char word_to_delete[] = "boy";
fputs("Enter string: ", stdout);
char buf[256];
fgets(buf, sizeof(buf), stdin);
if (delete_word(buf, word_to_delete))
{
printf("Word %s deleted from buf: ", word_to_delete);
puts(buf);
}
else
{
printf("Word %s not found in buf: ", word_to_delete);
puts(buf);
}
system("PAUSE");
return 0;
}
int chDelimit(int ch)
{
return
(ch == '\n' || ch == '\t') ||
(ch >= ' ' && ch <= '/') ||
(ch >= ':' && ch <= '#') ||
(ch >= '[' && ch <= '`') ||
(ch >= '{' && ch <= '~') ||
(ch == '\0');
}
char *find_pattern(char *buf,
const char *pattern)
{
size_t n = 0;
while (*buf)
{
while (buf[n] && pattern[n])
{
if (buf[n] != pattern[n])
{
break;
}
n++;
}
if (!pattern[n])
{
return buf;
}
else if (!*buf)
{
return NULL;
}
n = 0;
buf++;
}
return NULL;
}
char *find_word(char *buf,
const char *word)
{
char *ptr;
size_t wlen;
wlen = strlen(word);
ptr = find_pattern(buf, word);
if (!ptr)
{
return NULL;
}
else if (ptr == buf)
{
if (chDelimit(buf[wlen]))
{
return ptr;
}
}
else
{
if (chDelimit(ptr[-1]) &&
chDelimit(ptr[wlen]))
{
return ptr;
}
}
ptr += wlen;
ptr = find_pattern(ptr, word);
while (ptr)
{
if (chDelimit(ptr[-1]) &&
chDelimit(ptr[wlen]))
{
return ptr;
}
ptr += wlen;
ptr = find_pattern(ptr, word);
}
return NULL;
}
int delete_word(char *buf,
const char *word)
{
size_t n;
size_t wlen;
char *tmp;
char *ptr;
wlen = strlen(word);
ptr = find_word(buf, word);
if (!ptr)
{
return 0;
}
else
{
n = ptr - buf;
tmp = ptr + wlen;
}
ptr = find_word(tmp, word);
while (ptr)
{
while (tmp < ptr)
{
buf[n++] = *tmp++;
}
tmp = ptr + wlen;
ptr = find_word(tmp, word);
}
strcpy(buf + n, tmp);
return 1;
}
If you have to do it manually, just loop over the indicies of your string to find the first one that matches and than you’ll have a second loop that loops for all the others that matches and resets all and jumps to the next index of the first loop if not matched something in order to continue the searching. If I recall accuretaly, all strings in C are accesible just like arrays, you’ll have to figure it out how. Don’t afraid, those principles are easy! C is an easy langugae, thiught very long to write.
In order to remove: store the first part in an array, store the second part in an array, alloc a new space for both of them and concatinate them there.
Thanks, hit the upvote button.
Vitali
EDIT: use \0 to terminate your newly created string.

Issues with 'saving progress' of function

I'm trying to parse through some CSV log files extracting only the n'th field (dismissing the others for speed). My function works as expected when I use a buffer size with fread greater than the size of the input.
The problem is when I read in part of the input and try to continue where I left off the next time the function is called. I believe the problem lies in how I'm handling the null terminator and setting my globals, but I just can't seem to figure it out.
Any help with understanding what I'm doing wrong greatly appreciated!
Code
#include <stdio.h>
#include <time.h>
int gcomc = 0;
int gpos = 0;
void test(char *str, int len)
{
const char *ptr = str;
char ch;
int i;
char so[10];
int comc = gcomc;
int pos = gpos;
for(i = 0; i < len; i++)
{
ch = ptr[i];
switch(ch)
{
case ';':
comc++;
break;
case '\0':
gcomc = comc;
gpos = pos;
break;
default:
if (comc == 3) {
ch = ptr[i];
so[pos++] = ch;
}
if (comc == 7) {
printf(" %s ", so);
comc = 0;
pos = 0;
gcomc = 0;
gpos = 0;
}
}
}
return;
}
int main(int argc, char* argv[])
{
FILE *fin=fopen("test.txt", "rb");
char buffer[100 + 1];
size_t bsz;
while((bsz = fread(buffer, sizeof *buffer, 100, fin)) > 0)
{
buffer[bsz] = '\0';
test(buffer, bsz);
}
return 1;
}
Input
A;B;C;D;E;F;G;H
I;J;K;L;M;N;O;P
Q;R;S;T;U;V;W;X
Y;Z;1;2;3;4;5;6
Output with buffer size of 100 (101)
D L T 2
Output with buffer size of 10 (11)
D P
Q X
Segmentation fault (core dumped)
Edit:
Thank you for the comments and code, I've reworked my (rather dumb written) code - any further criticism is welcome (constructive or destructive, I learn from it all):
#include <stdio.h>
#include <time.h>
void test(char *str, int len);
int gcomc, gpos = 0;
void test(char *str, int len)
{
const char *ptr = str;
char ch;
int i;
static char so[10];
for(i = 0; i < len; i++)
{
ch = ptr[i];
switch(ch)
{
case ';':
gcomc++;
break;
default:
if (gcomc == 3) {
ch = ptr[i];
so[gpos++] = ch;
}
if (gcomc == 7) {
so[gpos] = '\0'; /* ensure so is null terminated */
printf(" %s ", so);
gcomc = 0;
gpos = 0;
}
}
}
return;
}
extern int main()
{
FILE *fin=fopen("test.txt", "rb");
char buffer[10 + 1];
size_t bsz;
while((bsz = fread(buffer, sizeof *buffer, sizeof buffer, fin)) > 0)
{
test(buffer, bsz);
}
return 1;
}
There are at least two problems in your code to be able to read the file in chunks.
First, the so array is automatic: it has no reason to keep its values from one call to the others. You should declare it global (outside the test function) or static.
Next, you only copy the local state to global one when you find a null. But the null is at position len, and you exit the loop just before (for(i = 0; i < len; i++) note the <) so on next call you start again with 0, 0. You should choose one method to indicate the end of the buffer, either passing a length, of writing a null marker, but mixing both is error prone. As you use fread, my advice is to stick to a length:
In main use:
while((bsz = fread(buffer, sizeof *buffer, sizeof buffer, fin)) > 0)
{
test(buffer, bsz);
}
(that way, you only write the size of the buffer once)
and in test:
void test(char *str, int len)
{
const char *ptr = str;
char ch;
int i;
static char so[10];
int comc = gcomc;
int pos = gpos;
for(i = 0; i < len; i++)
{
ch = ptr[i];
switch(ch)
{
case ';':
comc++;
break;
default:
if (comc == 3) {
ch = ptr[i];
so[pos++] = ch;
}
if (comc == 7) {
so[pos] = '\0'; /* ensure so is null terminated */
printf(" %s ", so);
comc = 0;
pos = 0;
gcomc = 0;
gpos = 0;
}
}
}
gcomc = comc; /* store the state to globals */
gpos = pos;
return;
}
But as you were said in comments mixing local and globals like that is error prone. It looks like you started coding before designing the structure of the program and identifying what actually needed to be global. You didn't, did you? ;-)
The state of the parser inside test() need to survive the multiple call. You took care of this partly only make the counters global. Globals are bad practise. Also you miss to save the state (its content) of so.
Encapsulate the state in a structure.
#include <stdlib.h>
#include <stdio.h>
#define SO_SIZE (10)
struct state
{
size_t comc;
size_t pos;
char so[SO_SIZE + 1]; /* Add 1 for the 0-terminator. */
}
and pass it to each call of the parser (test() here).
Adjust the parser like this:
int test(struct state * pstate, const char *str, size_t len)
{
int result = 0; /* be optimistic. */
char ch;
size_t i;
for (i = 0; i <= len; i++)
{
ch = str[i];
switch (ch)
{
case ';':
pstate->comc++;
break;
default:
if (pstate->comc == 3)
{
ch = str[i];
if (SO_SIZE <= pstate->pos)
{
result = -1; /* overflow */
break;
}
pstate->so[pstate->pos++] = ch;
}
if (pstate->comc == 7)
{
printf(" %s ", pstate->so);
pstate->comc = 0;
pstate->pos = 0;
}
}
}
return result;
}
Then call it like this:
#define BUFFER_SIZE (100)
int main(void)
{
FILE *fin = fopen("test.txt", "rb");
if (NULL == fin)
{
perror("fopen() failed");
return EXIT_FAILURE;
}
{
char buffer[BUFFER_SIZE + 1] = {0};
size_t bsz;
struct state state = {0};
int result;
while (0 < (bsz = fread(buffer, sizeof *buffer, sizeof buffer, fin))
&& (0 == result))
{
result = test(&state, buffer, bsz);
}
return result ?EXIT_FAILURE :EXIT_SUCCESS;
}
}

K&R Exercise 1-19: Reverse Char Array

I'm able to reverse the array fine, but I can't get the program to terminate when I do CTRL+D(EOF) in terminal.
The only way I can get the program to terminate is if the very first thing I do after compiling is doing CTRL+D. But if I type in one string, then CTRL+D will not work after that.
I'm not quite sure where my error is.
#include <stdio.h>
#define MAXLINE 1000 // Maximum input.
// ----------------- reverseLine -----------------
// This method reads in chars to be put into an
// array to make a string. EOF and \n are the
// delimiters on the chars, then \0 is the
// delimiter for the string itself. Then the
// array is swapped in place to give the reverse
// of the string.
//------------------------------------------------
int reverseLine(char s[], int lim)
{
int c, i, newL;
// c is the individual chars, and i is for indices of the array.
for (i = 0; i < lim - 1 && (c=getchar()) != EOF && c != '\n'; ++i)
{
s[i] = c;
}
if (c == '\n') // This lets me know if the text ended in a new line.
{
newL = 1;
}
// REVERSE
int toSwap;
int end = i-1;
int begin = 0;
while(begin <= end) // Swap the array in place starting from both ends.
{
toSwap = s[begin];
s[begin] = s[end];
s[end] = toSwap;
--end;
++begin;
}
if (newL == 1) // Add the new line if it's there.
{
s[i] = '\n';
++i;
}
s[i] = '\0'; // Terminate the string.
return i;
}
int main()
{
int len;
char line[MAXLINE];
while ((len = reverseLine(line, MAXLINE)) > 0) // If len is zero, then there is no line to recored.
{
printf("%s", line);
}
return 0;
}
The only thing I can think of is the while loop in main checks if len > 0, so if I type EOF, maybe it can't make a valid comparison? But that wouldn't make sense as to why it works when that's the first and only thing I type.
Your program will never read the EOF because of this condition:
(c=getchar()) != EOF && c != '\n';
As soon as c is equal to '\n' the loop terminates and all the following characters are ignored. I think you should separate input from line reversing and make the usual checks on the reverse function parameters.
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define SIZE_MAX (256U)
static bool linereverse(char *line);
static bool deletenewline(char *s);
int main(void)
{
char buff[SIZE_MAX];
bool success;
(void) fputs("Enter a string: ", stdout);
if( NULL == fgets(buff,(size_t) SIZE_MAX, stdin))
{
(void) fputs("Error: invalid input!\n",stderr);
return EXIT_FAILURE;
}
success = deletenewline(buff);
if(false == success)
{
(void) fputs("Error: cannot remove newline\n",stderr);
return EXIT_FAILURE;
}
success = linereverse(buff);
if(false == success)
{
(void) fputs("Error: cannot reverse the line");
return EXIT_FAILURE;
}
(void) fputs("The line reversed is: ", stdout);
(void) fputs(buff, stdout);
(void) puchar('\n');
return EXIT_SUCCESS;
}
static bool linereverse(char *line)
{
size_t i;
size_t j;
char tmp;
if(NULL == line)
{
return false;
}
i = 0;
j = strlen(line) - 1;
while(i < j)
{
tmp = line[i];
line[i] = line[j];
line[j] tmp;
++i;
--j;
}
return true;
}
static bool deletenewline(char *s)
{
char *p;
if(NULL == s)
{
return false;
}
p = strrchr(s,'\n');
if(NULL != p)
{
*p = '\0';
}
return true;
}

Remove punctuation at beginning and end of a string

I have a string and I want to remove all the punctuation from the beginning and the end of it only, but not the middle.
I have wrote a code to remove the punctuation from the first and last character of a string only, which is clearly very inefficient and useless if a string has 2 or more punctuations at the end.
Here is an example:
{ Hello ""I am:: a Str-ing!! }
Desired output
{ Hello I am a Str-ing }
Are there any functions that I could use? Thanks.
This is what I've done so far. I'm actually editing the string in a linked-list
if(ispunct(removeend->string[(strlen(removeend->string))-1]) != 0) {
removeend->string[(strlen(removeend->string))-1] = '\0';
}
else {}
Iterate over the string, use isalpha() to check each character, write the characters which pass into a new string.
char *rm_punct(char *str) {
char *h = str;
char *t = str + strlen(str) - 1;
while (ispunct(*p)) p++;
while (ispunct(*t) && p < t) { *t = 0; t--; }
/* also if you want to preserve the original address */
{ int i;
for (i = 0; i <= t - p + 1; i++) {
str[i] = p[i];
} p = str; } /* --- */
return p;
}
Iterate over the string, use isalpha() to check each character, after the first character that passes start writing into a new string.
Iterate over the new string backwards, replace all punctuation with \0 until you find a character which isn't punctuation.
#include <stdio.h>
#include <ctype.h>
#include <string.h>
char* trim_ispunct(char* str){
int i ;
char* p;
if(str == NULL || *str == '\0') return str;
for(i=strlen(str)-1; ispunct(str[i]);--i)
str[i]='\0';
for(p=str;ispunct(*p);++p);
return strcpy(str, p);
}
int main(){
//test
char str[][16] = { "Hello", "\"\"I", "am::", "a", "Str-ing!!" };
int i, size = sizeof(str)/sizeof(str[0]);
for(i = 0;i<size;++i)
printf("%s\n", trim_ispunct(str[i]));
return 0;
}
/* result:
Hello
I
am
a
Str-ing
*/
Ok, in a while iteration, call multiple times the strtok function to separate each single string by the character (white space). You could also use sscanf instead of strtok.
Then, for each string, you have to do a for cycle, but beginning from the end of the string up to the beginning.As soon as you encounter !isalpha(current character) put a \0 in the current string position. You have eliminated the tail's punctuation chars.
Now, do another for cycle on the same string. Now from 0 to strlen(currentstring). While is !isalpha(current character) continue. If isalpha put the current character in in a buffer and all the remaining characters. The buffer is the cleaned string. Copy it into the original string.
Repeat the above two steps for the others strtok's outputs. End.
Construct a tiny state machine. The cha2class() function divides the characters into equivalence classes. The state machine will always skip punctuation, except when it has alphanumeric characters on the left and the right; in that case it will be preserved. (that is the memmove() in state 3)
#include <stdio.h>
#include <string.h>
#define IS_ALPHA 1
#define IS_WHITE 2
#define IS_PUNCT 3
int cha2class(int ch);
void scrutinize(char *str);
int cha2class(int ch)
{
if (ch >= 'a' && ch <= 'z') return IS_ALPHA;
if (ch >= 'A' && ch <= 'Z') return IS_ALPHA;
if (ch == ' ' || ch == '\t') return IS_WHITE;
if (ch == EOF || ch == 0) return IS_WHITE;
return IS_PUNCT;
}
void scrutinize(char *str)
{
size_t pos,dst,start;
int typ, state ;
state = 0;
for (dst = pos = start=0; ; pos++) {
typ = cha2class(str[pos]);
switch(state) {
case 0: /* BOF, white seen */
if (typ==IS_WHITE) break;
else if (typ==IS_ALPHA) { start = pos; state =1; }
else if (typ==IS_PUNCT) { start = pos; state =2; continue;}
break;
case 1: /* inside a word */
if (typ==IS_ALPHA) break;
else if (typ==IS_WHITE) { state=0; }
else if (typ==IS_PUNCT) { start = pos; state =3;continue; }
break;
case 2: /* inside punctuation after whitespace: skip it */
if (typ==IS_PUNCT) continue;
else if (typ==IS_WHITE) { state=0; }
else if (typ==IS_ALPHA) {state=1; }
break;
case 3: /* inside punctuation after a word */
if (typ==IS_PUNCT) continue;
else if (typ==IS_WHITE) { state=0; }
else if (typ==IS_ALPHA) {
memmove(str+dst, str+start, pos-start); dst += pos-start;
state =1; }
break;
}
str[dst++] = str[pos];
if (str[pos] == '\0') break;
}
}
int main (int argc, char **argv)
{
char test[] = ".This! is... ???a.string?" ;
scrutinize(test);
printf("Result=%s\n", test);
return 0;
}
int main (int argc, char **argv)
{
char test[] = ".This! is... ???a.string?" ;
scrutinize(test);
printf("Result=%s\n", test);
return 0;
}
OUTPUT:
Result=This is a.string

Resources