【问题标题】:How does one pass a control or nonprinting character to a C program?如何将控制或非打印字符传递给 C 程序?
【发布时间】:2023-12-31 10:59:01
【问题描述】:

情况:我有一个 C 程序,它接受一个字符串参数并对其进行处理(细节无关紧要。)我想向它传递一个包含控制字符的字符串,例如 EOF、CR 或 LF,并且无法在终端中将我的键盘切换到原始输入模式。我的问题是:C 是否有任何功能可以让我以某种方式指示或“键入”字符? (例如,在某些语言中创建字符串时,您可以用斜杠转义字符或指示其十六进制代码。我想知道在从终端内向 C 程序传递参数方面是否存在类似的情况 - 所以我在问命令行参数。)

【问题讨论】:

  • 通常有热键(Ctrl+D = EOF 等)检查您的终端应用程序是否有合适的热键。或者,如果您的意思是在 C 字符串中传递实际字符,您可以使用它的文字(“\r”、“\n”等)将其输入到 c 字符串中。
  • 我的意思是命令行参数。热键的问题在于,除非我处于原始模式,否则它们不会输入字符。例如,Ctrl+D 实际上会退出我所在的任何程序。
  • 如果您需要许多这样的控制字符,那么这些可能不是处理程序中事物的最佳方式。
  • 是的...不过,我仍然想回答我的问题。 :)
  • 不幸的是,一些控制字符就是这样工作的,因为它们是非标准的(“\n”与“\r\n”)

标签: c command-line-arguments control-characters


【解决方案1】:

您可能想学习如何在 bash 中将二进制数据作为参数传递。见this question

这是一个 C 语言的简短演示。程序以十六进制逐个字符打印出传递给它的每个参数。

/* compile with cc -o binarg binargs.c */

#include <stdio.h>

int main(int argc, char *argv[])
{
    int i;
    char *ip;
    printf("\n");
    for(i=0; i<argc; i++)
    {
        printf("argv[%d]=%s\n",i,argv[i]);
        for(ip=(char*)argv[i]; *ip!=0; ip++)
        {
            printf("0x%02X <-\t`%c'\n",*ip,*ip);
        }
        printf("\n");
    }
    return 0;
}

让我们按照上面提到的帖子中的说明向它传递二进制参数。

./binargs ABC $'\x41\x42\x43' $'\t\n'

结果:

argv[0]=./binargs
0x2E <- `.'
0x2F <- `/'
0x62 <- `b'
0x69 <- `i'
0x6E <- `n'
0x61 <- `a'
0x72 <- `r'
0x67 <- `g'
0x73 <- `s'

argv[1]=ABC
0x41 <- `A'
0x42 <- `B'
0x43 <- `C'

argv[2]=ABC
0x41 <- `A'
0x42 <- `B'
0x43 <- `C'

argv[3]=

0x09 <- `       '
0x0A <- `
'

argv[0] 是我们程序的名称binargs 本身

argv[1] 是一个常规字符串“ABC”

argv[2] 和 argv[1] 一样,但是是十六进制的

argv[3] 是两个控制字符的序列:HT LF

注意它使用传统的Unix方式来引用每个字符,所以我们可以在打印时看到不可打印字符的边界:

./binargs $'\a\b\v\f'  

(让我们跳过 argv[0] 部分)

argv[1]=


0x07 <- `'
0x08 <- '
0x0B <- `
         '
0x0C <- `
         '

或者我们可以通过管道将其发送到cat -v

./binargs $'\a\b\v\f' | cat -v

这使得结果更具可读性:

argv[1]=^G^H^K^L
0x07 <- `^G'
0x08 <- `^H'
0x0B <- `^K'
0x0C <- `^L'

【讨论】:

    【解决方案2】:

    在这种情况下,您需要非常清楚功能的位置。处理可能会或可能不会由调用您的程序的命令行解释器完成。根据您使用的是 DOS 命令行、Linux 还是 Unix 等,您可能会从相同的 C 代码中得到不同的结果。

    【讨论】:

    • 使用哪个 shell 也会影响它,以及终端设置。我怀疑这个问题没有“便携”的答案。
    【解决方案3】:

    如果您对能够通过转换函数发送 C 字符串样式的转义序列感到满意,只要您不在输入的中间使用引号,以下可能对您有用。如果您需要在输入中间使用引号,则必须弄清楚您的 shell 使用什么转义序列来通过命令行参数传递引号。

    #include <stdio.h>
    #include <ctype.h>
    #include <stdlib.h>
    
    // Unescapes 'str' in place, collapsing it back on itself, and
    // returns the resulting length of the collapsed buffer.  Handles
    // mid-buffer nul characters (0x00).  You can easily add your own
    // special escape sequences if you wish.  Just be sure that no escape
    // sequence translates into more characters than it takes to encode
    // the escape sequence itself in the original string.
    int unescape(char* str)
    {
        char *out, *in;
        int len=0;
        in = out = str; // both start at the same place
        while(*in)
        {
            char c = *in++;
            if (c != '\\')
                *out++ = c; // regular, unescaped character
            else
            {                   // escaped character; process it...
                c = *in++;
                if      (c == '0') *out++ = '\0';
                else if (c == 'a') *out++ = '\a';
                else if (c == 'b') *out++ = '\b';
                else if (c == 'f') *out++ = '\f';
                else if (c == 'n') *out++ = '\n';
                else if (c == 'r') *out++ = '\r';
                else if (c == 't') *out++ = '\t';
                else if (c == 'v') *out++ = '\v';
                else if (c == 'x'  // arbitrary hexadecimal value
                        && isxdigit(in[0]) && isxdigit(in[1]))
                {
                    char x[3];
                    x[0] = *in++;
                    x[1] = *in++;
                    x[3] = '\0';
                    *out++ = strtol(x, NULL, 16);
                }
                else if (c>='0' && c<='3' // arbitrary octal value
                        && in[0]>='0' && in[0]<='7'
                        && in[1]>='0' && in[1]<='7')
                {
                    *out++ = (c-'0')*64 + (in[0]-'0')*8 + (in[1]-'0');
                    in += 2;
                }
                else // any other char following '\' is just itself.
                    *out++ = *in++;
            }
            ++len; // each time through the loop adds one character
        }
        *out = '\0';
        return len;
    }
    
    void print_buf(const char* buf, int len)
    {
        int col;
        unsigned char* cp = (unsigned char*)buf;
        for (col=0; len>0; --len, ++col)
            printf(" %02x%s", *cp++, ((col&16==15) ? "\n" : ""));
    }
    
    int main(int argc, char*argv[])
    {
        char* str;
        int len;
    
        if (argc<2)
        {
            fprintf(stderr, "First arg must be a string, "
                    "and it probably ought to be quoted.\n");
            exit(1);
        }
    
        printf("\nInput string: \"%s\"\n", argv[1]);
        print_buf(argv[1], strlen(argv[1]));
    
        str = malloc(strlen(argv[1]));
        strcpy(str, argv[1]);
        len = unescape(str);
        printf("\nunescape() produces the following:\n");
        print_buf(str, len);
        free(str);
    
        printf("\n");
    }
    

    【讨论】:

      最近更新 更多