C reading string and accepts only brackets dynamically

594 views Asked by At

I've to read a string of brackets to analyze that. How can I read a string to be inserted in an array generated dynamically?

How can I avoid all characters from reading except for brackets using scanf? [ ] { } ( )

Thank you.

edit: I have to read a series of brackets from keyboard but I don't know the length. So I've to create an array generated dynamically ( this is a requirement ) to contains only the space of the brackets. While I'm reading I want to accepts only brackets and avoid all other characters, is this possibile with scanf with the regex?

3

There are 3 answers

0
chux - Reinstate Monica On

How can I avoid all characters from reading except for brackets using scanf? [ ] { } ( )

Code cannot. At some level, code is reading whatever the user types including [ ] { } ( ) and characters that are not. The best is for code to read the input and then selectivity operate on it even if that means reading and tossing the data. In any case, OP certainly wants to read a '\n' too to know when to stop.

Using fgets() is best for user input @Michael Walz, but OP reports that scanf() must be used.

scanf() has a scan set specifier that allows for reading only select characters, leaving others to remain in stdin. A scan set is "%[...]" with the ... as the desired characters. If ] is desired to be part of the scan set, it should be the first.

Since it is not stated an upper bound of input length, code can live dangerously and simply re-allocate memory as needed to accommodate user input by reading 1 character at a time.

Below is some not so efficient code that meets the goal:

char *bracket_read(void) {
  size_t brackets_length = 0;
  // Allocate +2, 1 for the null character and 1 for the potential next character
  char *brackets = malloc(brackets_length + 2);
  assert(brackets);
  brackets[brackets_length] = '\0';

  char non_bracket = 0;
  while (non_bracket != '\n') {
    #define SCAN_SET "][(){}"
    int cnt = scanf("%1[" SCAN_SET "]", &brackets[brackets_length]);
    if (cnt == 1) {
      brackets_length++;
      char *t = realloc(brackets, brackets_length + 2);
      assert(t);
      brackets = t;
    } else {
      // Quietly consume other characters
      if (scanf("%c", &non_bracket) == EOF) {
        if (brackets_length == 0) {
          free(brackets);
          return NULL;
        }
        non_bracket = '\n';
      }
    }
  }
  return brackets;
}

int main() {
  char *s;
  while ((s = bracket_read()) != NULL) {
    printf(".%s.\n", s);
    fflush(stdout);
    free(s);
  }
}
2
GorvGoyl On

Simply check all brackets using if condition. Suppose if ch is the array holding the input string then:

if(ch[0]=='[' || ch[0]==']'||...){
// process the bracket and do same for all other ch indeces.
}
0
John Bode On

So, I'm assuming your brief is to strip bracket characters from input text and store them for later analysis. Assuming that's correct, here's one pass that you may find useful:

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <stddef.h>

/**
 * Input buffer size
 */
#define BUFSIZE 20

/**
 * Extends the target buffer by doubling its size.
 * If the realloc call fails, the original buffer
 * is left intact.
 */
int extend( char **buffer, size_t *bufferSize )
{
  char *tmp = realloc( *buffer, *bufferSize * 2 );
  if ( tmp )
  {
    *buffer = tmp;
    *bufferSize *= 2;
  }

  return tmp != NULL;
}

int main( void )
{
  /**
   * Fixed buffer for reading from input stream
   */
  char inputBuffer[BUFSIZE+1] = {0};
  /**
   * Dynamically allocated target buffer that can be extended as necessary
   */
  char *targetBuffer = NULL;
  size_t targetBufferSize = sizeof *targetBuffer * sizeof inputBuffer;
  size_t targetBufferPos = 0;
  int done = 0;
  char fmt[20] = {0};

  /**
   * Allocate the target buffer, which is initially the same size as the input buffer.
   */
  if ( !(targetBuffer = malloc( targetBufferSize )) )
  {
    fprintf( stderr, "FATAL: could not allocate memory for targetBuffer\n" );
    exit( EXIT_FAILURE );
  }

  /**
   * Create our input format string.  Since we're using the [ conversion
   * specficier, we need to also specify the max size we want to read at
   * one time using the field width specifier.  Unfortunately, you can't
   * pass the field width as an argument in scanf the way you can with
   * printf, so we have to create the format string and save it to its
   * own buffer.  When we're done, it will be "%20[^\n]%c".  
   */
  sprintf( fmt, "%%%zu[^\n]%%c", sizeof inputBuffer - 1 );

  while ( !done )
  {
    char follow;

    /**
     * Read BUFIZE characters plus a "follow" character.  If the "follow" character
     * is a newline, then we've read the complete input line and don't need
     * to loop again.  Otherwise, push the "follow" character back onto the input
     * stream for the next read.
     */
     if ( scanf( fmt, inputBuffer, &follow ) < 1 )
     {
       fprintf( stderr, "FATAL: error on read\n" );
       free( targetBuffer );
       exit( EXIT_FAILURE );
     }

     if ( follow == '\n' )
       done = 1;
     else
       ungetc( (int) follow, stdin );

     /**
      * Walk down our input buffer; if the next character is one of '[', ']', '{' , '}',
      * '(', or ')', append it to the target buffer.
      */
     char *p = inputBuffer;
     while ( *p )
     {
       if ( *p == '[' || *p == ']' || *p == '{' || *p == '}' || *p == '(' || *p == ')' )
       {
         /**
          * Before we append a character to the target buffer, we first need to make
          * sure there's room left (targetBufPos < targetBufSize).  If there isn't
          * any room left, we need to extend the target buffer before appending to it.
          *
          * We're taking advantage of the short-circuit nature of the || operator. Remember
          * that given the expression a || b, if a evaluates to true, then the whole
          * expression evaluates to true regardless of the value of b, so b isn't
          * evaluated at all.
          *
          * If targetBufferPos < targetBufferSize evaluates to true (non-zero), then
          * the extend function isn't called; we simply append to the target buffer.
          *
          * If targetBufferPos < targetBufferSize is *not* true, then the extend
          * function is called.  If extend returns true (non-zero), then the operation
          * succeeded and we append to the now-lengthened target buffer.  If extend
          * returns false (zero), then the operation failed and we bail out with an error.
          */
         if ( targetBufferPos < targetBufferSize || extend( &targetBuffer,  &targetBufferSize ) )
         {
           targetBuffer[targetBufferPos++] = *p;
         }
         else
         { 
           fprintf( stderr, "FATAL: could not extend memory for  targetBuffer\n" );
           free( targetBuffer );
           exit( EXIT_FAILURE );
         }
       }
       p++;
     }
   } 
   /**
    * We're treating targetBuffer as a string, so we need to add
    * the 0 terminator to the end.  Again, we first need to make
    * sure there's room in the buffer, and if not, to extend it.
    *
    * Yes, there's a corner case here where you're doubling the buffer
    * size to add one more character.  I'm not going to worry about it
    * here, but it's easy enough to fix.  
    */
   if ( targetBufferPos < targetBufferSize || extend( &targetBuffer,  &targetBufferSize ) )
   {
     targetBuffer[targetBufferPos++] = 0;
   }
   else
   {
     fprintf( stderr, "FATAL: could not extend memory for targetBuffer\n" );
     free( targetBuffer );
     exit( EXIT_FAILURE );
   }

   printf( "Found the following bracket characters in the input string: %s\n", targetBuffer );
   free( targetBuffer );
   return EXIT_SUCCESS;
 }

Sample run:

$ ./scanner
(((This))) [[[[is]]]] {a} (T)[e]{s}[t]
Found the following bracket characters in the input string: ((()))[[[[]]]]{}()[]{}[]