12

私は VS2010 でブースト 1.50 を使用しており、Windows ファイル HANDLEを使用して読み取りを行っています(これは、ソケットでの asio の使用に比べて比較的一般的ではないようです)。

問題

コールバックは 8 行目に到達し、1 行目のhandle_readすべてが追加された最初のビットを返します。さらにコールバックが 2 行目から繰り返されます。うんざりします。

  • 短いテキスト ファイルを開きます (下記)
  • handle_read1 行目から 7 行目までの正しい内容で期待されるコールバックを取得する
  • 次のコールバックには、予想よりも長いバイト読み取り lengthパラメータがあります
  • を使用していませんがlengthgetline対応する長い行を asio ストリーム バッファから抽出します。
  • 抽出されたコンテンツは、入力ファイルの最初の行を繰り返すように途中で切り替わります
  • さらにhandle_readコールバックが行 2 から 7 をリサイクルすると、「長いハイブリッド」行の問題が発生します。
  • 吐き気

入力

LINE 1 abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789
LINE 2 abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789
...3--E similarly...
LINE F abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789

出力

出力の最初の 15 行を次に示します (ずっと続きます)。

line #1, length 70, getline() [69] 'LINE 1 abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789'
line #2, length 70, getline() [69] 'LINE 2 abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789'
...line #3 through #6 are fine too...
line #7, length 70, getline() [69] 'LINE 7 abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789'
line #8, length 92, getline() [91] 'LINE 8 abcdefghijklmnoLINE 1 abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789'
line #9, length 70, getline() [69] 'LINE 2 abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789'
...line #10 through #13 are fine...
line #14, length 70, getline() [69] 'LINE 7 abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789'
line #15, length 92, getline() [91] 'LINE 8 abcdefghijklmnoLINE 1 abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789'
...

出力ライン #8 と #15 は、入力ライン 8 とライン 1 の混合であることに注意してください。

コード

#include "stdafx.h"

#include <cassert>
#include <iostream>
#include <string>

#include <boost/asio.hpp>
#include <boost/bind.hpp>

#include <Windows.h>
#include <WinBase.h>

class AsyncReader
{
  public:
    AsyncReader(boost::asio::io_service& io_service, HANDLE handle)
      : io_service_(io_service),
        input_buffer(/*size*/ 8192),
        input_handle(io_service, handle)
    {
        start_read();
    }

    void start_read()
    {
        boost::asio::async_read_until(input_handle, input_buffer, '\n',
            boost::bind(&AsyncReader::handle_read, this,
                boost::asio::placeholders::error,
                boost::asio::placeholders::bytes_transferred));
    }

    void handle_read(const boost::system::error_code& error, std::size_t length);
    // void handle_write(const boost::system::error_code& error);

  private:
    boost::asio::io_service& io_service_;
    boost::asio::streambuf input_buffer;
    boost::asio::windows::stream_handle input_handle;
};

void AsyncReader::handle_read(const boost::system::error_code& error, std::size_t length)
{
    if (!error)
    {
        static int count = 0;
        ++count;

        // method 1: (same problem)
        // const char* pStart = boost::asio::buffer_cast<const char*>(input_buffer.data());
        // std::string s(pStart, length);
        // input_buffer.consume(length);

        // method 2:
        std::istream is(&input_buffer);
        std::string s;
        assert(std::getline(is, s));

        std::cout << "line #" << count << ", length " << length << ", getline() [" << s.size() << "] '" << s << "'\n";

        start_read();
    }
    else if (error == boost::asio::error::not_found)
        std::cerr << "Did not receive ending character!\n";
    else
        std::cerr << "Misc error during read!\n";
}
int _tmain(int argc, _TCHAR* argv[])
{
    boost::asio::io_service io_service;

    HANDLE handle = ::CreateFile(TEXT("c:/temp/input.txt"),
                                 GENERIC_READ,
                                 0, // share mode
                                 NULL, // security attribute: NULL = default
                                 OPEN_EXISTING, // creation disposition
                                 FILE_FLAG_OVERLAPPED,
                                 NULL // template file
                                );

    AsyncReader obj(io_service, handle);

    io_service.run();

    std::cout << "Normal termination\n";
    getchar();
    return 0;
}

私の考え

  • それはオプションの何かかもしれません-CreateFile切り替えるまでまったく機能しませんでしたFILE_FLAG_OVERLAPPED-エラーとしても現れない他の要件があるかどうかわかりません...?
  • 私は試してみましたが、(ソケット用に)見つけることができるすべてのサンプルコードがそれを処理することを示唆しているにinput_buffer.commit.consumeかかわらず、私がすべきことがあるかどうかはわかりませんgetline...
  • 憤慨/Linuxが恋しい....
4

3 に答える 3

5

Astream_handleは常にオフセット 0 で読み取ります。ソケットハンドル用で、通常のファイルには役に立たないと思います。

streambuf にまだ改行が含まれていない場合、async_read_until() を呼び出すと 512 バイトが取得されます。最初の呼び出しでは、7 行を少し超えて読み取ります。7 行を抽出すると、残りの文字 ("LINE 8 abcdefghijklmno") には改行がなく、(同じ) 512 バイトが追加されます。

問題を解決するには、random_access_handle. ファイルの位置を手動で追跡しasync_read_untilasync_read_at.

class AsyncReader
{
  ...
  void start_read()
  {
    async_read_at(input_handle, input_offset, input_buffer, ...);
  }
private:
  boost::asio::windows::random_access_handle input_handle;
  boost::uint64_t input_offset;
};

void AsyncReader::handle_read(const boost::system::error_code& error,
                              std::size_t length)
{
  input_offset += length;
  if (!error || error == boost::asio::error::eof)
  {
    ...
于 2013-07-21T20:13:09.137 に答える
2

1 つのオプションはfseek()、ユーザーの ReadHandler が呼び出される前にファイルを次の位置に移動することです。次にasync_read_some()、として実装できますasync_read_at(ftell())

AsyncReader は、stream_handle の代わりに ReadUntilHandle を使用できます。

class ReadUntilHandle : public boost::asio::windows::random_access_handle
{
  public:
    ReadUntilHandle(boost::asio::io_service& ios, HANDLE handle)
      : boost::asio::windows::random_access_handle(ios, handle)
    {}

    template <typename MutableBufferSequence, typename Handler>
    void async_read_some(const MutableBufferSequence& buffers, Handler& handler)
    {
      LARGE_INTEGER offset;
      offset.QuadPart = 0;
      if (::SetFilePointerEx(native_handle(), offset, &offset, FILE_CURRENT)) {
        async_read_some_at(offset.QuadPart, buffers,
                           std::bind(&on_read_complete<Handler>, handler,
                                     native_handle(), std::ref(get_io_service()),
                                     std::placeholders::_1, std::placeholders::_2));
      } else {
        boost::system::error_code error(::GetLastError(), boost::asio::error::get_system_category());
        get_io_service().post(boost::asio::detail::bind_handler(handler, error, 0));
      }
    }
  private:
    template <typename Handler> static void
    on_read_complete(Handler& handler, HANDLE native_handle, boost::asio::io_service& ios,
                   boost::system::error_code error, std::size_t length)
    {
      if (0 != length) { // update file position
        LARGE_INTEGER offset;
        offset.QuadPart = length;
        if (!::SetFilePointerEx(native_handle, offset, NULL, FILE_CURRENT) && !error) {
          error.assign(::GetLastError(),  boost::asio::error::get_system_category());
        }
      }
      ios.dispatch(boost::asio::detail::bind_handler(handler, error, length));
    }
};
于 2013-07-24T15:53:17.567 に答える