【问题标题】:How to write() hexadecimal representation of \n?如何写()\ n的十六进制表示?
【发布时间】:2021-07-08 19:38:21
【问题描述】:

我正在尝试 write() 的十六进制表示 \n 没有任何成功。

我有ft_putstr_non_printable.c的代码:

#include <unistd.h>

void    ft_putstr_non_printable(char *str)
{
    int             i;
    unsigned char   a;
    char            c;

    i = 0;
    a = 0x0;
    while (str[i] != '\0')
    {
        if (str[i] <= 31 || str[i] == 127)
        {
            a = str[i];
            write(1, &a, 1);
        }
        else
        {
            c = str[i];
            write(1, &c, 1);
        }
        i++;
    }
}

还有main.c

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

#include "ft_putstr_non_printable.c"

int main(void)
{
    char a[] = "\n au revoir\a";
    char b[] = "omellette du fromage\b";
    char c[] = "coeuf@ca6va\e fef";
    char d[] = " Batata \x7F rfg";
    char e[] = "roquefort`[e{forte-e_tem,bolor \n feff";
    char f[] = " we 9are 78familly \x1F rgfenf";

    ft_putstr_non_printable(a);
    ft_putstr_non_printable(b);
    ft_putstr_non_printable(c);
    ft_putstr_non_printable(d);
    ft_putstr_non_printable(e);
    ft_putstr_non_printable(f);
}

我做错了吗?如何获得 \x0a?

编辑:我不能使用printf()。我仅限于write()

【问题讨论】:

  • 嗯,write(1, &amp;a, 1);write(1, &amp;c, 1); 基本上是相同的,所以你不会那样做。 write 不像 printfwrite 是一个只写入原始字节的低级函数。要让\n0a0x0a 出现,意味着您将不得不写入两个或更多字节,而这反过来意味着您将不得不以某种方式构造这两个或更多字节。如果允许,最简单的方法是sprintf
  • 打印\n(以及任何字符)的十六进制表示:printf("%02x", '\n');
  • 研究字符串库。
  • 如果您出于某种原因试图避免使用printf,那么您正在寻找诸如itoa 之类的将数字转换为字符串的函数。然后你必须显式打印每个字符
  • 建议:不要像 #include "ft_putstr_non_printable.c" 那样使用 #include .c 文件

标签: c gcc


【解决方案1】:

str[i] 超出可打印范围时写入一个字符,形成一个小字符串并写入。

    // if (str[i] <= 31 || str[i] == 127)
    if (str[i] <= 31 || str[i] >= 127) {
        unsigned char a = str[i];
        char buf[5];
        int len = sprintf(buf, "\\x%02X", a);
        // write(1, &a, 1);
        write(1, buf, len);
    }

我仅限于 write()

如果sprintf() 不可用:

        // int len = sprintf(buf, "\\x%02X", a);
        buf[0] = '\\'; 
        buf[1] = 'x'; 
        buf[2] = "0123456789ABCDEF"[a/16];
        buf[3] = "0123456789ABCDEF"[a%16];
        buf[4] = '\0';
        len = 4;

高级:

char 可能是无符号,因此大于 127 的值是可能的。

