Как я могу создать манипулятор потока для обработки строк? [Дубликат]

Другое событие NullPointerException возникает, когда объявляется массив объектов, а затем сразу же пытается разыменовать его внутри.

String[] phrases = new String[10];
String keyPhrase = "Bird";
for(String phrase : phrases) {
    System.out.println(phrase.equals(keyPhrase));
}

Этот конкретный NPE можно избежать, если порядок сравнения отменяется ; а именно, использовать .equals для гарантированного непустого объекта.

Все элементы внутри массива инициализируются их общим начальным значением ; для любого типа массива объектов, это означает, что все элементы null.

Вы должны инициализировать элементы в массиве перед доступом или разыменованием их.

String[] phrases = new String[] {"The bird", "A bird", "My bird", "Bird"};
String keyPhrase = "Bird";
for(String phrase : phrases) {
    System.out.println(phrase.equals(keyPhrase));
}

50
задан Kara 6 August 2015 в 21:55
поделиться

7 ответов

Сначала вы должны сохранить некоторое состояние в каждом потоке. Вы можете сделать это с помощью функции iword и указателя, который вы передаете ей, заданного xalloc :

inline int geti() { 
    static int i = ios_base::xalloc();
    return i;
}

ostream& add_one(ostream& os) { os.iword(geti()) = 1; return os; } 
ostream& add_none(ostream& os) { os.iword(geti()) = 0; return os; }

на месте, вы уже можете получить некоторое состояние во всех потоках. Теперь вам просто нужно подключиться к соответствующей операции вывода. Цифровой вывод выполняется фасеткой, поскольку он потенциально зависит от локали. Итак, вы можете сделать

struct my_num_put : num_put<char> {
    iter_type 
    do_put(iter_type s, ios_base& f, char_type fill, long v) const { 
        return num_put<char>::do_put(s, f, fill, v + f.iword(geti())); 
    } 

    iter_type 
    do_put(iter_type s, ios_base& f, char_type fill, unsigned long v) const { 
        return num_put<char>::do_put(s, f, fill, v + f.iword(geti())); 
    } 
}; 

Теперь вы можете протестировать материал.

int main() {
    // outputs: 11121011
    cout.imbue(locale(locale(),new my_num_put));
    cout << add_one << 10 << 11 
         << add_none << 10 << 11;
}

Если вы хотите, чтобы только следующее число увеличивалось, просто установите для этого слова значение 0 снова после каждого вызова do_put.

65
ответ дан Johannes Schaub - litb 19 August 2018 в 09:51
поделиться
  • 1
    +1. Это тщательное, по-книжному (и, как вы можете видеть, довольно сложное) решение. Но мне интересно, будет ли проще (и, возможно, яснее) просто создать функцию plusone (), которая принимает один аргумент и возвращает результат? – j_random_hacker 29 April 2009 в 04:58
  • 2
    очень хорошая экспозиция! – user 29 April 2009 в 10:33
  • 3
    Спасибо людям :) – Johannes Schaub - litb 28 September 2009 в 15:33

Я полностью согласен с Нилом Баттерворт на этом, однако в конкретном случае, который вы используете, вы можете сделать это совершенно ужасно. Не делайте этого в любом производственном коде. В нем много ошибок. Во-первых, он работает только в одном слое выше, он не изменяет состояние базового потока.

class plusone_stream : public std::ostream
{
  public:
    std::ostream operator<<(int i)
    {
      _out << i+1;
      return *this;
    }
};

std::ostream& plusone(std::ostream& out)
{
    return plusone_stream(out);
}
13
ответ дан 1800 INFORMATION 19 August 2018 в 09:51
поделиться

Манипуляторы hex, dec и oct просто изменяют свойство basefield существующего stream.

См. Справочник по C ++ для более удаленных об этих манипуляторах.

Как указано в ответе Neil Butterworth , вам нужно расширить существующие классы потоков или создать свои собственные, чтобы иметь манипуляторы, которые влияют на будущие значения, вставленные в поток.

В примере вашего манипулятора plusone объект потока должен иметь внутренний флаг, указывающий, что его нужно добавить ко всем вставленным значениям. Манифест plusone просто установил бы этот флаг, а код для обработки вставки потока проверил бы этот флаг перед вставкой чисел.

