C - How to read a string line by line?
Asked Answered
S

2

9

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?

Sphygmo answered 31/7, 2013 at 23:56 Comment(6)
Can I use fgets to process an already-existing string?Sphygmo
I think the following may answer your question: #5598013Corina
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.Illusionary
Hmm. So basically looping until a new line is seen, and then saving character-by-character into the new string?Sphygmo
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.Illusionary
@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.Illusionary
G
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;
}
Gormandize answered 1/8, 2013 at 1:6 Comment(1)
you say const char * headers but then you are modifying the chars (even though via a different pointer). should not be const...Satirical
N
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.

Nonstandard answered 6/9, 2022 at 22:17 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.