为了很好地扭转这个过程,以十六进制打印\\ 可能是有意义的。

    if (str[i] <= 31 || str[i] >= 127 || str[i] == '\\') {

【讨论】:

  • buf[2] = "0123456789ABCDEF"[a%16]; 喜欢它...我完全忘记了那个小技巧。不错。
  • @MiguelM "0123456789ABCDEF" 是一个字符串a/16a 除以 16,商为 0 到 15。然后 [quotient] 索引字符串,得到 quotient'th char
  • @0___________ 关于编译器可能/可能不会在 2 行代码中使用相同的字符串是正确的(因此存在编码效率问题)。无论哪种方式都能正常运行。
  • @MiguelM 如果输入字符串是单个换行符'\n',需要什么输出?如果输入是 4 个字符串:反斜杠、'x'、'0'、'a',需要什么输出?
  • @chux-ReinstateMonica Ohhhhhh
【解决方案2】:

注意:

我推荐 chux 的答案 - 恢复莫妮卡,因为从 0..15 到十六进制的转换很好。

https://stackoverflow.com/a/68307913/4386427

我将保留这个答案,以防有人更喜欢这种更长时间的代码转换方式

回答

鉴于您的所有限制(这会阻止正常代码),您可能正在寻找:

char a = '\n';  // Or any other char
char h;
unsigned char tmp;

tmp = a;
tmp = tmp / 16;
if (tmp < 10)
{
    h = '0' + tmp;
}
else
{
    h = 'a' + tmp - 10;
}
write(1,&h,1);

tmp = a
tmp = tmp % 16;
if (tmp < 10)
{
    h = '0' + tmp;
}
else
{
    h = 'a' + tmp - 10;
}
write(1,&h,1);

输出

0a

【讨论】:

    【解决方案3】:

    另一种选择:

    void ft_putstr_non_printable(const char *str) {
        static const char hex[] = "0123456789ABCDEF";
        int outfd = fileno(stdout);
        char buf[4] = {'0', 'x'};
        unsigned char ch; // used to convert the usually signed `char` to unsigned
    
        for(; (ch = *str) != '\0'; ++str) {    // loop until null terminator
            if (ch < ' ' || ch > '~') {        // outside printable ASCII range?
                // pick the last two chars in `buf` from the hex array:
                buf[2] = hex[ch >> 4];         // the high nibble
                buf[3] = hex[ch & 0xF];        // the low nibble
                write(outfd, buf, sizeof buf); // ex: writes 0x7F if ch == 127
            } else {
                write(outfd, &ch, 1);
            }
        }
        ch = '\n';
        write(outfd, &ch, 1);
    }
    

    【讨论】:

      【解决方案4】:

      我已将十六进制输出包装到 [] 中以使它们更加不同。

      #include <unistd.h>
      
      void    ft_putstr_non_printable(const char *s)
      {
          const char *hex = "0123456789ABCDEF";
          const unsigned char *str = s;
          while (*str)
          {
              if (*str <= 31 || *str >= 127)
              {
                  char hexrep[] = {'[','0','x', hex[*str >> 4], hex[*str & 0x0f],']'};
                  write(1, hexrep, sizeof(hexrep));
              }
              else
              {
                  write(1, str, 1);
              }
              str++;
          }
          write(1, (char[]){'\n'},1);
      }
      
      
      int main(void)
      {
          char a[] = "\n au revoir\a";
          char b[] = "omellette du fromage\b";
          char c[] = "coeuf@ca6va\e fef";
          char d[] = " Batata \x7F rfg";
          char e[] = "roquefort`[e{forte-e_tem,bolor \n feff";
          char f[] = " we 9are 78familly \x1F rgfenf";
      
          ft_putstr_non_printable(a);
          ft_putstr_non_printable(b);
          ft_putstr_non_printable(c);
          ft_putstr_non_printable(d);
          ft_putstr_non_printable(e);
          ft_putstr_non_printable(f);
      }
      

      https://godbolt.org/z/zq7sPfM6q

      输出:

      [0x0A] au revoir[0x07]
      omellette du fromage[0x08]
      coeuf@ca6va[0x1B] fef
       Batata [0x7F] rfg
      roquefort`[e{forte-e_tem,bolor [0x0A] feff
       we 9are 78familly [0x1F] rgfenf
      

      如果您想要\xHH 格式,只需将一行更改为:

                  char hexrep[] = {'\\','x', hex[*str >> 4], hex[*str & 0x0f]};
      

      https://godbolt.org/z/6GonenfK7

      输出:

      \x0A au revoir\x07
      omellette du fromage\x08
      coeuf@ca6va\x1B fef
       Batata \x7F rfg
      roquefort`[e{forte-e_tem,bolor \x0A feff
       we 9are 78familly \x1F rgfenf
      

      【讨论】:

      • hex[*str &gt;&gt; 4]*str &lt; 0 时可能会出现问题。也许强制unsigned char
      【解决方案5】:

      write() 实际上是做什么的?它在&lt;unistd.h&gt; 中定义为:

      ssize_t write(int fildes, const void *buf, size_t nbyte);
      

      写入filedescriptor。它从 buffer 写入并从该内存位置写入 nbytes 字节。

      因此,当您将&amp;i 传递给它时,write 只会看到内存地址。它没有看到int&amp;c 也是如此。您需要做的是将每个字符(作为数字)转换为代表该字符的字符串。 ft_putstr_non_printable 的代码如下所示:

      void ft_putstr_non_printable(char *str)
      {
          int             i;
          unsigned char   a;
          i = 0;
          const char[] hexchars = "0123456789ABCDEF";
          for (int ii = 0;str[i]!='\0';ii++)
          {
              a = str[i];
              if (str[i]> 31 && str[i] < 127)
                  write(1, &a, 1);
              else
              {
                  write(1,'[',1);
                  if (a<=16
                  while (a!=0)
                  {
                      write(1,hexchars+(a%16),1);
                      a/=16;
                  }
                  write(1,']',1);
              }
          }
      }
      

      这基本上就是itoa() 的工作原理。 @4386427 的答案对于单字节字符来说更优雅,但我想明确展示如何在 while 循环中做到这一点。

      【讨论】:

      • 代码以小端顺序打印十六进制值。这是故意的吗?注意:while (a!=0) { write(1,hexchars+(a%16),1); a/=16; } 写入 1 或 2 个字符 - 如果 1 个字符的十六进制输出后跟一个数字字符,则会导致歧义。
      • 不幸的是,对于 n 元转义码,歧义是不可避免的。 (基本上又是字符串问题:要么在前面加上字符数,要么在后面加上代码 ender。)我选择采用方括号表示法。它不是那么好,但它可以完成工作。
      • 至于字节序,我不知道如何在没有字节序反向操作或复杂的模数学运算的情况下解决这个问题......或者像你所做的那样推送到堆栈。
      猜你喜欢
      • 2016-02-06
      • 1970-01-01
      • 2021-07-12
      • 2014-10-12
      • 2016-03-05
      • 1970-01-01
      • 1970-01-01
      • 2019-10-03
      • 1970-01-01
      相关资源
      最近更新 更多