-1
ответ дан Community 19 August 2018 в 09:51
поделиться
  • 1
    Имейте в -1 извините - iostreams действительно содержат механизм расширяемости (xalloc (), iword (), pword ()), как указано в litb. – j_random_hacker 29 April 2009 в 05:01
  • 2
    Не надо извиняться. Я явно ошибался, и я бы не знал, не увидев -1. Спасибо, что привлекли его к моему вниманию! – e.James 29 April 2009 в 15:00
Подход

litb «правильный путь» и необходим для сложных вещей, но что-то вроде этого может быть достаточно хорошим. Добавьте неприкосновенность частной жизни и дружбу по вкусу.

struct PlusOne
{
   PlusOne(int i) : i_(i) { }
   int i_;
};

std::ostream &
operator<<(std::ostream &o, const PlusOne &po)
{
   return o << (po.i_ + 1);
}

std::cout << "1 + 1 = " << PlusOne(num2); // => "1 + 1 = 2"

В этом простом примере создание и потоковая передача временного объекта выглядит не намного полезнее, чем определение функции plusOne (), как уже было сказано. Но предположим, что вы хотели, чтобы он работал следующим образом:

std::ostream &
operator<<(std::ostream &o, const PlusOne &po)
{
   return o << po.i_ << " + 1 = " << (po.i_ + 1);
}

std::cout << PlusOne(num2); // => "1 + 1 = 2"
0
ответ дан Dan 19 August 2018 в 09:51
поделиться

Я создал простое решение для вашего тестового примера, не используя <iomanip>. Я не могу обещать, что один и тот же подход будет работать в реальной жизни.

Основной подход заключается в том, что cout << plusone возвращает временный вспомогательный объект (PlusOnePlus), который, в свою очередь, имеет перегруженный operator << который выполняет добавление.

Я тестировал его в Windows:

PlusOne plusone;
cout << plusone << 41

производит «42», как и ожидалось. Вот код:

class PlusOnePlus {
public:
    PlusOnePlus(ostream& os) : m_os(os) {}
    // NOTE: This implementation relies on the default copy ctor,
    // assignment, etc.
private:
    friend ostream& operator << (PlusOnePlus& p, int n);
    ostream& m_os;
};

class PlusOne {
public:
    static void test(ostream& os);
};

PlusOnePlus operator << (ostream& os, const PlusOne p)
{
    return PlusOnePlus(os);
}

ostream& operator << (PlusOnePlus& p, int n)
{
    return p.m_os << n + 1;
}

void PlusOne::test(ostream& os)
{
    PlusOne plusone;
    os << plusone << 0 << endl;
    os << plusone << 41 << endl;
}

EDIT: прокомментировал код, чтобы указать, что я полагаюсь на конструктор копирования по умолчанию (и т. Д.) Для PlusOnePlus. Надежная реализация, вероятно, определит эти

1
ответ дан Dan Breslau 19 August 2018 в 09:51
поделиться
  • 1
    Умный, но обратите внимание, что следующее не будет работать: & quot; os & lt; плюс один; os & lt; 41; & Quot; – j_random_hacker 29 April 2009 в 05:02
  • 2
    Я мог бы утверждать, что это особенность :-) хотя, по общему признанию, она несовместима со стандартным способом работы манипуляторов. – Dan Breslau 29 April 2009 в 15:48
  • 3
    Да, я сомневаюсь, что кто-нибудь заметит еще одно безвозвратное несоответствие в C ++ ...;) +1. – j_random_hacker 30 April 2009 в 14:44

Это не прямой ответ на ваш вопрос, но разве вы не считаете, что использование простой старой функции проще и проще использовать, чем писать полномасштабный манипулятор?

#include <sstream>

template<typename T>
std::string plusone(T const& t) {
    std::ostringstream oss;
    oss << (t + 1);
    return oss.str();
}

Использование:

cout << plusone(42);

Под «clear to use» я подразумеваю, что пользователю не нужно спрашивать себя: «Это влияет только на следующий элемент или все последующие элементы?» Из проверки видно, что затронут только аргумент функции.

(Для примера plusone() вы можете упростить еще больше, просто вернув T вместо этого, но вернув std::string общий случай.)

2
ответ дан j_random_hacker 19 August 2018 в 09:51
поделиться

Вам придется играть с файловыми системами. Я добавил в закладки следующие ссылки на эту тему:

Поскольку библиотека Maciej Sobczak больше не доступна в Интернете, и, поскольку лицензия позволяет мне это сделать (исправьте меня, если я ошибаюсь), вот копия его главный файл, который мне удалось спасти от забвения:

