2

i'm doing something like excel, i have something like this:

1         2           3
A1        B1          C1

where it replaces the content for specified content, where A1 replaces the content for 1. B1 replaces the content of 2...and etc...

i'm using a multidimensional array, and i do the things like this:

int offset = 0, readCharCount;
    while(sscanf(matris[i][c] + offset, "%c%d%*c%n", &col, &linha, &readCharCount) == 2){
        //printf("%c, %d\n", col, linha);
        //strcpy(matris[i][c], matris[linha-1][col - 'A']);

        offset += readCharCount;
      //printf(" {%c, %d}", col, linha);
      //printf("\n");
  }

But when i have A1+B1+C1 and another things, i cant replace the total content, because other references will be removed....

So, at the cell, A1+B1+C1, i wanna change B1 for the content specified....i wanna have like this:

This -> A1+B1+C1

to -> 1+2+3

....

Thanks.

sehe
  • 374,641
  • 47
  • 450
  • 633
GoldenMedal
  • 102
  • 2
  • 9

3 Answers3

4

You might just reuse this c++ solution (replacing the generic iterators by hardcoding char* instead).

I gave it a whirl. However, I wish to give a warning: it looks like you're trying to implement an expression parser. I'd strongly advise you to either

  • handroll a (recursive descent) parser
  • use flex/bison (or lex/yacc)

so you don't paint yourself in an awkward corner of error-prone text-handling in C.

Edit: I rewrote your C program using C++; you can see it working live here.

Edit 2: Another fixup of your C program in pure C: http://ideone.com/ExnufJ updated to support iterative expansions now, too

The answer just concerns itself with the pure C approach:

So, let's get started. I assumed a sample "spreadsheet" (it could contain numbers instead of strings):

const char* cells[][4] = {
    /* A       B           C        D                 */
    { "the"  , "lazy"    , "cow"  , "jumped"  }, /* 1 */
    { "over" , "the"     , "quick", "brown"   }, /* 2 */
    { "paper", "packages", "tied" , "up"      }, /* 3 */
    { "with" , "silver"  , "white", "winters" }, /* 4 */
    { "that" , "melt"    , "fox" ,  "springs" }, /* 5 */
};

Using just two helpers:

const char* get_cell_value(const char* coordinate_b, const char* coordinate_e);
char* expand_cell_references(const char* f, const char* const l, char* o); /*the magic engine*/

we can write the following demo program:

int main()
{
    const char in[] = "The C2 D2 C5 D1 A2 B2 B1 dog!";

    char out[1024] = {0};
    expand_cell_references(in, in+strlen(in), out);
    puts(out); /* "The quick brown fox jumped over the lazy dog!" */

    return 0;
}

which prints the well-known test phrase as per the comment. Now, get_cell_value is really simple:

const char* get_cell_value(const char* coordinate_b, const char* coordinate_e)
{
    size_t col = 0, row = 0;
    const char* it;
    for (it=coordinate_b; it != coordinate_e; ++it)
    {
        if (*it >= 'A' && *it <= 'Z')
            col = 26*col + (*it - 'A');
        if (*it >= '0' && *it <= '9')
            row = 10*row + (*it - '0'); /* or use atoi and friends */
    }
    row--; /* 1-based row nums in Excel */

    return cells[row][col]; /* 1-based indexes in Excel */
}

And expand_cell_references is slightly more involved, being a simple DFA parser:

char* expand_cell_references(const char* f, const char* const l, char* o)
{
    enum parser_state {
        other,
        in_coord_col,
        in_coord_row
    } state = other;

    /*temporary storage for coordinates being parsed:*/
    char accum[16] = {0};
    char* accit = accum;
    while (f!=l)
    {
        switch(state) /*dummy, the transitions flow in fallthrough order for now*/
        {
            case other:
                *(accit = accum) = 0; /*reset the accumulator*/
                while (f!=l && !(*f>='A' && *f<='Z'))
                    *o++ = *f++;
                /*fallthrough*/
            case in_coord_col:
                while (f!=l && *f>='A' && *f<='Z')
                    *accit++ = *f++;
                /*fallthrough*/
            case in_coord_row:
                {
                    const char* expanded = accum;
                    if (f!=l && *f>='0' && *f<='9')
                    {
                        while (f!=l && *f>='0' && *f<='9')
                            *accit++ = *f++;
                        expanded = get_cell_value(accum, accit);
                    }
                    else
                    {
                        *accit = 0;
                    }
                    while (*expanded)
                        *o++ = *expanded++;
                    continue; /*state = other;*/
                }
        }
    }
    return o;
}

I took some shortcuts there, because this grammar is so minimalist, but it should give you a proper idea of where to start.

See a live demo here http://ideone.com/kS7XqB so you can play with it yourself. Note that I added debugging (asserts) to the get_cell_value function so you don't accidentally reference out-of-bounds indexes.

