From owner-freebsd-questions@FreeBSD.ORG Tue Feb 19 00:45:09 2008 Return-Path: Delivered-To: freebsd-questions@freebsd.org Received: from mx1.freebsd.org (mx1.freebsd.org [IPv6:2001:4f8:fff6::34]) by hub.freebsd.org (Postfix) with ESMTP id 7A75816A419 for ; Tue, 19 Feb 2008 00:45:09 +0000 (UTC) (envelope-from keramida@ceid.upatras.gr) Received: from mx-out-04.forthnet.gr (mx-out.forthnet.gr [193.92.150.104]) by mx1.freebsd.org (Postfix) with ESMTP id EA1D713C45E for ; Tue, 19 Feb 2008 00:45:08 +0000 (UTC) (envelope-from keramida@ceid.upatras.gr) Received: from mx-av-03.forthnet.gr (mx-av.forthnet.gr [193.92.150.27]) by mx-out-04.forthnet.gr (8.13.8/8.13.8) with ESMTP id m1J0j66W032110; Tue, 19 Feb 2008 02:45:06 +0200 Received: from MX-IN-05.forthnet.gr (mx-in-05.forthnet.gr [193.92.150.32]) by mx-av-03.forthnet.gr (8.14.1/8.14.1) with ESMTP id m1J0j5ux028317; Tue, 19 Feb 2008 02:45:05 +0200 Received: from kobe.laptop (adsl48-14.kln.forthnet.gr [77.49.175.14]) by MX-IN-05.forthnet.gr (8.14.2/8.14.2) with ESMTP id m1J0j3fB020652; Tue, 19 Feb 2008 02:45:05 +0200 Authentication-Results: MX-IN-05.forthnet.gr smtp.mail=keramida@ceid.upatras.gr; spf=neutral Authentication-Results: MX-IN-05.forthnet.gr header.from=keramida@ceid.upatras.gr; sender-id=neutral Received: from kobe.laptop (kobe.laptop [127.0.0.1]) by kobe.laptop (8.14.2/8.14.2) with ESMTP id m1J0j3d5005545; Tue, 19 Feb 2008 02:45:03 +0200 (EET) (envelope-from keramida@ceid.upatras.gr) Received: (from keramida@localhost) by kobe.laptop (8.14.2/8.14.2/Submit) id m1J0iwEv005541; Tue, 19 Feb 2008 02:44:58 +0200 (EET) (envelope-from keramida@ceid.upatras.gr) Date: Tue, 19 Feb 2008 02:44:58 +0200 From: Giorgos Keramidas To: Gary Kline Message-ID: <20080219004458.GC5362@kobe.laptop> References: <20080218230351.GA28000@thought.org> MIME-Version: 1.0 Content-Type: text/plain; charset=us-ascii Content-Disposition: inline In-Reply-To: <20080218230351.GA28000@thought.org> Cc: freebsd-questions@freebsd.org Subject: Re: is there an easier way? X-BeenThere: freebsd-questions@freebsd.org X-Mailman-Version: 2.1.5 Precedence: list List-Id: User questions List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Tue, 19 Feb 2008 00:45:09 -0000 On 2008-02-18 15:03, Gary Kline wrote: > > To my fellow C nerds, > > It's been a great manny years since I wrote this appended > snippet. Now I can't remember why (of if ) I need all the > strcpy() calls. Is there a simpler, more logical way of > printing a bunch of string by snipping off the left-most? > > In short,, can anyone 'splain why strtok needs all this? > #include > #include > > main() > { > char *bp, buf[512], *tok, tstr[512]; > static char *delim=" ", s1[256]="abc def ghi jkl mno."; > > bp = strcpy(buf, tstr); > strcpy(bp, s1); /* bp filled with writable mem works like this, too */ > while ((tok = strtok(bp, delim)) != NULL) > { > bp = NULL; > printf("tok = [%s]\n", tok); > } > } I don't know about `simpler', but the hardcoded `magic' numbers above, like 256 and 512, make me nervous. Another point which is worth noting is that you don't need to copy the string at all, for one thing. If you have a string defined like this: const char *s = "hello world"; Then you can use a second pointer to point somewhere within that string and print from that point: const char *point = s + 7; printf("%s\n", point); So if you want to strip the first 'word' from a C string, you can do this in the following steps: [0] Get the initial string in your s1[] buffer, i.e.: const char s1[] = "abc def ghi jkl mno."; Note that if you use `s1[]' to define the string, the compiler is smart enough to grab enough space for it. This way you don't need all the `magic' numbers, like 256 and 512 in the original code. [1] Find the first whitespace character in the string, using strcspn() to match everything *except* a whitespace: const char *p; size_t num_chars; num_chars = strcspn(s1, " \t"); p = s1 + num_chars; [2] If the character pointed at by the new value of `p' is the terminating '\0' character of the original string, you are done; the original string contains a single word, so you can skip it entirely. if (*p == '\0') { printf("\n"); return; } [3] Now you can use strspn(), which is the *inverse* operation, to skip the whitespace characters you just found: num_chars = strspn(p, " \t"); p += num_chars; [4] If the character pointed at by the new value of `p' is the terminating '\0' character of the original string, then you hit the end of the string again, so there is no `second' word; you can move on again: if (*p == '\0') { printf("\n"); return; } [5] If you are still around, then `p' now points right at the beginning of the second word. printf("%s\n", p); return; A sample function which returns a pointer to the second word of the string you pass to it, could be something like this: #include #include #define SPACECHARS " \t" char * secondword(char *s) { size_t num_chars; char *p; if (s == NULL) return NULL; /* * Skip any non-whitespace chars first. */ num_chars = strcspn(s, SPACECHARS); p = s + num_chars; if (*p == '\0') return p; /* * Then skip whitespace, until we hit the end of the * string, or we find the second word. */ num_chars = strspn(p, SPACECHARS); p += num_chars; /* * Even if we just hit '\0' or a non-space, we can * simply return whatever our next match character is. * It's either going to end up being the start of an * empty string, or the second word we wanted to find. */ return p; }