// streamstate.h
//
// Copyright (C) Maciej Sobczak, 2002, 2003
//
// Permission to copy, use, modify, sell and distribute this software is
// granted provided this copyright notice appears in all copies.  This software
// is provided "as is" without express or implied warranty, and with no claim
// as to its suitability for any purpose.
//
// <http://lists.boost.org/Archives/boost/2002/10/38275.php>
// <http://www.ddj.com/dept/cpp/184402062?pgno=1>
// <http://www.msobczak.com/prog/publications.html>

#ifndef STREAMSTATE_H_INCLUDED
#define STREAMSTATE_H_INCLUDED

#include <ios>
#include <istream>
#include <ostream>

// helper exception class, thrown when the source of error
// was in one of the functions managing the additional state storage
class StreamStateException : public std::ios_base::failure
{
public:
    explicit StreamStateException()
        : std::ios_base::failure(
            "Error while managing additional IOStream state.")
    {
    }
};

// State should be:
// default-constructible
// copy-constructible
// assignable

// note: the "void *" slot is used for storing the actual value
//       the "long" slot is used to propagate the error flag
template
<
    class State,
    class charT = char,
    class traits = std::char_traits<charT>
>
class streamstate
{
public:
    // construct with the default state value
    streamstate() {}

    // construct with the given stream value
    streamstate(const State &s) : state_(s) {}

    // modifies the stream
    std::basic_ios<charT, traits> &
    modify(std::basic_ios<charT, traits> &ios) const
    {
        long *errslot;
        void *&p = state_slot(ios, errslot);

        // propagate the error flag to the real stream state
        if (*errslot == std::ios_base::badbit)
        {
            ios.setstate(std::ios_base::badbit);
            *errslot = 0;
        }

        // here, do-nothing-in-case-of-error semantics
        if (ios.bad())
            return ios;

        if (p == NULL)
        {
            // copy existing state object if this is new slot
            p = new State(state_);
            ios.register_callback(state_callback, 0);
        }
        else
            *static_cast<State*>(p) = state_;

        return ios;
    }

    // gets the current (possibly default) state from the slot
    static State & get(std::basic_ios<charT, traits> &ios)
    {
        long *errslot;
        void *&p = state_slot(ios, errslot);

        // propagate the error flag to the real stream state
        if (*errslot == std::ios_base::badbit)
        {
            ios.setstate(std::ios_base::badbit);
            *errslot = 0;
        }

        // this function returns a reference and therefore
        // the only sensible error reporting is via exception
        if (ios.bad())
            throw StreamStateException();

        if (p == NULL)
        {
            // create default state if this is new slot
            p = new State;
            ios.register_callback(state_callback, 0);
        }

        return *static_cast<State*>(p);
    }

private:
    // manages the destruction and format copying
    // (in the latter case performs deep copy of the state)
    static void state_callback(std::ios_base::event e,
        std::ios_base &ios, int)
    {
        long *errslot;
        if (e == std::ios_base::erase_event)
        {
            // safe delete if state_slot fails
            delete static_cast<State*>(state_slot(ios, errslot));
        }
        else if (e == std::ios_base::copyfmt_event)
        {
            void *& p = state_slot(ios, errslot);
            State *old = static_cast<State*>(p);

            // Standard forbids any exceptions from callbacks
            try
            {
                // in-place deep copy
                p = new State(*old);
    }
            catch (...)
            {
                // clean the value slot and
                // set the error flag in the error slot
                p = NULL;
                *errslot = std::ios_base::badbit;
            }
        }
    }

    // returns the references to associated slot
    static void *& state_slot(std::ios_base &ios, long *&errslot)
    {
        static int index = std::ios_base::xalloc();
        void *&p = ios.pword(index);
        errslot = &(ios.iword(index));

        // note: if pword failed,
        // then p is a valid void *& initialized to 0
        // (27.4.2.5/5)

        return p;
    }

    State state_;
};

// partial specialization for iword functionality
template
<
    class charT,
    class traits
>
class streamstate<long, charT, traits>
{
public:
    // construct with the default state value
    streamstate() {}

    // construct with the given stream value
    streamstate(long s) : state_(s) {}

    // modifies the stream
    // the return value is not really useful,
    // it has to be downcasted to the expected stream type
    std::basic_ios<charT, traits> &
    modify(std::basic_ios<charT, traits> &ios) const
    {
        long &s = state_slot(ios);
        s = state_;

        return ios;
    }

