8

In my C program, I have a string that I want to process one line at a time, ideally by saving each line into another string, doing what I want with said string, and then repeating. I have no idea how this would be accomplished, though.

I was thinking of using sscanf. Is there a "read pointer" present in sscanf like there would be if I was reading from a file? What would be another alternative for doing this?

user1174511
  • 309
  • 3
  • 5
  • 15
  • Can I use fgets to process an already-existing string? – user1174511 Jul 31 '13 at 23:57
  • I think the following may answer your question: http://stackoverflow.com/questions/5597513/line-by-line-reading-in-c-and-c – Siva Jul 31 '13 at 23:59
  • No. I did a quick read on your topic, sorry for not read all. `fgets()` process input from a `FILE` pointer. You can implement it yourself, a simple looping until new line or NULL is seen, keeping on a variable with `static` storage class the offset where loop stoped and in next functions call you start from this offset. – The Mask Aug 01 '13 at 00:02
  • Hmm. So basically looping until a new line is seen, and then saving character-by-character into the new string? – user1174511 Aug 01 '13 at 00:02
  • You still can re-implement a struct like `FILE' for your string. To only difference to my previously suggestion is that instead of hold offset on local variable with static storage, you will keep it on struct member. – The Mask Aug 01 '13 at 00:05
  • @user1174511: EDIT: Yes. For save memory, if you want, you can returns offset from start and end as `match`(IIRC) function in POSIX C does. So, the new line will be the area among this offsets defined by your function. – The Mask Aug 01 '13 at 00:09

2 Answers2

11

Here's an example of how you can do it efficiently, if you are allowed to write into the long string:

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

int main(int argc, char ** argv)
{
   char longString[] = "This is a long string.\nIt has multiple lines of text in it.\nWe want to examine each of these lines separately.\nSo we will do that.";
   char * curLine = longString;
   while(curLine)
   {
      char * nextLine = strchr(curLine, '\n');
      if (nextLine) *nextLine = '\0';  // temporarily terminate the current line
      printf("curLine=[%s]\n", curLine);
      if (nextLine) *nextLine = '\n';  // then restore newline-char, just to be tidy    
      curLine = nextLine ? (nextLine+1) : NULL;
   }
   return 0;
}

If you're not allowed to write into the long string, then you'll need to make a temporary string for each line instead, in order to have the per-line string NUL terminated. Something like this:

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

int main(int argc, char ** argv)
{
   const char longString[] = "This is a long string.\nIt has multiple lines of text in it.\nWe want to examine each of these lines separately.\nSo we will do that.";
   const char * curLine = longString;
   while(curLine)
   {
      const char * nextLine = strchr(curLine, '\n');
      int curLineLen = nextLine ? (nextLine-curLine) : strlen(curLine);
      char * tempStr = (char *) malloc(curLineLen+1);
      if (tempStr)
      {
         memcpy(tempStr, curLine, curLineLen);
         tempStr[curLineLen] = '\0';  // NUL-terminate!
         printf("tempStr=[%s]\n", tempStr);
         free(tempStr);
      }
      else printf("malloc() failed!?\n");

      curLine = nextLine ? (nextLine+1) : NULL;
   }
   return 0;
}
Jeremy Friesner
  • 70,199
  • 15
  • 131
  • 234
  • you say `const char * headers` but then you are modifying the chars (even though via a different pointer). should not be const... – mrbrdo Dec 04 '15 at 00:26
0

Building on Jeremy Friesner's answer, here's what I came up with. First, a function for counting the number of lines:

int countChar(char* str, char c) {
  char* nextChar = strchr(str, c);
  int count = 0;

  while (nextChar) {
    count++;
    nextChar = strchr(nextChar + 1, c);
  }

  return count;
}

Next, a function for copying the string into an array of lines:

char** lineator(char* origin) {
  char* str = (char*) malloc(strlen(origin) + 1);
  strcpy(str, origin);

  int count = countChar(origin, '\n');
  char** lines = (char**) malloc(sizeof(char *) * count);

  char* nextLine = strchr(str, '\n');
  char* currentLine = str;

  int i = 0;

  while (nextLine) {
    *nextLine = '\0';

    lines[i] = malloc(strlen(currentLine) + 1);
    strcpy(lines[i], currentLine);

    currentLine = nextLine + 1;
    nextLine = strchr(currentLine, '\n');

    i++;
  }

  free(str);
  return lines;
}

Then I use these two functions to read the lines one by one. For example:

int count = countChar (contents, '\n');
char** lines = lineator(contents);
const char equals[2] = "=";

for (int i = 0; i < count; i++) {
  printf("%s\n", lines[i]);
}

I could of course use Mr. Freisner's answer. That would be more efficient and require fewer lines of code, but conceptually, I find this method a little easier to comprehend. I also neglected to deal with malloc failing.

yman
  • 41
  • 4