【问题标题】:Stop on newline when using read(...)使用 read(...) 时停止换行
【发布时间】:2016-08-03 19:48:27
【问题描述】:

我需要从通过 UART 连接的 GPS 读取 NMEA 语句。操作系统是 Debian,语言必须是 C++。为此,我使用open(...) 打开文件并使用read(...) 读取字符串。但是,这样我必须指定一个字符串长度,这会分解句子。相反,我想读到 NMEA 句子的结尾。我如何使用read(...) 并在新线路上停止?有read(...)的选项吗?

【问题讨论】:

    标签: linux file uart


    【解决方案1】:

    我如何使用read(...) 并停止换行?有没有选择 read(...)?

    不,read() 没有选择这样做。

    根据POSIX standard

    read() 函数读取之前写入文件的数据。如果有的话 文件结尾之前的常规文件的一部分没有被 写入,read() 应返回值为 0 的字节。例如, lseek() 允许将文件偏移量设置为超出现有的末尾 文件中的数据。如果稍后在此时写入数据,则后续 读取数据的前一端和新端之间的间隙 写入的数据应返回值为 0 的字节,直到数据被写入 进入间隙。

    成功完成后,nbyte 大于 0,read() 应标记更新文件的最后数据访问时间戳,并且 应返回读取的字节数。这个数字永远不会 大于nbyte。如果返回值可能小于nbyte 文件中剩余的字节数小于nbyte,如果 read() 请求被信号中断,或者文件是管道 或 FIFO 或特殊文件,并且立即少于 nbyte 字节 可供阅读。例如,来自关联文件的read() 使用终端可能会返回一行输入的数据。

    如果read() 在读取任何数据之前被信号中断,它 应返回 -1 并将 errno 设置为 [EINTR]。

    如果read()在成功后被信号中断 读取一些数据,它应该返回读取的字节数。

    ...

    read() 处理原始字节,没有任何解释。

    如果要使用库函数从文件中读取文本数据行,可以使用getline() function

    【讨论】:

    • 我很想使用 getline(),但该函数需要 FILE*,而我只有一个文件描述符
    • @Amiguel: fdopen() 将从描述符中获得FILE*。如果您想保持理智,请只阅读其中一个或另一个!
    • 还没来得及检查,但我相信fdopen()会解决问题。我需要使用描述符来设置波特率等等,但我想使用FILE* 来执行读/写。
    • @AmiuelS 您还可以使用fileno()FILE * 获取文件描述符,因此这也可能对您有用,具体取决于您的代码的组织方式。只是不要直接访问FILE * 来获取描述符,因为这是非常不可移植的——只需更改编译器标志就可以在某些实现上更改FILE * 结构的内部结构。由于您是从设备读取数据,因此有时您可能会读取部分行,无论您如何读取它。
    【解决方案2】:

    我需要从通过 UART 连接的 GPS 读取 NMEA 语句。
    ...
    如何使用 read(...) 并在新行上停止?

    如果您打开了终端设备(例如 /dev/ttyUSB0),那么您可以使用终端的行规处理程序将接收到的文本解析为行。
    终端必须以阻塞模式打开(这是默认模式,除非指定非阻塞),并且终端必须配置为canonical input(使用termios API)。

    是否有读取(...)的选项?

    当终端设备配置为规范输入时,read() 将返回一行文本(除非发生错误)。确保您的读取缓冲区(和 count 参数)对于最长的预期行足够大,以便 read() 不会截断该行。

    来自termios 手册页:

       Canonical and noncanonical mode  
    
       The setting of the ICANON canon flag in c_lflag determines whether
       the terminal is operating in canonical mode (ICANON set) or
       noncanonical mode (ICANON unset).  By default, ICANON set.
    
       In canonical mode:
    
       * Input is made available line by line.  An input line is available
         when one of the line delimiters is typed (NL, EOL, EOL2; or EOF at
         the start of line).  Except in the case of EOF, the line delimiter
         is included in the buffer returned by read(2).
    
       * Line editing is enabled (ERASE, KILL; and if the IEXTEN flag is
         set: WERASE, REPRINT, LNEXT).  A read(2) returns at most one line
         of input; if the read(2) requested fewer bytes than are available
         in the current line of input, then only as many bytes as requested
         are read, and the remaining characters will be available for a
         future read(2).
    
       * The maximum line length is 4096 chars (including the terminating
         newline character); lines longer than 4096 chars are truncated.
         After 4095 characters, input processing (e.g., ISIG and ECHO*
         processing) continues, but any input data after 4095 characters up
         to (but not including) any terminating newline is discarded.  This
         ensures that the terminal can always receive more input until at
         least one line can be read.
    

    使用 stty 命令,或 tcgetattr()tcsetattr() 来配置终端模式。
    学习Setting Terminal Modes ProperlySerial Programming Guide for POSIX Operating Systems.

    请注意,读取缓冲区中返回的行不是字符串,并且不会以空字节终止。解决方案见Linux Serial Read throws Error

    【讨论】:

    • 不错。我没有想到将设备的特征更改为read() from。
    猜你喜欢
    • 2016-06-24
    • 1970-01-01
    • 2011-11-26
    • 2010-10-27
    • 1970-01-01
    • 1970-01-01
    • 2014-01-09
    • 2014-02-08
    • 2014-11-04
    相关资源
    最近更新 更多