    static long & get(std::basic_ios<charT, traits> &ios)
    {
        return state_slot(ios);
    }

private:
    static long & state_slot(std::basic_ios<charT, traits> &ios)
    {
        static int index = std::ios_base::xalloc();
        long &s = ios.iword(index);

        // this function returns a reference and we decide
        // to report errors via exceptions
        if (ios.bad())
            throw StreamStateException();

        return s;
    }

    long state_;
};

// convenience inserter for ostream classes
template
<
    class State,
    class charT,
    class traits
>
std::basic_ostream<charT, traits> &
operator<<(std::basic_ostream<charT, traits> &os,
    const streamstate<State> &s)
{
    s.modify(os);
    return os;
}

// convenience extractor for istream classes
template
<
    class State,
    class charT,
    class traits
>
std::basic_istream<charT, traits> &
operator>>(std::basic_istream<charT, traits> &is,
    const streamstate<State> &s)
{
    s.modify(is);
    return is;
}

// the alternative if there is a need to have
// many different state values of the same type
// here, the instance of streamstate_value encapsulates
// the access information (the slot index)

template
<
    class State,
    class charT = char,
    class traits = std::char_traits<char>
>
class streamstate_value
{
public:

    streamstate_value()
        : index_(-1)
    {
    }

    // returns a reference to current (possibly default) state
    State & get(std::basic_ios<charT, traits> &ios)
    {
        long *errslot;
        void *&p = state_slot(ios, errslot, index_);

        // propagate the error flag to the real stream state
        if (*errslot == std::ios_base::badbit)
        {
            ios.setstate(std::ios_base::badbit);
            *errslot = 0;
        }

        // this function returns a reference and the only
        // sensible way of error reporting is via exception
        if (ios.bad())
            throw StreamStateException();

        if (p == NULL)
        {
            // create default state if this is new slot
            p = new State;
            ios.register_callback(state_callback, index_);
        }

        return *static_cast<State*>(p);
    }

private:

    // manages the destruction and format copying
    // (in the latter case performs deep copy of the state)
    static void state_callback(std::ios_base::event e,
        std::ios_base &ios, int index)
    {
        long *errslot;
        if (e == std::ios_base::erase_event)
        {
            // safe delete if state_slot fails
            delete static_cast<State*>(state_slot(ios, errslot, index));
        }
        else if (e == std::ios_base::copyfmt_event)
        {
            void *& p = state_slot(ios, errslot, index);
            State *old = static_cast<State*>(p);

            // Standard forbids any exceptions from callbacks
            try
            {
                // in-place deep copy
                p = new State(*old);
    }
            catch (...)
            {
                // clean the value slot and set the error flag
                // in the error slot
                p = NULL;
                *errslot = std::ios_base::badbit;
            }
        }
    }

    // returns the references to associated slot
    static void *& state_slot(std::ios_base &ios,
        long *& errslot, int & index)
    {
        if (index < 0)
        {
            // first index usage
            index = std::ios_base::xalloc();
        }

        void *&p = ios.pword(index);
        errslot = &(ios.iword(index));

        // note: if pword failed,
        // then p is a valid void *& initialized to 0
        // (27.4.2.5/5)

        return p;
    }

    int index_;
};

// partial specialization for iword functionality
template
<
    class charT,
    class traits
>
class streamstate_value<long, charT, traits>
{
public:
    // construct with the default state value
    streamstate_value()
        : index_(-1)
    {
    }

    long & get(std::basic_ios<charT, traits> &ios)
    {
        if (index_ < 0)
        {
            // first index usage
            index_ = std::ios_base::xalloc();
        }

        long &s = ios.iword(index_);
        if (ios.bad())
            throw StreamStateException();

        return s;
    }

private:
    long index_;
};

#endif // STREAMSTATE_H_INCLUDED 
1
ответ дан Luc Hermitte 19 August 2018 в 09:51
поделиться
  • 1
    Ссылка библиотеки мертва. – Ben Fulton 1 March 2017 в 15:41
  • 2
    @BenFulton. В самом деле. К сожалению, библиотека больше не кажется доступной. IIRC, у меня есть копия zip где-то. И вот оно. Может быть, сейчас лучше отступить, чтобы ускорить рост. – Luc Hermitte 1 March 2017 в 16:15