Community
  • 1
  • 1
sehe
  • 374,641
  • 47
  • 450
  • 633
  • and if i have + - * / between them? it will keep this signals too? – GoldenMedal Jun 21 '13 at 22:48
  • Have you tried it? You know, I made it exceptionally easy for you to try just that **[http://ideone.com/0eTr7F](http://ideone.com/0eTr7F)**. Cheers! – sehe Jun 22 '13 at 09:17
  • it works gratly, but, i cant initializer cells with my string that is inside of a multidimensional array, and if i take off the constant cell at the beggining, it appers that cell is not declared yet...what can i do to works with my code? – GoldenMedal Jun 22 '13 at 18:19
  • Yeah... what _can_ you do. You should just be able to replace the `return` statement of the `get_cell_value` function. This was a separate function deliberately in my code, so thay it would be easy to adapt to your code. I obviously can't implement it for you since you don't show the relevant existing code in your question... Good luck. – sehe Jun 22 '13 at 18:43
  • if you wanna see, my entire code: http://pastebin.com/AgcDi7YU its write in portuguese, but you can understand... thanks.. – GoldenMedal Jun 22 '13 at 20:30
  • @user2505640 simply replacing `return cells[row][col]` by `return matris[row][col]` appears to be exactly what you want. Of course, declare `static char matris[30][26][302] = {0};` somewhere _global_ and _before the definition of `get_cell_value()` :/ – sehe Jun 22 '13 at 21:59
  • On a different note, there is quite a large number of issues with that code. Why are you doing this in C? C++ or Python would appear to make the whole problem trivial, IMO? – sehe Jun 22 '13 at 22:00
  • Here's a simple rewrite of the C implementation in C++, including the test case from your original question: http://coliru.stacked-crooked.com/view?id=814ec79f83fa4d93a9be9f5500988b50-0681a6d6fa3348403a2b0d4989bfddf5 running live :) – sehe Jun 22 '13 at 23:12
  • i'm doing in C. i put static char matris[30][26][302]; at the beggining of the code, after get_cell_value prototype static char matris[30][26][302]; const char* get_cell_value(const char* coordinate_b, const char* coordinate_e); and changed cell[row][col] to matris[row][col] on the body, i changed this: char out[1024] = {0}; expand_cell_references(matris[i][c], matris[i][c]+strlen(matris[i][c]), out); puts(out); and i made this input: 1 2 3 A1 A1+B1 C1 and my return was 1 2 3 +. thats wierd =/ – GoldenMedal Jun 23 '13 at 17:12
  • @GoldenMedal It's only weird because you don't show the modified code? Also, note that there where several big problems in [the code](http://pastebin.com/AgcDi7YU) _before that point_ (initializing the matrix from the file, that is. Notably, the return value of `fgets` should be checked (!!!), `vetorDeNumeros` should probably not be fixed at 2 elements size (because `k` will get >1 unless there are at most 2 lines in the input), ... – sehe Jun 23 '13 at 18:46
  • ... and `ordenar(vetorDeNumeros)` is undefined behaviour until all elements were initialized - meaning, on my system, that `colunas` would always end up being zero and nothing would ever happen since 0 columns are processed.. – sehe Jun 23 '13 at 18:47
  • Here is my attempt to fix it, and it works alright: **[http://ideone.com/Zz1pRI](http://ideone.com/Zz1pRI)** (you can see the correct output for yourself!). Disclaimer: I haven't checked ***all*** of the code for potential problems. – sehe Jun 23 '13 at 18:54
  • you are right, i defined vetorDeNumeros as 2 only for tests, and i forgott... colunas before i had processed, being more than 0 now, because i fixed vetorDeNumeros as 26(max columm size) and should work no? thats the code.http://pastebin.com/q4yhqvTr Again, i put 1 2 3 A1 A1+B1 C1 it returned 1,2,3 +, only, i think i made something wrong again... – GoldenMedal Jun 23 '13 at 19:01
  • i understand what u told about matris initialized inside the main.... Can i hug you?? Thanks for totally help. – GoldenMedal Jun 23 '13 at 19:07
  • @GoldenMedal like I said, you need to avoid reading the uninitialized elements from `vetorDeNumeros` (also, I think it needs to be [30] because it's indexes with `k`, not `j` so it needs to be at least the same size as the corresponding matrix dimension? Finally, no you can't hug me (the internet has limits), but [feel free to accept my answer](http://meta.stackexchange.com/questions/5234/how-does-accepting-an-answer-work) :) – sehe Jun 23 '13 at 19:11
  • yeah, should be 30, and should be the same of collums, my bad. I accept you answer, thank you. Now i need to make sums with this numbers, i have a function now to do it. Thanks. – GoldenMedal Jun 23 '13 at 19:14
  • @GoldenMedal Cough. I gently remind you of my warning: "***it looks like you're trying to implement an expression parser***. I'd strongly advise you to either..." - caveat emptor! But good luck on your journey :) – sehe Jun 23 '13 at 19:17
  • (There, I totally aced my profile pic to go with my last comment!) – sehe Jun 23 '13 at 19:21
  • 1 more thing, i have a function that recieve the content in the cell, and return another content to this cell, to use my function i do this? out = my_function(out); ? – GoldenMedal Jun 24 '13 at 00:55
  • And aparrently it happens for some cases.... if i insert http://pastebin.com/NSPsbACf , i made a function to remove the =, so dont look at this....my output is this...http://pastebin.com/jr8w6zqX i dont know why =/. – GoldenMedal Jun 24 '13 at 01:27
  • I did another tests, if the cell, what you are, need the result from other cell, its will maintain the content of the other cell, try this: http://ideone.com/kMmRkR it works with other single cells, but, at least, if the cell needs result from other cell, it brokes '-' – GoldenMedal Jun 24 '13 at 02:14
  • Hmmm. You have managed to confuse yourself using just your own cell formulas :) If you wanted to recursively evaluate cells, you should have thought of that! **[http://ideone.com/ExnufJ](http://ideone.com/ExnufJ)** (I must be crazy for doing this) – sehe Jun 24 '13 at 07:01
  • to do this only if has an '=' i do only this? ideone.com/Tkixqt – GoldenMedal Jun 25 '13 at 21:24
  • @GoldenMedal Once again, if you need more input grammar, extend 'expand_cell_values' instead! See my repeated hints "it looks like you're trying to implement an expression parser". You still haven't described the **actual** input grammar, so I can't help you. And _no_, I don't suggest you 'fake' your way out of it by crusting on 'arbitrary conditions' separately. – sehe Jun 26 '13 at 07:57
  • http://ideone.com/w9tqHD here's what I mean. I ditched the `clean_equals` function as I think it never did what you needed anyways. The same goes for the other 'parser-avoidance' functions (`clean`, `RemoveSpaces`?). OT: I don't know why you (think) you need `ordenar`. – sehe Jun 26 '13 at 08:29
  • Here's the code, still working precisely the same, but with (a) fewer restrictions (b) 86 lines of code less: http://ideone.com/butnkS (***113 lines of code less*** than [your original paste-bin](http://pastebin.com/AgcDi7YU)). Also note that there is no reason why columns should be limited to 26 (my `get_cell_value` supports column names like AA999) – sehe Jun 26 '13 at 08:53
1

I think that as may be replaced with values ​​by table lookup by cutting the name simply its corresponding value in the name of the string you want to replace.

E.g

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


typedef struct pair {
    char key[16];
    int  value;
} Pair;

int main(void){
    Pair var_table[] = { { "A1", 1 }, {"B1", 2}, { "C1", 3 }};//sorted
    size_t size = sizeof(var_table)/sizeof(*var_table);
    char input[256] = "A1+B1+C1";
    char outbuff[4096];
    char *p;
    int offset = 0;

    printf("This -> %s\n\n", input);
    for(p=input;*p;){
        Pair *var;
        char op, *opp;

        opp=strpbrk(p, "+-*/");
        if(opp){
            op = *opp;
            *opp = '\0';//cut string at op position
        }
        //search key(p)
        var = (Pair*)bsearch(p, var_table, size, sizeof(*var), (int (*)(const void *, const void *))strcmp);
        if(var)//find!
            offset += sprintf(outbuff + offset, "%d", var->value);//or store array? 
        else
            offset += sprintf(outbuff + offset, "%s", "#UNKNOWN_VAR_NAME#");
        if(opp){
            offset += sprintf(outbuff + offset, "%c", op);
            p = opp + 1;
        } else 
            break;
    }
    printf("to -> %s\n", outbuff);


    return 0;
}
BLUEPIXY
  • 39,699
  • 7
  • 33
  • 70
  • woah. This looks to be a fine solution in C. Note that it seems considerably less flexible: The OP specifically mentions he has the 'cells' in a matrix, this solution handles only very very rigid grammar (I don't even think it will handle whitespace right now). Lastly, using strpbrk isn't exactly a basis that affords many opportunities to extend the grammar should it need to be more complicated. Still +1 for instructive, idiomatic code! – sehe Jun 22 '13 at 09:41
  • @not-sehe It may be necessary to write a tokenizer if aim something a little more flexible. Can not be determined only whether it is necessary. – BLUEPIXY Jun 22 '13 at 09:46
0

Any time you run into some string manipulation problem in C, your first instinct should be to look at string.h and see what's available there. As outlined in several answers here, there is no function to do string replacement directly, but it should be possible to use strstr and strncpy to find occurrences of substrings and then copy the replacement (into a new buffer so as not to clobber the rest of the original, obviously).

Community
  • 1
  • 1
Imre Kerr
  • 2,388
  • 14
  • 34