Беда с трехмерным массивом символов в D - PullRequest
3 голосов
/ 28 января 2011

Я изучаю D, и у меня есть простая программа, которая читает строку за строкой в ​​текстовом файле, разделяет каждую строку на отдельные слова и печатает все это в стандартный вывод.

import std.stdio;
import std.string;

void main(string args[])
{
    char[][][] lines;
    auto input = File(args[1], "r");
    foreach(line; input.byLine())
    {
        auto words = split(strip(line));
        lines ~= words;
    }

    foreach(line; lines)
    {
        writeln(line);
    }
}

Код для создания words работает.Если я просто вызываю writeln на словах каждый раз, когда они назначаются, я получаю вывод, который хочу.Но если я добавлю words к lines и выведу lines, тогда произойдут странные вещи.lines имеет запись для каждой строки в исходном файле, но каждая строка является поврежденной версией последней прочитанной строки.Например, если последняя строка файла выглядит следующим образом:

END    START        * End of routine

Я получаю вывод, который выглядит примерно так:

[       , END, ST, *, End , f rout, ne,    ,     , e other]
[     , END, ST, *, End of, rout, ne,      ,   , e othe]
[    , END, STAR, *, End of, rout, ne.,        
e]
[    , END, START  , *, End of, rout, ne.,        
e]
[END , STAR]
[     , END, START     , *, End , f , out, ne.  ]
[END, START, *, End, of ro, tine. ,  ,   ,  
]
[END, STA, *, o,  r, ut]
[  , END , S, *, End, o,  r, utine.,  ,   ,  , 
,  o]
[END, START    , *, of routi, e.,   ]

Есть идеи, что я делаю неправильно?

Ответы [ 2 ]

8 голосов
/ 28 января 2011

Ваша основная проблема заключается в том, что byLine использует тот же буфер, вам нужно дублировать его, чтобы он не переопределял ваши данные

auto words = split(strip(line).dup);

Более подходящим классом хранения является строка вместо char [],если только вы не собираетесь изменять настоящие символы.Тем не менее, вы получите ошибку компилятора в v 2.0, потому что line будет char [].Это просто вопрос дублирования этой строки в неизменяемую.

auto words = split(strip(line).idup);

Таким образом, ваша программа будет выглядеть как

import std.stdio;
import std.string;

void main(string[] args)
{
    string[][] lines;
    auto input = File(args[1], "r");
    foreach(line; input.byLine())
    {
        auto words = split(strip(line).idup);
        lines ~= words;
    }

    foreach(line; lines)
    {
        writeln(line);
    }
}
5 голосов
/ 28 января 2011

Ответ на этот вопрос двоякий.

Во-первых, byLine, как указано, использует внутренний буфер (для скорости), который перезаписывается при последующих итерациях цикла.

Во-вторых, посмотрите наоперации для words.split(strip(line)).strip только изменяет начало и конец массива (который является ссылкой), и split разбивает массив на меньшие подмассивы, которые ссылаются на те же базовые данные. не разрушительный ;таким образом, не нужно ни перераспределять.Из-за этого окончательный string[] words по-прежнему указывает на исходный буфер, который перезаписывается при следующем изменении.

Решение состоит в том, чтобы удостовериться, что вы копируете данные, если хотите, чтобы они выходили из области цикла, путемнаписание auto words = split(strip(line).dup);.Обратите внимание, что дублирование words будет не работать, так как это будет дублировать только массив массивов, а не сами массивы.

Кроме того, вы должны использовать string[] args.С-подобный синтаксис поддерживается только по старым причинам и не рекомендуется для использования.

...